/src/poco/Foundation/src/URI.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // |
2 | | // URI.cpp |
3 | | // |
4 | | // Library: Foundation |
5 | | // Package: URI |
6 | | // Module: URI |
7 | | // |
8 | | // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. |
9 | | // and Contributors. |
10 | | // |
11 | | // SPDX-License-Identifier: BSL-1.0 |
12 | | // |
13 | | |
14 | | |
15 | | #include "Poco/URI.h" |
16 | | #include "Poco/NumberFormatter.h" |
17 | | #include "Poco/Exception.h" |
18 | | #include "Poco/String.h" |
19 | | #include "Poco/NumberParser.h" |
20 | | #include "Poco/Path.h" |
21 | | |
22 | | |
23 | | namespace Poco { |
24 | | |
25 | | |
26 | | const std::string URI::RESERVED_PATH = "?#"; |
27 | | const std::string URI::RESERVED_QUERY = "?#/:;+@"; |
28 | | const std::string URI::RESERVED_QUERY_PARAM = "?#/:;+@&="; |
29 | | const std::string URI::RESERVED_FRAGMENT = ""; |
30 | | const std::string URI::ILLEGAL = "%<>{}|\\\"^`!*'()$,[]"; |
31 | | |
32 | | |
33 | | URI::URI(): |
34 | 0 | _port(0) |
35 | 0 | { |
36 | 0 | } |
37 | | |
38 | | |
39 | | URI::URI(const std::string& uri): |
40 | 245k | _port(0) |
41 | 245k | { |
42 | 245k | parse(uri); |
43 | 245k | } |
44 | | |
45 | | |
46 | | URI::URI(const char* uri): |
47 | 0 | _port(0) |
48 | 0 | { |
49 | 0 | parse(std::string(uri)); |
50 | 0 | } |
51 | | |
52 | | |
53 | | URI::URI(const std::string& scheme, const std::string& pathEtc): |
54 | 0 | _scheme(scheme), |
55 | 0 | _port(0) |
56 | 0 | { |
57 | 0 | toLowerInPlace(_scheme); |
58 | 0 | std::string::const_iterator beg = pathEtc.begin(); |
59 | 0 | std::string::const_iterator end = pathEtc.end(); |
60 | 0 | parsePathEtc(beg, end); |
61 | 0 | } |
62 | | |
63 | | |
64 | | URI::URI(const std::string& scheme, const std::string& authority, const std::string& pathEtc): |
65 | 0 | _scheme(scheme) |
66 | 0 | { |
67 | 0 | toLowerInPlace(_scheme); |
68 | 0 | std::string::const_iterator beg = authority.begin(); |
69 | 0 | std::string::const_iterator end = authority.end(); |
70 | 0 | parseAuthority(beg, end); |
71 | 0 | beg = pathEtc.begin(); |
72 | 0 | end = pathEtc.end(); |
73 | 0 | parsePathEtc(beg, end); |
74 | 0 | } |
75 | | |
76 | | |
77 | | URI::URI(const std::string& scheme, const std::string& authority, const std::string& path, const std::string& query): |
78 | 0 | _scheme(scheme), |
79 | 0 | _path(path), |
80 | 0 | _query(query) |
81 | 0 | { |
82 | 0 | toLowerInPlace(_scheme); |
83 | 0 | std::string::const_iterator beg = authority.begin(); |
84 | 0 | std::string::const_iterator end = authority.end(); |
85 | 0 | parseAuthority(beg, end); |
86 | 0 | } |
87 | | |
88 | | |
89 | | URI::URI(const std::string& scheme, const std::string& authority, const std::string& path, const std::string& query, const std::string& fragment): |
90 | 0 | _scheme(scheme), |
91 | 0 | _path(path), |
92 | 0 | _query(query), |
93 | 0 | _fragment(fragment) |
94 | 0 | { |
95 | 0 | toLowerInPlace(_scheme); |
96 | 0 | std::string::const_iterator beg = authority.begin(); |
97 | 0 | std::string::const_iterator end = authority.end(); |
98 | 0 | parseAuthority(beg, end); |
99 | 0 | } |
100 | | |
101 | | |
102 | | URI::URI(const URI& uri): |
103 | 19.5k | _scheme(uri._scheme), |
104 | 19.5k | _userInfo(uri._userInfo), |
105 | 19.5k | _host(uri._host), |
106 | 19.5k | _port(uri._port), |
107 | 19.5k | _path(uri._path), |
108 | 19.5k | _query(uri._query), |
109 | 19.5k | _fragment(uri._fragment) |
110 | 19.5k | { |
111 | 19.5k | } |
112 | | |
113 | | |
114 | | URI::URI(URI&& uri) noexcept: |
115 | 0 | _scheme(std::move(uri._scheme)), |
116 | 0 | _userInfo(std::move(uri._userInfo)), |
117 | 0 | _host(std::move(uri._host)), |
118 | 0 | _port(std::move(uri._port)), |
119 | 0 | _path(std::move(uri._path)), |
120 | 0 | _query(std::move(uri._query)), |
121 | 0 | _fragment(std::move(uri._fragment)) |
122 | 0 | { |
123 | 0 | } |
124 | | |
125 | | |
126 | | URI::URI(const URI& baseURI, const std::string& relativeURI): |
127 | 2.23k | _scheme(baseURI._scheme), |
128 | 2.23k | _userInfo(baseURI._userInfo), |
129 | 2.23k | _host(baseURI._host), |
130 | 2.23k | _port(baseURI._port), |
131 | 2.23k | _path(baseURI._path), |
132 | 2.23k | _query(baseURI._query), |
133 | 2.23k | _fragment(baseURI._fragment) |
134 | 2.23k | { |
135 | 2.23k | resolve(relativeURI); |
136 | 2.23k | } |
137 | | |
138 | | |
139 | | URI::URI(const Path& path): |
140 | 0 | _scheme("file"), |
141 | 0 | _port(0) |
142 | 0 | { |
143 | 0 | Path absolutePath(path); |
144 | 0 | absolutePath.makeAbsolute(); |
145 | 0 | _path = absolutePath.toString(Path::PATH_UNIX); |
146 | 0 | } |
147 | | |
148 | | |
149 | | URI::~URI() |
150 | 261k | { |
151 | 261k | } |
152 | | |
153 | | |
154 | | URI& URI::operator = (const URI& uri) |
155 | 0 | { |
156 | 0 | if (&uri != this) |
157 | 0 | { |
158 | 0 | _scheme = uri._scheme; |
159 | 0 | _userInfo = uri._userInfo; |
160 | 0 | _host = uri._host; |
161 | 0 | _port = uri._port; |
162 | 0 | _path = uri._path; |
163 | 0 | _query = uri._query; |
164 | 0 | _fragment = uri._fragment; |
165 | 0 | } |
166 | 0 | return *this; |
167 | 0 | } |
168 | | |
169 | | |
170 | | URI& URI::operator = (URI&& uri) noexcept |
171 | 0 | { |
172 | 0 | _scheme = std::move(uri._scheme); |
173 | 0 | _userInfo = std::move(uri._userInfo); |
174 | 0 | _host = std::move(uri._host); |
175 | 0 | _port = std::move(uri._port); |
176 | 0 | _path = std::move(uri._path); |
177 | 0 | _query = std::move(uri._query); |
178 | 0 | _fragment = std::move(uri._fragment); |
179 | |
|
180 | 0 | return *this; |
181 | 0 | } |
182 | | |
183 | | |
184 | | URI& URI::operator = (const std::string& uri) |
185 | 2.21k | { |
186 | 2.21k | clear(); |
187 | 2.21k | parse(uri); |
188 | 2.21k | return *this; |
189 | 2.21k | } |
190 | | |
191 | | |
192 | | URI& URI::operator = (const char* uri) |
193 | 0 | { |
194 | 0 | clear(); |
195 | 0 | parse(std::string(uri)); |
196 | 0 | return *this; |
197 | 0 | } |
198 | | |
199 | | |
200 | | void URI::swap(URI& uri) noexcept |
201 | 0 | { |
202 | 0 | std::swap(_scheme, uri._scheme); |
203 | 0 | std::swap(_userInfo, uri._userInfo); |
204 | 0 | std::swap(_host, uri._host); |
205 | 0 | std::swap(_port, uri._port); |
206 | 0 | std::swap(_path, uri._path); |
207 | 0 | std::swap(_query, uri._query); |
208 | 0 | std::swap(_fragment, uri._fragment); |
209 | 0 | } |
210 | | |
211 | | |
212 | | void URI::clear() |
213 | 2.21k | { |
214 | 2.21k | _scheme.clear(); |
215 | 2.21k | _userInfo.clear(); |
216 | 2.21k | _host.clear(); |
217 | 2.21k | _port = 0; |
218 | 2.21k | _path.clear(); |
219 | 2.21k | _query.clear(); |
220 | 2.21k | _fragment.clear(); |
221 | 2.21k | } |
222 | | |
223 | | |
224 | | std::string URI::toString() const |
225 | 132k | { |
226 | 132k | std::string uri; |
227 | 132k | if (isRelative()) |
228 | 79.4k | { |
229 | 79.4k | encode(_path, RESERVED_PATH, uri); |
230 | 79.4k | } |
231 | 52.7k | else |
232 | 52.7k | { |
233 | 52.7k | uri = _scheme; |
234 | 52.7k | uri += ':'; |
235 | 52.7k | std::string auth = getAuthority(); |
236 | 52.7k | if (!auth.empty() || _scheme == "file") |
237 | 41.0k | { |
238 | 41.0k | uri.append("//"); |
239 | 41.0k | uri.append(auth); |
240 | 41.0k | } |
241 | 52.7k | if (!_path.empty()) |
242 | 44.2k | { |
243 | 44.2k | if (!auth.empty() && _path[0] != '/') |
244 | 17 | uri += '/'; |
245 | 44.2k | encode(_path, RESERVED_PATH, uri); |
246 | 44.2k | } |
247 | 8.46k | else if (!_query.empty() || !_fragment.empty()) |
248 | 4.08k | { |
249 | 4.08k | uri += '/'; |
250 | 4.08k | } |
251 | 52.7k | } |
252 | 132k | if (!_query.empty()) |
253 | 41.3k | { |
254 | 41.3k | uri += '?'; |
255 | 41.3k | uri.append(_query); |
256 | 41.3k | } |
257 | 132k | if (!_fragment.empty()) |
258 | 16.3k | { |
259 | 16.3k | uri += '#'; |
260 | 16.3k | uri.append(_fragment); |
261 | 16.3k | } |
262 | 132k | return uri; |
263 | 132k | } |
264 | | |
265 | | |
266 | | void URI::setScheme(const std::string& scheme) |
267 | 63.6k | { |
268 | 63.6k | _scheme = scheme; |
269 | 63.6k | toLowerInPlace(_scheme); |
270 | 63.6k | } |
271 | | |
272 | | |
273 | | void URI::setUserInfo(const std::string& userInfo) |
274 | 0 | { |
275 | 0 | _userInfo.clear(); |
276 | 0 | decode(userInfo, _userInfo); |
277 | 0 | } |
278 | | |
279 | | |
280 | | void URI::setHost(const std::string& host) |
281 | 0 | { |
282 | 0 | _host = host; |
283 | 0 | } |
284 | | |
285 | | |
286 | | unsigned short URI::getPort() const |
287 | 0 | { |
288 | 0 | if (_port == 0) |
289 | 0 | return getWellKnownPort(); |
290 | 0 | else |
291 | 0 | return _port; |
292 | 0 | } |
293 | | |
294 | | |
295 | | void URI::setPort(unsigned short port) |
296 | 0 | { |
297 | 0 | _port = port; |
298 | 0 | } |
299 | | |
300 | | |
301 | | std::string URI::getAuthority() const |
302 | 52.7k | { |
303 | 52.7k | std::string auth; |
304 | 52.7k | if (!_userInfo.empty()) |
305 | 1.38k | { |
306 | 1.38k | auth.append(_userInfo); |
307 | 1.38k | auth += '@'; |
308 | 1.38k | } |
309 | 52.7k | if (_host.find(':') != std::string::npos) |
310 | 669 | { |
311 | 669 | auth += '['; |
312 | 669 | auth += _host; |
313 | 669 | auth += ']'; |
314 | 669 | } |
315 | 52.0k | else auth.append(_host); |
316 | 52.7k | if (_port && !isWellKnownPort()) |
317 | 2.98k | { |
318 | 2.98k | auth += ':'; |
319 | 2.98k | NumberFormatter::append(auth, _port); |
320 | 2.98k | } |
321 | 52.7k | return auth; |
322 | 52.7k | } |
323 | | |
324 | | |
325 | | void URI::setAuthority(const std::string& authority) |
326 | 0 | { |
327 | 0 | _userInfo.clear(); |
328 | 0 | _host.clear(); |
329 | 0 | _port = 0; |
330 | 0 | std::string::const_iterator beg = authority.begin(); |
331 | 0 | std::string::const_iterator end = authority.end(); |
332 | 0 | parseAuthority(beg, end); |
333 | 0 | } |
334 | | |
335 | | |
336 | | void URI::setPath(const std::string& path) |
337 | 0 | { |
338 | 0 | _path.clear(); |
339 | 0 | decode(path, _path); |
340 | 0 | } |
341 | | |
342 | | |
343 | | void URI::setRawQuery(const std::string& query) |
344 | 0 | { |
345 | 0 | _query = query; |
346 | 0 | } |
347 | | |
348 | | |
349 | | void URI::setQuery(const std::string& query) |
350 | 1.37k | { |
351 | 1.37k | _query.clear(); |
352 | 1.37k | encode(query, RESERVED_QUERY, _query); |
353 | 1.37k | } |
354 | | |
355 | | |
356 | | void URI::addQueryParameter(const std::string& param, const std::string& val) |
357 | 1.21M | { |
358 | 1.21M | if (!_query.empty()) _query += '&'; |
359 | 1.21M | encode(param, RESERVED_QUERY_PARAM, _query); |
360 | 1.21M | _query += '='; |
361 | 1.21M | encode(val, RESERVED_QUERY_PARAM, _query); |
362 | 1.21M | } |
363 | | |
364 | | |
365 | | std::string URI::getQuery() const |
366 | 0 | { |
367 | 0 | std::string query; |
368 | 0 | decode(_query, query); |
369 | 0 | return query; |
370 | 0 | } |
371 | | |
372 | | |
373 | | URI::QueryParameters URI::getQueryParameters(bool plusIsSpace) const |
374 | 2.21k | { |
375 | 2.21k | QueryParameters result; |
376 | 2.21k | std::string::const_iterator it(_query.begin()); |
377 | 2.21k | std::string::const_iterator end(_query.end()); |
378 | 1.22M | while (it != end) |
379 | 1.21M | { |
380 | 1.21M | std::string name; |
381 | 1.21M | std::string value; |
382 | 9.90M | while (it != end && *it != '=' && *it != '&') |
383 | 8.68M | { |
384 | 8.68M | if (plusIsSpace && (*it == '+')) |
385 | 2.20k | name += ' '; |
386 | 8.68M | else |
387 | 8.68M | name += *it; |
388 | 8.68M | ++it; |
389 | 8.68M | } |
390 | 1.21M | if (it != end && *it == '=') |
391 | 1.46k | { |
392 | 1.46k | ++it; |
393 | 1.49M | while (it != end && *it != '&') |
394 | 1.49M | { |
395 | 1.49M | if (plusIsSpace && (*it == '+')) |
396 | 1.64k | value += ' '; |
397 | 1.49M | else |
398 | 1.49M | value += *it; |
399 | 1.49M | ++it; |
400 | 1.49M | } |
401 | 1.46k | } |
402 | 1.21M | std::string decodedName; |
403 | 1.21M | std::string decodedValue; |
404 | 1.21M | URI::decode(name, decodedName); |
405 | 1.21M | URI::decode(value, decodedValue); |
406 | 1.21M | result.push_back(std::make_pair(decodedName, decodedValue)); |
407 | 1.21M | if (it != end && *it == '&') ++it; |
408 | 1.21M | } |
409 | 2.21k | return result; |
410 | 2.21k | } |
411 | | |
412 | | |
413 | | void URI::setQueryParameters(const QueryParameters& params) |
414 | 2.21k | { |
415 | 2.21k | _query.clear(); |
416 | 2.21k | for (const auto& p: params) |
417 | 1.21M | { |
418 | 1.21M | addQueryParameter(p.first, p.second); |
419 | 1.21M | } |
420 | 2.21k | } |
421 | | |
422 | | |
423 | | std::string URI::getFragment() const |
424 | 0 | { |
425 | 0 | std::string fragment; |
426 | 0 | decode(_fragment, fragment); |
427 | 0 | return fragment; |
428 | 0 | } |
429 | | |
430 | | |
431 | | void URI::setFragment(const std::string& fragment) |
432 | 1.37k | { |
433 | 1.37k | _fragment.clear(); |
434 | 1.37k | encode(fragment, RESERVED_FRAGMENT, _fragment); |
435 | 1.37k | } |
436 | | |
437 | | |
438 | | void URI::setRawFragment(const std::string& fragment) |
439 | 0 | { |
440 | 0 | _fragment = fragment; |
441 | 0 | } |
442 | | |
443 | | |
444 | | void URI::setPathEtc(const std::string& pathEtc) |
445 | 0 | { |
446 | 0 | _path.clear(); |
447 | 0 | _query.clear(); |
448 | 0 | _fragment.clear(); |
449 | 0 | std::string::const_iterator beg = pathEtc.begin(); |
450 | 0 | std::string::const_iterator end = pathEtc.end(); |
451 | 0 | parsePathEtc(beg, end); |
452 | 0 | } |
453 | | |
454 | | |
455 | | std::string URI::getPathEtc() const |
456 | 0 | { |
457 | 0 | std::string pathEtc; |
458 | 0 | encode(_path, RESERVED_PATH, pathEtc); |
459 | 0 | if (!_query.empty()) |
460 | 0 | { |
461 | 0 | pathEtc += '?'; |
462 | 0 | pathEtc += _query; |
463 | 0 | } |
464 | 0 | if (!_fragment.empty()) |
465 | 0 | { |
466 | 0 | pathEtc += '#'; |
467 | 0 | pathEtc += _fragment; |
468 | 0 | } |
469 | 0 | return pathEtc; |
470 | 0 | } |
471 | | |
472 | | |
473 | | std::string URI::getPathAndQuery() const |
474 | 0 | { |
475 | 0 | std::string pathAndQuery; |
476 | 0 | encode(_path, RESERVED_PATH, pathAndQuery); |
477 | 0 | if (!_query.empty()) |
478 | 0 | { |
479 | 0 | pathAndQuery += '?'; |
480 | 0 | pathAndQuery += _query; |
481 | 0 | } |
482 | 0 | return pathAndQuery; |
483 | 0 | } |
484 | | |
485 | | |
486 | | void URI::resolve(const std::string& relativeURI) |
487 | 78.5k | { |
488 | 78.5k | URI parsedURI(relativeURI); |
489 | 78.5k | resolve(parsedURI); |
490 | 78.5k | } |
491 | | |
492 | | |
493 | | void URI::resolve(const URI& relativeURI) |
494 | 78.0k | { |
495 | 78.0k | if (!relativeURI._scheme.empty()) |
496 | 28.2k | { |
497 | 28.2k | _scheme = relativeURI._scheme; |
498 | 28.2k | _userInfo = relativeURI._userInfo; |
499 | 28.2k | _host = relativeURI._host; |
500 | 28.2k | _port = relativeURI._port; |
501 | 28.2k | _path = relativeURI._path; |
502 | 28.2k | _query = relativeURI._query; |
503 | 28.2k | removeDotSegments(); |
504 | 28.2k | } |
505 | 49.7k | else |
506 | 49.7k | { |
507 | 49.7k | if (!relativeURI._host.empty()) |
508 | 1.26k | { |
509 | 1.26k | _userInfo = relativeURI._userInfo; |
510 | 1.26k | _host = relativeURI._host; |
511 | 1.26k | _port = relativeURI._port; |
512 | 1.26k | _path = relativeURI._path; |
513 | 1.26k | _query = relativeURI._query; |
514 | 1.26k | removeDotSegments(); |
515 | 1.26k | } |
516 | 48.5k | else |
517 | 48.5k | { |
518 | 48.5k | if (relativeURI._path.empty()) |
519 | 8.67k | { |
520 | 8.67k | if (!relativeURI._query.empty()) |
521 | 1.75k | _query = relativeURI._query; |
522 | 8.67k | } |
523 | 39.8k | else |
524 | 39.8k | { |
525 | 39.8k | if (relativeURI._path[0] == '/') |
526 | 1.26k | { |
527 | 1.26k | _path = relativeURI._path; |
528 | 1.26k | removeDotSegments(); |
529 | 1.26k | } |
530 | 38.5k | else |
531 | 38.5k | { |
532 | 38.5k | mergePath(relativeURI._path); |
533 | 38.5k | } |
534 | 39.8k | _query = relativeURI._query; |
535 | 39.8k | } |
536 | 48.5k | } |
537 | 49.7k | } |
538 | 78.0k | _fragment = relativeURI._fragment; |
539 | 78.0k | } |
540 | | |
541 | | |
542 | | bool URI::isRelative() const |
543 | 150k | { |
544 | 150k | return _scheme.empty(); |
545 | 150k | } |
546 | | |
547 | | |
548 | | bool URI::empty() const |
549 | 0 | { |
550 | 0 | return _scheme.empty() && _host.empty() && _path.empty() && _query.empty() && _fragment.empty(); |
551 | 0 | } |
552 | | |
553 | | |
554 | | bool URI::operator == (const URI& uri) const |
555 | 0 | { |
556 | 0 | return equals(uri); |
557 | 0 | } |
558 | | |
559 | | |
560 | | bool URI::operator == (const std::string& uri) const |
561 | 0 | { |
562 | 0 | URI parsedURI(uri); |
563 | 0 | return equals(parsedURI); |
564 | 0 | } |
565 | | |
566 | | |
567 | | bool URI::operator != (const URI& uri) const |
568 | 0 | { |
569 | 0 | return !equals(uri); |
570 | 0 | } |
571 | | |
572 | | |
573 | | bool URI::operator != (const std::string& uri) const |
574 | 0 | { |
575 | 0 | URI parsedURI(uri); |
576 | 0 | return !equals(parsedURI); |
577 | 0 | } |
578 | | |
579 | | |
580 | | bool URI::equals(const URI& uri) const |
581 | 0 | { |
582 | 0 | return _scheme == uri._scheme |
583 | 0 | && _userInfo == uri._userInfo |
584 | 0 | && _host == uri._host |
585 | 0 | && getPort() == uri.getPort() |
586 | 0 | && _path == uri._path |
587 | 0 | && _query == uri._query |
588 | 0 | && _fragment == uri._fragment; |
589 | 0 | } |
590 | | |
591 | | |
592 | | void URI::normalize() |
593 | 2.21k | { |
594 | 2.21k | removeDotSegments(!isRelative()); |
595 | 2.21k | } |
596 | | |
597 | | |
598 | | void URI::removeDotSegments(bool removeLeading) |
599 | 33.0k | { |
600 | 33.0k | if (_path.empty()) return; |
601 | | |
602 | 28.1k | bool leadingSlash = *(_path.begin()) == '/'; |
603 | 28.1k | bool trailingSlash = *(_path.rbegin()) == '/'; |
604 | 28.1k | std::vector<std::string> segments; |
605 | 28.1k | std::vector<std::string> normalizedSegments; |
606 | 28.1k | getPathSegments(segments); |
607 | 28.1k | for (const auto& s: segments) |
608 | 143k | { |
609 | 143k | if (s == "..") |
610 | 36.9k | { |
611 | 36.9k | if (!normalizedSegments.empty()) |
612 | 36.2k | { |
613 | 36.2k | if (normalizedSegments.back() == "..") |
614 | 33.2k | normalizedSegments.push_back(s); |
615 | 2.96k | else |
616 | 2.96k | normalizedSegments.pop_back(); |
617 | 36.2k | } |
618 | 682 | else if (!removeLeading) |
619 | 25 | { |
620 | 25 | normalizedSegments.push_back(s); |
621 | 25 | } |
622 | 36.9k | } |
623 | 106k | else if (s != ".") |
624 | 104k | { |
625 | 104k | normalizedSegments.push_back(s); |
626 | 104k | } |
627 | 143k | } |
628 | 28.1k | buildPath(normalizedSegments, leadingSlash, trailingSlash); |
629 | 28.1k | } |
630 | | |
631 | | |
632 | | void URI::getPathSegments(std::vector<std::string>& segments) const |
633 | 28.2k | { |
634 | 28.2k | getPathSegments(_path, segments); |
635 | 28.2k | } |
636 | | |
637 | | |
638 | | void URI::getPathSegments(const std::string& path, std::vector<std::string>& segments) |
639 | 66.8k | { |
640 | 66.8k | std::string::const_iterator it = path.begin(); |
641 | 66.8k | std::string::const_iterator end = path.end(); |
642 | 66.8k | std::string seg; |
643 | 107M | while (it != end) |
644 | 107M | { |
645 | 107M | if (*it == '/') |
646 | 173k | { |
647 | 173k | if (!seg.empty()) |
648 | 155k | { |
649 | 155k | segments.push_back(seg); |
650 | 155k | seg.clear(); |
651 | 155k | } |
652 | 173k | } |
653 | 107M | else seg += *it; |
654 | 107M | ++it; |
655 | 107M | } |
656 | 66.8k | if (!seg.empty()) |
657 | 58.3k | segments.push_back(seg); |
658 | 66.8k | } |
659 | | |
660 | | |
661 | | void URI::encode(const std::string& str, const std::string& reserved, std::string& encodedStr) |
662 | 2.61M | { |
663 | 2.61M | for (auto c: str) |
664 | 208M | { |
665 | 208M | if ((c >= 'a' && c <= 'z') || |
666 | 208M | (c >= 'A' && c <= 'Z') || |
667 | 208M | (c >= '0' && c <= '9') || |
668 | 208M | c == '-' || c == '_' || |
669 | 208M | c == '.' || c == '~') |
670 | 35.9M | { |
671 | 35.9M | encodedStr += c; |
672 | 35.9M | } |
673 | 172M | else if (c <= 0x20 || c >= 0x7F || ILLEGAL.find(c) != std::string::npos || reserved.find(c) != std::string::npos) |
674 | 171M | { |
675 | 171M | encodedStr += '%'; |
676 | 171M | encodedStr += NumberFormatter::formatHex((unsigned) (unsigned char) c, 2); |
677 | 171M | } |
678 | 710k | else encodedStr += c; |
679 | 208M | } |
680 | 2.61M | } |
681 | | |
682 | | |
683 | | void URI::decode(const std::string& str, std::string& decodedStr, bool plusAsSpace) |
684 | 2.60M | { |
685 | 2.60M | bool inQuery = false; |
686 | 2.60M | std::string::const_iterator it = str.begin(); |
687 | 2.60M | std::string::const_iterator end = str.end(); |
688 | 214M | while (it != end) |
689 | 211M | { |
690 | 211M | char c = *it++; |
691 | 211M | if (c == '?') inQuery = true; |
692 | | // spaces may be encoded as plus signs in the query |
693 | 211M | if (inQuery && plusAsSpace && c == '+') c = ' '; |
694 | 211M | else if (c == '%') |
695 | 77.8M | { |
696 | 77.8M | if (it == end) throw URISyntaxException("URI encoding: no hex digit following percent sign", str); |
697 | 77.8M | char hi = *it++; |
698 | 77.8M | if (it == end) throw URISyntaxException("URI encoding: two hex digits must follow percent sign", str); |
699 | 77.8M | char lo = *it++; |
700 | 77.8M | if (hi >= '0' && hi <= '9') |
701 | 39.6M | c = hi - '0'; |
702 | 38.1M | else if (hi >= 'A' && hi <= 'F') |
703 | 38.1M | c = hi - 'A' + 10; |
704 | 1.20k | else if (hi >= 'a' && hi <= 'f') |
705 | 1.09k | c = hi - 'a' + 10; |
706 | 109 | else throw URISyntaxException("URI encoding: not a hex digit"); |
707 | 77.8M | c *= 16; |
708 | 77.8M | if (lo >= '0' && lo <= '9') |
709 | 74.5M | c += lo - '0'; |
710 | 3.24M | else if (lo >= 'A' && lo <= 'F') |
711 | 3.24M | c += lo - 'A' + 10; |
712 | 1.26k | else if (lo >= 'a' && lo <= 'f') |
713 | 1.18k | c += lo - 'a' + 10; |
714 | 82 | else throw URISyntaxException("URI encoding: not a hex digit"); |
715 | 77.8M | } |
716 | 211M | decodedStr += c; |
717 | 211M | } |
718 | 2.60M | } |
719 | | |
720 | | |
721 | | bool URI::isWellKnownPort() const |
722 | 3.00k | { |
723 | 3.00k | return _port == getWellKnownPort(); |
724 | 3.00k | } |
725 | | |
726 | | |
727 | | unsigned short URI::getWellKnownPort() const |
728 | 3.00k | { |
729 | 3.00k | if (_scheme == "ftp") |
730 | 19 | return 21; |
731 | 2.98k | else if (_scheme == "ssh") |
732 | 16 | return 22; |
733 | 2.96k | else if (_scheme == "telnet") |
734 | 14 | return 23; |
735 | 2.95k | else if (_scheme == "smtp") |
736 | 18 | return 25; |
737 | 2.93k | else if (_scheme == "dns") |
738 | 14 | return 53; |
739 | 2.92k | else if (_scheme == "http" || _scheme == "ws") |
740 | 35 | return 80; |
741 | 2.88k | else if (_scheme == "nntp") |
742 | 13 | return 119; |
743 | 2.87k | else if (_scheme == "imap") |
744 | 13 | return 143; |
745 | 2.85k | else if (_scheme == "ldap") |
746 | 13 | return 389; |
747 | 2.84k | else if (_scheme == "https" || _scheme == "wss") |
748 | 27 | return 443; |
749 | 2.81k | else if (_scheme == "smtps") |
750 | 14 | return 465; |
751 | 2.80k | else if (_scheme == "rtsp") |
752 | 14 | return 554; |
753 | 2.79k | else if (_scheme == "ldaps") |
754 | 14 | return 636; |
755 | 2.77k | else if (_scheme == "dnss") |
756 | 16 | return 853; |
757 | 2.76k | else if (_scheme == "imaps") |
758 | 14 | return 993; |
759 | 2.74k | else if (_scheme == "sip") |
760 | 17 | return 5060; |
761 | 2.73k | else if (_scheme == "sips") |
762 | 14 | return 5061; |
763 | 2.71k | else if (_scheme == "xmpp") |
764 | 14 | return 5222; |
765 | 2.70k | else |
766 | 2.70k | return 0; |
767 | 3.00k | } |
768 | | |
769 | | |
770 | | void URI::parse(const std::string& uri) |
771 | 247k | { |
772 | 247k | std::string::const_iterator it = uri.begin(); |
773 | 247k | std::string::const_iterator end = uri.end(); |
774 | 247k | if (it == end) return; |
775 | 164k | if (*it != '/' && *it != '.' && *it != '?' && *it != '#') |
776 | 88.3k | { |
777 | 88.3k | std::string scheme; |
778 | 196M | while (it != end && *it != ':' && *it != '?' && *it != '#' && *it != '/') scheme += *it++; |
779 | 88.3k | if (it != end && *it == ':') |
780 | 64.0k | { |
781 | 64.0k | ++it; |
782 | 64.0k | if (it == end) throw URISyntaxException("URI scheme must be followed by authority or path", uri); |
783 | 63.6k | setScheme(scheme); |
784 | 63.6k | if (*it == '/') |
785 | 32.1k | { |
786 | 32.1k | ++it; |
787 | 32.1k | if (it != end && *it == '/') |
788 | 31.0k | { |
789 | 31.0k | ++it; |
790 | 31.0k | parseAuthority(it, end); |
791 | 31.0k | } |
792 | 1.11k | else --it; |
793 | 32.1k | } |
794 | 63.6k | parsePathEtc(it, end); |
795 | 63.6k | } |
796 | 24.2k | else |
797 | 24.2k | { |
798 | 24.2k | it = uri.begin(); |
799 | 24.2k | parsePathEtc(it, end); |
800 | 24.2k | } |
801 | 88.3k | } |
802 | 76.4k | else parsePathEtc(it, end); |
803 | 164k | } |
804 | | |
805 | | |
806 | | void URI::parseAuthority(std::string::const_iterator& it, const std::string::const_iterator& end) |
807 | 31.0k | { |
808 | 31.0k | std::string userInfo; |
809 | 31.0k | std::string part; |
810 | 179M | while (it != end && *it != '/' && *it != '?' && *it != '#') |
811 | 179M | { |
812 | 179M | if (*it == '@') |
813 | 12.3k | { |
814 | 12.3k | userInfo = part; |
815 | 12.3k | part.clear(); |
816 | 12.3k | } |
817 | 179M | else part += *it; |
818 | 179M | ++it; |
819 | 179M | } |
820 | 31.0k | std::string::const_iterator pbeg = part.begin(); |
821 | 31.0k | std::string::const_iterator pend = part.end(); |
822 | 31.0k | parseHostAndPort(pbeg, pend); |
823 | 31.0k | _userInfo = userInfo; |
824 | 31.0k | } |
825 | | |
826 | | |
827 | | void URI::parseHostAndPort(std::string::const_iterator& it, const std::string::const_iterator& end) |
828 | 31.0k | { |
829 | 31.0k | if (it == end) return; |
830 | 29.1k | std::string host; |
831 | 29.1k | if (*it == '[') |
832 | 1.26k | { |
833 | | // IPv6 address |
834 | 1.26k | ++it; |
835 | 5.44M | while (it != end && *it != ']') host += *it++; |
836 | 1.26k | if (it == end) throw URISyntaxException("unterminated IPv6 address"); |
837 | 1.23k | ++it; |
838 | 1.23k | } |
839 | 27.8k | else |
840 | 27.8k | { |
841 | 24.2M | while (it != end && *it != ':') host += *it++; |
842 | 27.8k | } |
843 | 29.0k | if (it != end && *it == ':') |
844 | 11.6k | { |
845 | 11.6k | ++it; |
846 | 11.6k | std::string port; |
847 | 4.29M | while (it != end) port += *it++; |
848 | 11.6k | if (!port.empty()) |
849 | 10.4k | { |
850 | 10.4k | int nport = 0; |
851 | 10.4k | if (NumberParser::tryParse(port, nport) && nport > 0 && nport < 65536) |
852 | 4.74k | _port = (unsigned short) nport; |
853 | 5.68k | else |
854 | 5.68k | throw URISyntaxException("bad or invalid port number", port); |
855 | 10.4k | } |
856 | 1.22k | else _port = 0; |
857 | 11.6k | } |
858 | 17.4k | else _port = 0; |
859 | 23.4k | _host = host; |
860 | 23.4k | if (_host.size() && _host[0] != '%') |
861 | 14.5k | toLowerInPlace(_host); |
862 | 23.4k | } |
863 | | |
864 | | |
865 | | void URI::parsePath(std::string::const_iterator& it, const std::string::const_iterator& end) |
866 | 129k | { |
867 | 129k | std::string path; |
868 | 347M | while (it != end && *it != '?' && *it != '#') path += *it++; |
869 | 129k | decode(path, _path); |
870 | 129k | } |
871 | | |
872 | | |
873 | | void URI::parsePathEtc(std::string::const_iterator& it, const std::string::const_iterator& end) |
874 | 158k | { |
875 | 158k | if (it == end) return; |
876 | 147k | if (*it != '?' && *it != '#') |
877 | 129k | parsePath(it, end); |
878 | 147k | if (it != end && *it == '?') |
879 | 54.0k | { |
880 | 54.0k | ++it; |
881 | 54.0k | parseQuery(it, end); |
882 | 54.0k | } |
883 | 147k | if (it != end && *it == '#') |
884 | 18.2k | { |
885 | 18.2k | ++it; |
886 | 18.2k | parseFragment(it, end); |
887 | 18.2k | } |
888 | 147k | } |
889 | | |
890 | | |
891 | | void URI::parseQuery(std::string::const_iterator& it, const std::string::const_iterator& end) |
892 | 54.0k | { |
893 | 54.0k | _query.clear(); |
894 | 93.1M | while (it != end && *it != '#') _query += *it++; |
895 | 54.0k | } |
896 | | |
897 | | |
898 | | void URI::parseFragment(std::string::const_iterator& it, const std::string::const_iterator& end) |
899 | 18.2k | { |
900 | 18.2k | _fragment.clear(); |
901 | 29.9M | while (it != end) _fragment += *it++; |
902 | 18.2k | } |
903 | | |
904 | | |
905 | | void URI::mergePath(const std::string& path) |
906 | 38.5k | { |
907 | 38.5k | std::vector<std::string> segments; |
908 | 38.5k | std::vector<std::string> normalizedSegments; |
909 | 38.5k | bool addLeadingSlash = false; |
910 | 38.5k | if (!_path.empty()) |
911 | 112 | { |
912 | 112 | getPathSegments(segments); |
913 | 112 | bool endsWithSlash = *(_path.rbegin()) == '/'; |
914 | 112 | if (!endsWithSlash && !segments.empty()) |
915 | 65 | segments.pop_back(); |
916 | 112 | addLeadingSlash = _path[0] == '/'; |
917 | 112 | } |
918 | 38.5k | getPathSegments(path, segments); |
919 | 38.5k | addLeadingSlash = addLeadingSlash || (!path.empty() && path[0] == '/'); |
920 | 38.5k | bool hasTrailingSlash = (!path.empty() && *(path.rbegin()) == '/'); |
921 | 38.5k | bool addTrailingSlash = false; |
922 | 38.5k | for (const auto& s: segments) |
923 | 70.6k | { |
924 | 70.6k | if (s == "..") |
925 | 2.02k | { |
926 | 2.02k | addTrailingSlash = true; |
927 | 2.02k | if (!normalizedSegments.empty()) |
928 | 1.50k | normalizedSegments.pop_back(); |
929 | 2.02k | } |
930 | 68.6k | else if (s != ".") |
931 | 66.1k | { |
932 | 66.1k | addTrailingSlash = false; |
933 | 66.1k | normalizedSegments.push_back(s); |
934 | 66.1k | } |
935 | 2.53k | else addTrailingSlash = true; |
936 | 70.6k | } |
937 | 38.5k | buildPath(normalizedSegments, addLeadingSlash, hasTrailingSlash || addTrailingSlash); |
938 | 38.5k | } |
939 | | |
940 | | |
941 | | void URI::buildPath(const std::vector<std::string>& segments, bool leadingSlash, bool trailingSlash) |
942 | 66.7k | { |
943 | 66.7k | _path.clear(); |
944 | 66.7k | bool first = true; |
945 | 66.7k | for (const auto& s: segments) |
946 | 199k | { |
947 | 199k | if (first) |
948 | 62.8k | { |
949 | 62.8k | first = false; |
950 | 62.8k | if (leadingSlash) |
951 | 1.96k | _path += '/'; |
952 | 60.9k | else if (_scheme.empty() && s.find(':') != std::string::npos) |
953 | 679 | _path.append("./"); |
954 | 62.8k | } |
955 | 136k | else _path += '/'; |
956 | 199k | _path.append(s); |
957 | 199k | } |
958 | 66.7k | if (trailingSlash) |
959 | 9.72k | _path += '/'; |
960 | 66.7k | } |
961 | | |
962 | | |
963 | | } // namespace Poco |