Coverage Report

Created: 2025-08-28 07:02

/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