Coverage Report

Created: 2024-04-25 06:27

/src/pdns/ext/yahttp/yahttp/url.hpp
Line
Count
Source (jump to first uncovered line)
1
#pragma once
2
#include <sstream>
3
#include <string>
4
5
#include "utility.hpp"
6
7
#ifndef YAHTTP_MAX_URL_LENGTH
8
13.1k
#define YAHTTP_MAX_URL_LENGTH 2048
9
#endif 
10
11
namespace YaHTTP {
12
  /*! URL parser and container */
13
  class URL {
14
   private: 
15
13.0k
      bool parseSchema(const std::string& url, size_t &pos) {
16
13.0k
          size_t pos1;
17
13.0k
          if (pos >= url.size()) return false; // no data
18
815
          if ( (pos1 = url.find_first_of(":",pos)) == std::string::npos ) return false; // schema is mandatory
19
782
          protocol = url.substr(pos, pos1-pos);
20
782
          if (protocol == "http") port = 80;
21
782
          if (protocol == "https") port = 443;
22
782
          pos = pos1+1; // after :
23
782
          if (url.compare(pos, 2, "//") == 0) {
24
246
             pathless = false; // if this is true we put rest into parameters
25
246
             pos += 2;
26
246
          }
27
782
          return true;
28
815
      }; //<! parse schema/protocol part 
29
30
246
      bool parseHost(const std::string& url, size_t &pos) {
31
246
          size_t pos1;
32
246
          if (pos >= url.size()) return true; // no data
33
229
          if ( (pos1 = url.find_first_of("/", pos)) == std::string::npos ) {
34
189
             host = url.substr(pos);
35
189
             path = "/";
36
189
             pos = url.size();
37
189
          } else {
38
40
             host = url.substr(pos, pos1-pos);
39
40
             pos = pos1;
40
40
          }
41
229
          if (host.at(0) == '[') { // IPv6
42
88
            if ((pos1 = host.find_first_of("]")) == std::string::npos) {
43
              // incomplete address
44
28
              return false;
45
28
            }
46
60
            size_t pos2;
47
60
            if ((pos2 = host.find_first_of(":", pos1)) != std::string::npos) {
48
27
              std::istringstream tmp(host.substr(pos2 + 1));
49
27
              tmp >> port;
50
27
            }
51
60
            host = host.substr(1, pos1 - 1);
52
141
          } else if ( (pos1 = host.find_first_of(":")) != std::string::npos ) {
53
43
             std::istringstream tmp(host.substr(pos1+1));
54
43
             tmp >> port;
55
43
             host = host.substr(0, pos1);
56
43
          }
57
201
          return true;
58
229
      }; //<! parse host and port
59
60
246
      bool parseUserPass(const std::string& url, size_t &pos) {
61
246
          size_t pos1,pos2;
62
246
          if (pos >= url.size()) return true; // no data
63
64
245
          if ( (pos1 = url.find_first_of("@",pos)) == std::string::npos ) return true; // no userinfo
65
62
          pos2 = url.find_first_of(":",pos);
66
67
62
          if (pos2 != std::string::npos) { // comes with password
68
24
             username = url.substr(pos, pos2 - pos);
69
24
             password = url.substr(pos2+1, pos1 - pos2 - 1);
70
24
             password = Utility::decodeURL(password);
71
38
          } else {
72
38
             username = url.substr(pos, pos1 - pos);
73
38
          }
74
62
          pos = pos1+1;
75
62
          username = Utility::decodeURL(username);
76
62
          return true;
77
245
      }; //<! parse possible username and password
78
79
327
      bool parsePath(const std::string& url, size_t &pos) {
80
327
          size_t pos1;
81
327
          if (pos >= url.size()) return true; // no data
82
145
          if (url[pos] != '/') return false; // not an url
83
145
          if ( (pos1 = url.find_first_of("?", pos)) == std::string::npos ) {
84
38
             path = url.substr(pos);
85
38
             pos = url.size();
86
107
          } else {
87
107
             path = url.substr(pos, pos1-pos);
88
107
             pos = pos1;
89
107
          }
90
145
          return true;
91
145
      }; //<! parse path component
92
93
327
      bool parseParameters(const std::string& url, size_t &pos) {
94
327
          size_t pos1;
95
327
          if (pos >= url.size()) return true; // no data
96
107
          if (url[pos] == '#') return true; // anchor starts here
97
107
          if (url[pos] != '?') return false; // not a parameter
98
107
          if ( (pos1 = url.find_first_of("#", pos)) == std::string::npos ) {
99
88
             parameters = url.substr(pos+1);;
100
88
             pos = url.size();
101
88
          } else {
102
19
             parameters = url.substr(pos+1, pos1-pos-1);
103
19
             pos = pos1;
104
19
          }
105
107
          if (parameters.size()>0 && *(parameters.end()-1) == '&') parameters.resize(parameters.size()-1);
106
107
          return true;
107
107
      }; //<! parse url parameters
108
109
327
      bool parseAnchor(const std::string& url, size_t &pos) {
110
327
          if (pos >= url.size()) return true; // no data
111
19
          if (url[pos] != '#') return false; // not anchor
112
19
          anchor = url.substr(pos+1);
113
19
          return true;
114
19
      }; //<! parse anchor
115
116
16.4k
      void initialize() {
117
16.4k
        protocol = ""; host = ""; port = 0; username = ""; password = ""; path = ""; parameters = ""; anchor =""; pathless = true;
118
16.4k
      }; //<! initialize to empty URL
119
120
  public:
121
0
      std::string to_string() const {
122
0
          std::string tmp;
123
0
          std::ostringstream oss;
124
0
            
125
0
          if (protocol.empty() == false) {
126
0
             oss << protocol << ":";
127
0
             if (host.empty() == false) {
128
0
               oss << "//";
129
0
             }
130
0
          }
131
0
132
0
          if (username.empty() == false) {
133
0
           if (password.empty() == false)
134
0
             oss << Utility::encodeURL(username) << ":" << Utility::encodeURL(password) << "@";
135
0
           else
136
0
             oss << Utility::encodeURL(username) << "@";
137
0
          }
138
0
          if (host.empty() == false)
139
0
             oss << host;
140
0
          if (!(protocol == "http" && port == 80) &&
141
0
              !(protocol == "https" && port == 443) &&
142
0
              port > 0) 
143
0
            oss << ":" << port;
144
0
145
0
          oss << path;
146
0
          if (parameters.empty() == false) {
147
0
             if (!pathless) 
148
0
                oss << "?";
149
0
             oss << parameters;
150
0
          }
151
0
          if (anchor.empty() == false)
152
0
             oss << "#" << anchor;
153
0
          return oss.str();
154
0
      }; //<! convert this URL to string
155
156
      std::string protocol; //<! schema/protocol 
157
      std::string host; //<! host
158
      int port; //<! port
159
      std::string username; //<! username
160
      std::string password; //<! password
161
      std::string path; //<! path 
162
      std::string parameters; //<! url parameters
163
      std::string anchor; //<! anchor
164
      bool pathless; //<! whether this url has no path
165
166
3.33k
      URL() { initialize(); }; //<! construct empty url
167
0
      URL(const std::string& url) {
168
0
        parse(url);
169
0
      }; //<! calls parse with url 
170
171
9.99k
      URL(const char *url) {
172
9.99k
        parse(std::string(url));
173
9.99k
      }; //<! calls parse with url
174
175
13.1k
      bool parse(const std::string& url) {
176
        // setup
177
13.1k
        initialize();
178
179
13.1k
        if (url.size() > YAHTTP_MAX_URL_LENGTH) return false;
180
13.1k
        size_t pos = 0;
181
13.1k
        if (*(url.begin()) != '/') { // full url?
182
13.0k
          if (parseSchema(url, pos) == false) return false;
183
782
          if (pathless) {
184
536
            parameters = url.substr(pos);
185
536
            return true;
186
536
          }
187
246
          if (parseUserPass(url, pos) == false) return false;
188
246
          if (parseHost(url, pos) == false) return false;
189
246
        }
190
329
        if (parsePath(url, pos) == false) return false;
191
329
        if (parseParameters(url, pos) == false) return false;
192
329
        return parseAnchor(url, pos);
193
329
    }; //<! parse various formats of urls ranging from http://example.com/foo?bar=baz into data:base64:d089swt64wt... 
194
195
0
    friend std::ostream & operator<<(std::ostream& os, const URL& url) {
196
0
      os<<url.to_string();
197
0
      return os;
198
0
    };
199
  };
200
};