/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 | | }; |