Coverage Report

Created: 2026-09-07 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pdns/ext/yahttp/yahttp/url.hpp
Line
Count
Source
1
#pragma once
2
#include <sstream>
3
#include <string>
4
5
#include "utility.hpp"
6
7
#ifndef YAHTTP_MAX_URL_LENGTH
8
15.5k
#define YAHTTP_MAX_URL_LENGTH 2048
9
#endif 
10
11
namespace YaHTTP {
12
  /*! URL parser and container */
13
  class URL {
14
   private: 
15
15.2k
      bool parseSchema(const std::string& url, size_t &pos) {
16
15.2k
          size_t pos1;
17
15.2k
          if (pos >= url.size()) return false; // no data
18
1.30k
          if ( (pos1 = url.find_first_of(":",pos)) == std::string::npos ) return false; // schema is mandatory
19
1.27k
          protocol = url.substr(pos, pos1-pos);
20
1.27k
          if (protocol == "http") port = 80;
21
1.27k
          if (protocol == "https") port = 443;
22
1.27k
          pos = pos1+1; // after :
23
1.27k
          if (url.compare(pos, 2, "//") == 0) {
24
543
             pathless = false; // if this is true we put rest into parameters
25
543
             pos += 2;
26
543
          }
27
1.27k
          return true;
28
1.30k
      }; //<! parse schema/protocol part 
29
30
543
      bool parseHost(const std::string& url, size_t &pos) {
31
543
          size_t pos1;
32
543
          if (pos >= url.size()) return true; // no data
33
467
          if ( (pos1 = url.find_first_of("/", pos)) == std::string::npos ) {
34
322
             host = url.substr(pos);
35
322
             path = "/";
36
322
             pos = url.size();
37
322
          } else {
38
145
             host = url.substr(pos, pos1-pos);
39
145
             pos = pos1;
40
145
          }
41
467
          if (host.at(0) == '[') { // IPv6
42
155
            if ((pos1 = host.find_first_of("]")) == std::string::npos) {
43
              // incomplete address
44
21
              return false;
45
21
            }
46
134
            size_t pos2;
47
134
            if ((pos2 = host.find_first_of(":", pos1)) != std::string::npos) {
48
74
              std::istringstream tmp(host.substr(pos2 + 1));
49
74
              tmp >> port;
50
74
            }
51
134
            host = host.substr(1, pos1 - 1);
52
312
          } else if ( (pos1 = host.find_first_of(":")) != std::string::npos ) {
53
163
             std::istringstream tmp(host.substr(pos1+1));
54
163
             tmp >> port;
55
163
             host = host.substr(0, pos1);
56
163
          }
57
446
          return true;
58
467
      }; //<! parse host and port
59
60
543
      bool parseUserPass(const std::string& url, size_t &pos) {
61
543
          size_t pos1,pos2;
62
543
          if (pos >= url.size()) return true; // no data
63
64
540
          if ( (pos1 = url.find_first_of("@",pos)) == std::string::npos ) return true; // no userinfo
65
208
          pos2 = url.find_first_of(":",pos);
66
67
208
          if (pos2 != std::string::npos) { // comes with password
68
123
             username = url.substr(pos, pos2 - pos);
69
123
             password = url.substr(pos2+1, pos1 - pos2 - 1);
70
123
             password = Utility::decodeURL(password);
71
123
          } else {
72
85
             username = url.substr(pos, pos1 - pos);
73
85
          }
74
208
          pos = pos1+1;
75
208
          username = Utility::decodeURL(username);
76
208
          return true;
77
540
      }; //<! parse possible username and password
78
79
718
      bool parsePath(const std::string& url, size_t &pos) {
80
718
          size_t pos1;
81
718
          if (pos >= url.size()) return true; // no data
82
339
          if (url[pos] != '/') return false; // not an url
83
339
          if ( (pos1 = url.find_first_of("?", pos)) == std::string::npos ) {
84
122
             path = url.substr(pos);
85
122
             pos = url.size();
86
217
          } else {
87
217
             path = url.substr(pos, pos1-pos);
88
217
             pos = pos1;
89
217
          }
90
339
          return true;
91
339
      }; //<! parse path component
92
93
718
      bool parseParameters(const std::string& url, size_t &pos) {
94
718
          size_t pos1;
95
718
          if (pos >= url.size()) return true; // no data
96
217
          if (url[pos] == '#') return true; // anchor starts here
97
217
          if (url[pos] != '?') return false; // not a parameter
98
217
          if ( (pos1 = url.find_first_of("#", pos)) == std::string::npos ) {
99
118
             parameters = url.substr(pos+1);;
100
118
             pos = url.size();
101
118
          } else {
102
99
             parameters = url.substr(pos+1, pos1-pos-1);
103
99
             pos = pos1;
104
99
          }
105
217
          if (parameters.size()>0 && *(parameters.end()-1) == '&') parameters.resize(parameters.size()-1);
106
217
          return true;
107
217
      }; //<! parse url parameters
108
109
718
      bool parseAnchor(const std::string& url, size_t &pos) {
110
718
          if (pos >= url.size()) return true; // no data
111
99
          if (url[pos] != '#') return false; // not anchor
112
99
          anchor = url.substr(pos+1);
113
99
          return true;
114
99
      }; //<! parse anchor
115
116
19.4k
      void initialize() {
117
19.4k
        protocol = ""; host = ""; port = 0; username = ""; password = ""; path = ""; parameters = ""; anchor =""; pathless = true;
118
19.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.91k
      URL() { initialize(); }; //<! construct empty url
167
0
      URL(const std::string& url) {
168
0
        parse(url);
169
0
      }; //<! calls parse with url 
170
171
11.7k
      URL(const char *url) {
172
11.7k
        parse(std::string(url));
173
11.7k
      }; //<! calls parse with url
174
175
15.5k
      bool parse(const std::string& url) {
176
        // setup
177
15.5k
        initialize();
178
179
15.5k
        if (url.size() > YAHTTP_MAX_URL_LENGTH) return false;
180
15.5k
        size_t pos = 0;
181
15.5k
        if (*(url.begin()) != '/') { // full url?
182
15.2k
          if (parseSchema(url, pos) == false) return false;
183
1.27k
          if (pathless) {
184
730
            parameters = url.substr(pos);
185
730
            return true;
186
730
          }
187
543
          if (parseUserPass(url, pos) == false) return false;
188
543
          if (parseHost(url, pos) == false) return false;
189
543
        }
190
737
        if (parsePath(url, pos) == false) return false;
191
737
        if (parseParameters(url, pos) == false) return false;
192
737
        return parseAnchor(url, pos);
193
737
    }; //<! 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
};