Coverage Report

Created: 2026-10-16 07:03

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
16.0k
#define YAHTTP_MAX_URL_LENGTH 2048
9
#endif 
10
11
namespace YaHTTP {
12
  /*! URL parser and container */
13
  class URL {
14
   private: 
15
15.8k
      bool parseSchema(const std::string& url, size_t &pos) {
16
15.8k
          size_t pos1;
17
15.8k
          if (pos >= url.size()) return false; // no data
18
1.31k
          if ( (pos1 = url.find_first_of(":",pos)) == std::string::npos ) return false; // schema is mandatory
19
1.28k
          protocol = url.substr(pos, pos1-pos);
20
1.28k
          if (protocol == "http") port = 80;
21
1.28k
          if (protocol == "https") port = 443;
22
1.28k
          pos = pos1+1; // after :
23
1.28k
          if (url.compare(pos, 2, "//") == 0) {
24
522
             pathless = false; // if this is true we put rest into parameters
25
522
             pos += 2;
26
522
          }
27
1.28k
          return true;
28
1.31k
      }; //<! parse schema/protocol part 
29
30
522
      bool parseHost(const std::string& url, size_t &pos) {
31
522
          size_t pos1;
32
522
          if (pos >= url.size()) return true; // no data
33
446
          if ( (pos1 = url.find_first_of("/", pos)) == std::string::npos ) {
34
315
             host = url.substr(pos);
35
315
             path = "/";
36
315
             pos = url.size();
37
315
          } else {
38
131
             host = url.substr(pos, pos1-pos);
39
131
             pos = pos1;
40
131
          }
41
446
          if (host.at(0) == '[') { // IPv6
42
151
            if ((pos1 = host.find_first_of("]")) == std::string::npos) {
43
              // incomplete address
44
19
              return false;
45
19
            }
46
132
            size_t pos2;
47
132
            if ((pos2 = host.find_first_of(":", pos1)) != std::string::npos) {
48
70
              std::istringstream tmp(host.substr(pos2 + 1));
49
70
              tmp >> port;
50
70
            }
51
132
            host = host.substr(1, pos1 - 1);
52
295
          } else if ( (pos1 = host.find_first_of(":")) != std::string::npos ) {
53
159
             std::istringstream tmp(host.substr(pos1+1));
54
159
             tmp >> port;
55
159
             host = host.substr(0, pos1);
56
159
          }
57
427
          return true;
58
446
      }; //<! parse host and port
59
60
522
      bool parseUserPass(const std::string& url, size_t &pos) {
61
522
          size_t pos1,pos2;
62
522
          if (pos >= url.size()) return true; // no data
63
64
521
          if ( (pos1 = url.find_first_of("@",pos)) == std::string::npos ) return true; // no userinfo
65
197
          pos2 = url.find_first_of(":",pos);
66
67
197
          if (pos2 != std::string::npos) { // comes with password
68
108
             username = url.substr(pos, pos2 - pos);
69
108
             password = url.substr(pos2+1, pos1 - pos2 - 1);
70
108
             password = Utility::decodeURL(password);
71
108
          } else {
72
89
             username = url.substr(pos, pos1 - pos);
73
89
          }
74
197
          pos = pos1+1;
75
197
          username = Utility::decodeURL(username);
76
197
          return true;
77
521
      }; //<! parse possible username and password
78
79
697
      bool parsePath(const std::string& url, size_t &pos) {
80
697
          size_t pos1;
81
697
          if (pos >= url.size()) return true; // no data
82
322
          if (url[pos] != '/') return false; // not an url
83
322
          if ( (pos1 = url.find_first_of("?", pos)) == std::string::npos ) {
84
114
             path = url.substr(pos);
85
114
             pos = url.size();
86
208
          } else {
87
208
             path = url.substr(pos, pos1-pos);
88
208
             pos = pos1;
89
208
          }
90
322
          return true;
91
322
      }; //<! parse path component
92
93
697
      bool parseParameters(const std::string& url, size_t &pos) {
94
697
          size_t pos1;
95
697
          if (pos >= url.size()) return true; // no data
96
208
          if (url[pos] == '#') return true; // anchor starts here
97
208
          if (url[pos] != '?') return false; // not a parameter
98
208
          if ( (pos1 = url.find_first_of("#", pos)) == std::string::npos ) {
99
119
             parameters = url.substr(pos+1);;
100
119
             pos = url.size();
101
119
          } else {
102
89
             parameters = url.substr(pos+1, pos1-pos-1);
103
89
             pos = pos1;
104
89
          }
105
208
          if (parameters.size()>0 && *(parameters.end()-1) == '&') parameters.resize(parameters.size()-1);
106
208
          return true;
107
208
      }; //<! parse url parameters
108
109
697
      bool parseAnchor(const std::string& url, size_t &pos) {
110
697
          if (pos >= url.size()) return true; // no data
111
89
          if (url[pos] != '#') return false; // not anchor
112
89
          anchor = url.substr(pos+1);
113
89
          return true;
114
89
      }; //<! parse anchor
115
116
20.0k
      void initialize() {
117
20.0k
        protocol = ""; host = ""; port = 0; username = ""; password = ""; path = ""; parameters = ""; anchor =""; pathless = true;
118
20.0k
      }; //<! 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
4.04k
      URL() { initialize(); }; //<! construct empty url
167
0
      URL(const std::string& url) {
168
0
        parse(url);
169
0
      }; //<! calls parse with url 
170
171
12.1k
      URL(const char *url) {
172
12.1k
        parse(std::string(url));
173
12.1k
      }; //<! calls parse with url
174
175
16.0k
      bool parse(const std::string& url) {
176
        // setup
177
16.0k
        initialize();
178
179
16.0k
        if (url.size() > YAHTTP_MAX_URL_LENGTH) return false;
180
16.0k
        size_t pos = 0;
181
16.0k
        if (*(url.begin()) != '/') { // full url?
182
15.8k
          if (parseSchema(url, pos) == false) return false;
183
1.28k
          if (pathless) {
184
767
            parameters = url.substr(pos);
185
767
            return true;
186
767
          }
187
522
          if (parseUserPass(url, pos) == false) return false;
188
522
          if (parseHost(url, pos) == false) return false;
189
522
        }
190
722
        if (parsePath(url, pos) == false) return false;
191
722
        if (parseParameters(url, pos) == false) return false;
192
722
        return parseAnchor(url, pos);
193
722
    }; //<! 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
};