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