Line data Source code
1 : //
2 : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/boostorg/url
9 : //
10 :
11 :
12 : #include <boost/url/detail/config.hpp>
13 : #include <boost/url/url_base.hpp>
14 : #include <boost/url/encode.hpp>
15 : #include <boost/url/error.hpp>
16 : #include <boost/url/host_type.hpp>
17 : #include <boost/url/scheme.hpp>
18 : #include <boost/url/url_view.hpp>
19 : #include <boost/url/detail/any_params_iter.hpp>
20 : #include <boost/url/detail/any_segments_iter.hpp>
21 : #include "detail/decode.hpp"
22 : #include <boost/url/detail/encode.hpp>
23 : #include <boost/url/detail/except.hpp>
24 : #include "detail/normalize.hpp"
25 : #include "detail/path.hpp"
26 : #include "detail/print.hpp"
27 : #include <boost/url/grammar/ci_string.hpp>
28 : #include <boost/url/rfc/authority_rule.hpp>
29 : #include <boost/url/rfc/query_rule.hpp>
30 : #include "rfc/detail/charsets.hpp"
31 : #include "rfc/detail/host_rule.hpp"
32 : #include "rfc/detail/ipvfuture_rule.hpp"
33 : #include "boost/url/rfc/detail/path_rules.hpp"
34 : #include "rfc/detail/port_rule.hpp"
35 : #include "rfc/detail/scheme_rule.hpp"
36 : #include "rfc/detail/userinfo_rule.hpp"
37 : #include <boost/url/grammar/parse.hpp>
38 : #include "detail/move_chars.hpp"
39 : #include <cstring>
40 : #include <iostream>
41 : #include <stdexcept>
42 : #include <utility>
43 :
44 : namespace boost {
45 : namespace urls {
46 :
47 : //------------------------------------------------
48 :
49 : // these objects help handle the cases
50 : // where the user passes in strings that
51 : // come from inside the url buffer.
52 :
53 8034 : url_base::
54 : op_t::
55 8034 : ~op_t()
56 : {
57 8034 : if(old)
58 1009 : u.cleanup(*this);
59 8034 : u.check_invariants();
60 8034 : }
61 :
62 8034 : url_base::
63 : op_t::
64 : op_t(
65 : url_base& impl_,
66 : core::string_view* s0_,
67 8034 : core::string_view* s1_) noexcept
68 : : u(impl_)
69 : , s0(s0_)
70 8034 : , s1(s1_)
71 : {
72 8034 : u.check_invariants();
73 8034 : }
74 :
75 : void
76 2326 : url_base::
77 : op_t::
78 : move(
79 : char* dest,
80 : char const* src,
81 : std::size_t n) noexcept
82 : {
83 2326 : if(! n)
84 402 : return;
85 1924 : if(s0)
86 : {
87 1226 : if(s1)
88 62 : return detail::move_chars(
89 62 : dest, src, n, *s0, *s1);
90 1164 : return detail::move_chars(
91 1164 : dest, src, n, *s0);
92 : }
93 698 : detail::move_chars(
94 : dest, src, n);
95 : }
96 :
97 : //------------------------------------------------
98 :
99 : // construct reference
100 1500 : url_base::
101 : url_base(
102 1500 : detail::url_impl const& impl) noexcept
103 1500 : : url_view_base(impl)
104 : {
105 1500 : }
106 :
107 : void
108 149 : url_base::
109 : reserve_impl(std::size_t n)
110 : {
111 298 : op_t op(*this);
112 149 : reserve_impl(n, op);
113 148 : if(s_)
114 146 : s_[size()] = '\0';
115 148 : }
116 :
117 : // make a copy of u
118 : void
119 3662 : url_base::
120 : copy(url_view_base const& u)
121 : {
122 3662 : if (this == &u)
123 117 : return;
124 3665 : op_t op(*this);
125 3662 : if(u.size() == 0)
126 : {
127 117 : clear();
128 117 : return;
129 : }
130 3545 : reserve_impl(
131 3545 : u.size(), op);
132 3542 : impl_ = u.impl_;
133 3542 : impl_.cs_ = s_;
134 3542 : impl_.from_ = {from::url};
135 3542 : std::memcpy(s_,
136 3542 : u.data(), u.size());
137 3542 : s_[size()] = '\0';
138 : }
139 :
140 : //------------------------------------------------
141 : //
142 : // Scheme
143 : //
144 : //------------------------------------------------
145 :
146 : url_base&
147 52 : url_base::
148 : set_scheme(core::string_view s)
149 : {
150 52 : set_scheme_impl(
151 : s, string_to_scheme(s));
152 39 : return *this;
153 : }
154 :
155 : url_base&
156 11 : url_base::
157 : set_scheme_id(urls::scheme id)
158 : {
159 11 : if(id == urls::scheme::unknown)
160 1 : detail::throw_invalid_argument();
161 10 : if(id == urls::scheme::none)
162 1 : return remove_scheme();
163 9 : set_scheme_impl(to_string(id), id);
164 9 : return *this;
165 : }
166 :
167 : url_base&
168 36 : url_base::
169 : remove_scheme()
170 : {
171 72 : op_t op(*this);
172 36 : auto const sn = impl_.len(id_scheme);
173 36 : if(sn == 0)
174 9 : return *this;
175 27 : auto const po = impl_.offset(id_path);
176 27 : auto fseg = first_segment();
177 : bool const encode_colon =
178 27 : !has_authority() &&
179 20 : impl_.nseg_ > 0 &&
180 58 : s_[po] != '/' &&
181 11 : fseg.contains(':');
182 27 : if(!encode_colon)
183 : {
184 : // just remove the scheme
185 18 : resize_impl(id_scheme, 0, op);
186 18 : impl_.scheme_ = urls::scheme::none;
187 18 : check_invariants();
188 18 : return *this;
189 : }
190 : // encode any ":" in the first path segment
191 9 : BOOST_ASSERT(sn >= 2);
192 9 : auto pn = impl_.len(id_path);
193 9 : std::size_t cn = 0;
194 46 : for (char c: fseg)
195 37 : cn += c == ':';
196 : std::size_t new_size =
197 9 : size() - sn + 2 * cn;
198 9 : bool need_resize = new_size > size();
199 9 : if (need_resize)
200 : {
201 1 : resize_impl(
202 1 : id_path, pn + 2 * cn, op);
203 : }
204 : // move [id_scheme, id_path) left
205 9 : op.move(
206 : s_,
207 9 : s_ + sn,
208 : po - sn);
209 : // move [id_path, id_query) left
210 9 : auto qo = impl_.offset(id_query);
211 9 : op.move(
212 9 : s_ + po - sn,
213 9 : s_ + po,
214 : qo - po);
215 : // move [id_query, id_end) left
216 9 : op.move(
217 9 : s_ + qo - sn + 2 * cn,
218 9 : s_ + qo,
219 9 : impl_.offset(id_end) - qo);
220 :
221 : // adjust part offsets.
222 : // (po and qo are invalidated)
223 9 : if (need_resize)
224 : {
225 1 : impl_.adjust_left(id_user, id_end, sn);
226 : }
227 : else
228 : {
229 8 : impl_.adjust_left(id_user, id_path, sn);
230 8 : impl_.adjust_left(id_query, id_end, sn - 2 * cn);
231 : }
232 9 : if (encode_colon)
233 : {
234 : // move the 2nd, 3rd, ... segments
235 9 : auto begin = s_ + impl_.offset(id_path);
236 9 : auto it = begin;
237 9 : auto end = begin + pn;
238 46 : while (*it != '/' &&
239 : it != end)
240 37 : ++it;
241 : // we don't need op here because this is
242 : // an internal operation
243 9 : std::memmove(it + (2 * cn), it, end - it);
244 :
245 : // move 1st segment
246 9 : auto src = s_ + impl_.offset(id_path) + pn;
247 9 : auto dest = s_ + impl_.offset(id_query);
248 9 : src -= end - it;
249 9 : dest -= end - it;
250 9 : pn -= end - it;
251 28 : do {
252 37 : --src;
253 37 : --dest;
254 37 : if (*src != ':')
255 : {
256 25 : *dest = *src;
257 : }
258 : else
259 : {
260 : // use uppercase as required by
261 : // syntax-based normalization
262 12 : *dest-- = 'A';
263 12 : *dest-- = '3';
264 12 : *dest = '%';
265 : }
266 37 : --pn;
267 37 : } while (pn);
268 : }
269 9 : s_[size()] = '\0';
270 9 : impl_.scheme_ = urls::scheme::none;
271 9 : return *this;
272 : }
273 :
274 : //------------------------------------------------
275 : //
276 : // Authority
277 : //
278 : //------------------------------------------------
279 :
280 : url_base&
281 112 : url_base::
282 : set_encoded_authority(
283 : pct_string_view s)
284 : {
285 224 : op_t op(*this, &detail::ref(s));
286 113 : authority_view a = grammar::parse(
287 : s, authority_rule
288 113 : ).value(BOOST_URL_POS);
289 111 : auto n = s.size() + 2;
290 : auto const need_slash =
291 133 : ! is_path_absolute() &&
292 22 : impl_.len(id_path) > 0;
293 111 : if(need_slash)
294 2 : ++n;
295 111 : auto dest = resize_impl(
296 : id_user, id_path, n, op);
297 111 : dest[0] = '/';
298 111 : dest[1] = '/';
299 111 : std::memcpy(dest + 2,
300 111 : s.data(), s.size());
301 111 : if(need_slash)
302 2 : dest[n - 1] = '/';
303 111 : impl_.apply_authority(a);
304 111 : if(need_slash)
305 2 : impl_.adjust_right(
306 : id_query, id_end, 1);
307 222 : return *this;
308 : }
309 :
310 : url_base&
311 57 : url_base::
312 : remove_authority()
313 : {
314 57 : if(! has_authority())
315 30 : return *this;
316 :
317 27 : op_t op(*this);
318 27 : auto path = impl_.get(id_path);
319 27 : bool const need_dot = path.starts_with("//");
320 27 : if(need_dot)
321 : {
322 : // prepend "/.", can't throw
323 4 : auto p = resize_impl(
324 : id_user, id_path, 2, op);
325 4 : p[0] = '/';
326 4 : p[1] = '.';
327 4 : impl_.split(id_user, 0);
328 4 : impl_.split(id_pass, 0);
329 4 : impl_.split(id_host, 0);
330 4 : impl_.split(id_port, 0);
331 : }
332 : else
333 : {
334 23 : resize_impl(
335 : id_user, id_path, 0, op);
336 : }
337 27 : impl_.host_type_ =
338 : urls::host_type::none;
339 27 : return *this;
340 : }
341 :
342 : //------------------------------------------------
343 : //
344 : // Userinfo
345 : //
346 : //------------------------------------------------
347 :
348 : url_base&
349 47 : url_base::
350 : set_userinfo(
351 : core::string_view s)
352 : {
353 47 : op_t op(*this, &s);
354 47 : encoding_opts opt;
355 47 : auto const n = encoded_size(
356 : s, detail::userinfo_chars, opt);
357 47 : auto dest = set_userinfo_impl(n, op);
358 47 : encode(
359 : dest,
360 : n,
361 : s,
362 : detail::userinfo_chars,
363 : opt);
364 47 : auto const pos = impl_.get(
365 : id_user, id_host
366 47 : ).find_first_of(':');
367 47 : if(pos != core::string_view::npos)
368 : {
369 9 : impl_.split(id_user, pos);
370 : // find ':' in plain string
371 : auto const pos2 =
372 9 : s.find_first_of(':');
373 9 : impl_.decoded_[id_user] =
374 9 : pos2 - 1;
375 9 : impl_.decoded_[id_pass] =
376 9 : s.size() - pos2;
377 : }
378 : else
379 : {
380 38 : impl_.decoded_[id_user] = s.size();
381 38 : impl_.decoded_[id_pass] = 0;
382 : }
383 94 : return *this;
384 : }
385 :
386 : url_base&
387 52 : url_base::
388 : set_encoded_userinfo(
389 : pct_string_view s)
390 : {
391 52 : op_t op(*this, &detail::ref(s));
392 52 : encoding_opts opt;
393 52 : auto const pos = s.find_first_of(':');
394 52 : if(pos != core::string_view::npos)
395 : {
396 : // user:pass
397 7 : auto const s0 = s.substr(0, pos);
398 7 : auto const s1 = s.substr(pos + 1);
399 : auto const n0 =
400 7 : detail::re_encoded_size_unsafe(
401 : s0,
402 : detail::user_chars,
403 : opt);
404 : auto const n1 =
405 7 : detail::re_encoded_size_unsafe(s1,
406 : detail::password_chars,
407 : opt);
408 : auto dest =
409 7 : set_userinfo_impl(n0 + n1 + 1, op);
410 7 : impl_.decoded_[id_user] =
411 7 : detail::re_encode_unsafe(
412 : dest,
413 7 : dest + n0,
414 : s0,
415 : detail::user_chars,
416 : opt);
417 7 : *dest++ = ':';
418 7 : impl_.decoded_[id_pass] =
419 7 : detail::re_encode_unsafe(
420 : dest,
421 7 : dest + n1,
422 : s1,
423 : detail::password_chars,
424 : opt);
425 7 : impl_.split(id_user, 2 + n0);
426 : }
427 : else
428 : {
429 : // user
430 : auto const n =
431 45 : detail::re_encoded_size_unsafe(
432 : s, detail::user_chars, opt);
433 45 : auto dest = set_userinfo_impl(n, op);
434 45 : impl_.decoded_[id_user] =
435 45 : detail::re_encode_unsafe(
436 : dest,
437 45 : dest + n,
438 : s,
439 : detail::user_chars,
440 : opt);
441 45 : impl_.split(id_user, 2 + n);
442 45 : impl_.decoded_[id_pass] = 0;
443 : }
444 104 : return *this;
445 : }
446 :
447 : url_base&
448 22 : url_base::
449 : remove_userinfo() noexcept
450 : {
451 22 : if(impl_.len(id_pass) == 0)
452 6 : return *this; // no userinfo
453 :
454 16 : op_t op(*this);
455 : // keep authority '//'
456 16 : resize_impl(
457 : id_user, id_host, 2, op);
458 16 : impl_.decoded_[id_user] = 0;
459 16 : impl_.decoded_[id_pass] = 0;
460 16 : return *this;
461 : }
462 :
463 : //------------------------------------------------
464 :
465 : url_base&
466 50 : url_base::
467 : set_user(core::string_view s)
468 : {
469 50 : op_t op(*this, &s);
470 50 : encoding_opts opt;
471 50 : auto const n = encoded_size(
472 : s, detail::user_chars, opt);
473 50 : auto dest = set_user_impl(n, op);
474 50 : encode_unsafe(
475 : dest,
476 : n,
477 : s,
478 : detail::user_chars,
479 : opt);
480 50 : impl_.decoded_[id_user] = s.size();
481 100 : return *this;
482 : }
483 :
484 : url_base&
485 43 : url_base::
486 : set_encoded_user(
487 : pct_string_view s)
488 : {
489 43 : op_t op(*this, &detail::ref(s));
490 43 : encoding_opts opt;
491 : auto const n =
492 43 : detail::re_encoded_size_unsafe(
493 : s, detail::user_chars, opt);
494 43 : auto dest = set_user_impl(n, op);
495 43 : impl_.decoded_[id_user] =
496 43 : detail::re_encode_unsafe(
497 : dest,
498 43 : dest + n,
499 : s,
500 : detail::user_chars,
501 : opt);
502 43 : BOOST_ASSERT(
503 : impl_.decoded_[id_user] ==
504 : s.decoded_size());
505 86 : return *this;
506 : }
507 :
508 : //------------------------------------------------
509 :
510 : url_base&
511 37 : url_base::
512 : set_password(core::string_view s)
513 : {
514 37 : op_t op(*this, &s);
515 37 : encoding_opts opt;
516 37 : auto const n = encoded_size(
517 : s, detail::password_chars, opt);
518 37 : auto dest = set_password_impl(n, op);
519 37 : encode_unsafe(
520 : dest,
521 : n,
522 : s,
523 : detail::password_chars,
524 : opt);
525 37 : impl_.decoded_[id_pass] = s.size();
526 74 : return *this;
527 : }
528 :
529 : url_base&
530 39 : url_base::
531 : set_encoded_password(
532 : pct_string_view s)
533 : {
534 39 : op_t op(*this, &detail::ref(s));
535 39 : encoding_opts opt;
536 : auto const n =
537 39 : detail::re_encoded_size_unsafe(
538 : s,
539 : detail::password_chars,
540 : opt);
541 39 : auto dest = set_password_impl(n, op);
542 39 : impl_.decoded_[id_pass] =
543 39 : detail::re_encode_unsafe(
544 : dest,
545 39 : dest + n,
546 : s,
547 : detail::password_chars,
548 : opt);
549 39 : BOOST_ASSERT(
550 : impl_.decoded_[id_pass] ==
551 : s.decoded_size());
552 78 : return *this;
553 : }
554 :
555 : url_base&
556 19 : url_base::
557 : remove_password() noexcept
558 : {
559 19 : auto const n = impl_.len(id_pass);
560 19 : if(n < 2)
561 12 : return *this; // no password
562 :
563 7 : op_t op(*this);
564 : // clear password, retain '@'
565 : auto dest =
566 7 : resize_impl(id_pass, 1, op);
567 7 : dest[0] = '@';
568 7 : impl_.decoded_[id_pass] = 0;
569 7 : return *this;
570 : }
571 :
572 : //------------------------------------------------
573 : //
574 : // Host
575 : //
576 : //------------------------------------------------
577 : /*
578 : host_type host_type() // ipv4, ipv6, ipvfuture, name
579 :
580 : std::string host() // return encoded_host().decode()
581 : pct_string_view encoded_host() // return host part, as-is
582 : std::string host_address() // return encoded_host_address().decode()
583 : pct_string_view encoded_host_address() // ipv4, ipv6, ipvfut, or encoded name, no brackets
584 :
585 : ipv4_address host_ipv4_address() // return ipv4_address or {}
586 : ipv6_address host_ipv6_address() // return ipv6_address or {}
587 : core::string_view host_ipvfuture() // return ipvfuture or {}
588 : std::string host_name() // return decoded name or ""
589 : pct_string_view encoded_host_name() // return encoded host name or ""
590 :
591 : --------------------------------------------------
592 :
593 : set_host( core::string_view ) // set host part from plain text
594 : set_encoded_host( pct_string_view ) // set host part from encoded text
595 : set_host_address( core::string_view ) // set host from ipv4, ipv6, ipvfut, or plain reg-name string
596 : set_encoded_host_address( pct_string_view ) // set host from ipv4, ipv6, ipvfut, or encoded reg-name string
597 :
598 : set_host_ipv4( ipv4_address ) // set ipv4
599 : set_host_ipv6( ipv6_address ) // set ipv6
600 : set_host_ipvfuture( core::string_view ) // set ipvfuture
601 : set_host_name( core::string_view ) // set name from plain
602 : set_encoded_host_name( pct_string_view ) // set name from encoded
603 : */
604 :
605 : // set host part from plain text
606 : url_base&
607 14 : url_base::
608 : set_host(
609 : core::string_view s)
610 : {
611 14 : if( s.size() > 2 &&
612 16 : s.front() == '[' &&
613 2 : s.back() == ']')
614 : {
615 : // IP-literal
616 : {
617 : // IPv6-address
618 : auto rv = parse_ipv6_address(
619 2 : s.substr(1, s.size() - 2));
620 2 : if(rv)
621 1 : return set_host_ipv6(*rv);
622 : }
623 : {
624 : // IPvFuture
625 : auto rv = grammar::parse(
626 1 : s.substr(1, s.size() - 2),
627 1 : detail::ipvfuture_rule);
628 1 : if(rv)
629 1 : return set_host_ipvfuture(rv->str);
630 : }
631 : }
632 12 : else if(s.size() >= 7) // "0.0.0.0"
633 : {
634 : // IPv4-address
635 10 : auto rv = parse_ipv4_address(s);
636 10 : if(rv)
637 4 : return set_host_ipv4(*rv);
638 : }
639 :
640 : // reg-name
641 8 : op_t op(*this, &s);
642 8 : encoding_opts opt;
643 8 : auto const n = encoded_size(
644 : s, detail::host_chars, opt);
645 8 : auto dest = set_host_impl(n, op);
646 8 : encode(
647 : dest,
648 8 : impl_.get(id_path).data() - dest,
649 : s,
650 : detail::host_chars,
651 : opt);
652 8 : impl_.decoded_[id_host] = s.size();
653 8 : impl_.host_type_ =
654 : urls::host_type::name;
655 8 : return *this;
656 : }
657 :
658 : // set host part from encoded text
659 : url_base&
660 115 : url_base::
661 : set_encoded_host(
662 : pct_string_view s)
663 : {
664 115 : if( s.size() > 2 &&
665 131 : s.front() == '[' &&
666 16 : s.back() == ']')
667 : {
668 : // IP-literal
669 : {
670 : // IPv6-address
671 : auto rv = parse_ipv6_address(
672 16 : s.substr(1, s.size() - 2));
673 16 : if(rv)
674 5 : return set_host_ipv6(*rv);
675 : }
676 : {
677 : // IPvFuture
678 : auto rv = grammar::parse(
679 11 : s.substr(1, s.size() - 2),
680 11 : detail::ipvfuture_rule);
681 11 : if(rv)
682 1 : return set_host_ipvfuture(rv->str);
683 : }
684 : }
685 99 : else if(s.size() >= 7) // "0.0.0.0"
686 : {
687 : // IPv4-address
688 55 : auto rv = parse_ipv4_address(s);
689 55 : if(rv)
690 5 : return set_host_ipv4(*rv);
691 : }
692 :
693 : // reg-name
694 104 : op_t op(*this, &detail::ref(s));
695 104 : encoding_opts opt;
696 104 : auto const n = detail::re_encoded_size_unsafe(
697 : s, detail::host_chars, opt);
698 104 : auto dest = set_host_impl(n, op);
699 104 : impl_.decoded_[id_host] =
700 208 : detail::re_encode_unsafe(
701 : dest,
702 104 : impl_.get(id_path).data(),
703 : s,
704 : detail::host_chars,
705 : opt);
706 104 : BOOST_ASSERT(impl_.decoded_[id_host] ==
707 : s.decoded_size());
708 104 : impl_.host_type_ =
709 : urls::host_type::name;
710 104 : return *this;
711 : }
712 :
713 : url_base&
714 9 : url_base::
715 : set_host_address(
716 : core::string_view s)
717 : {
718 : {
719 : // IPv6-address
720 9 : auto rv = parse_ipv6_address(s);
721 9 : if(rv)
722 1 : return set_host_ipv6(*rv);
723 : }
724 : {
725 : // IPvFuture
726 : auto rv = grammar::parse(
727 8 : s, detail::ipvfuture_rule);
728 8 : if(rv)
729 1 : return set_host_ipvfuture(rv->str);
730 : }
731 7 : if(s.size() >= 7) // "0.0.0.0"
732 : {
733 : // IPv4-address
734 5 : auto rv = parse_ipv4_address(s);
735 5 : if(rv)
736 2 : return set_host_ipv4(*rv);
737 : }
738 :
739 : // reg-name
740 5 : op_t op(*this, &s);
741 5 : encoding_opts opt;
742 5 : auto const n = encoded_size(
743 : s, detail::host_chars, opt);
744 5 : auto dest = set_host_impl(n, op);
745 5 : encode(
746 : dest,
747 5 : impl_.get(id_path).data() - dest,
748 : s,
749 : detail::host_chars,
750 : opt);
751 5 : impl_.decoded_[id_host] = s.size();
752 5 : impl_.host_type_ =
753 : urls::host_type::name;
754 5 : return *this;
755 : }
756 :
757 : url_base&
758 7 : url_base::
759 : set_encoded_host_address(
760 : pct_string_view s)
761 : {
762 : {
763 : // IPv6-address
764 7 : auto rv = parse_ipv6_address(s);
765 7 : if(rv)
766 1 : return set_host_ipv6(*rv);
767 : }
768 : {
769 : // IPvFuture
770 : auto rv = grammar::parse(
771 6 : s, detail::ipvfuture_rule);
772 6 : if(rv)
773 1 : return set_host_ipvfuture(rv->str);
774 : }
775 5 : if(s.size() >= 7) // "0.0.0.0"
776 : {
777 : // IPv4-address
778 3 : auto rv = parse_ipv4_address(s);
779 3 : if(rv)
780 1 : return set_host_ipv4(*rv);
781 : }
782 :
783 : // reg-name
784 4 : op_t op(*this, &detail::ref(s));
785 4 : encoding_opts opt;
786 4 : auto const n = detail::re_encoded_size_unsafe(
787 : s, detail::host_chars, opt);
788 4 : auto dest = set_host_impl(n, op);
789 4 : impl_.decoded_[id_host] =
790 8 : detail::re_encode_unsafe(
791 : dest,
792 4 : impl_.get(id_path).data(),
793 : s,
794 : detail::host_chars,
795 : opt);
796 4 : BOOST_ASSERT(impl_.decoded_[id_host] ==
797 : s.decoded_size());
798 4 : impl_.host_type_ =
799 : urls::host_type::name;
800 4 : return *this;
801 : }
802 :
803 : url_base&
804 16 : url_base::
805 : set_host_ipv4(
806 : ipv4_address const& addr)
807 : {
808 16 : op_t op(*this);
809 : char buf[urls::ipv4_address::max_str_len];
810 16 : auto s = addr.to_buffer(buf, sizeof(buf));
811 16 : auto dest = set_host_impl(s.size(), op);
812 16 : std::memcpy(dest, s.data(), s.size());
813 16 : impl_.decoded_[id_host] = impl_.len(id_host);
814 16 : impl_.host_type_ = urls::host_type::ipv4;
815 16 : auto bytes = addr.to_bytes();
816 16 : std::memcpy(
817 16 : impl_.ip_addr_,
818 16 : bytes.data(),
819 : bytes.size());
820 32 : return *this;
821 : }
822 :
823 : url_base&
824 10 : url_base::
825 : set_host_ipv6(
826 : ipv6_address const& addr)
827 : {
828 10 : op_t op(*this);
829 : char buf[2 +
830 : urls::ipv6_address::max_str_len];
831 : auto s = addr.to_buffer(
832 10 : buf + 1, sizeof(buf) - 2);
833 10 : buf[0] = '[';
834 10 : buf[s.size() + 1] = ']';
835 10 : auto const n = s.size() + 2;
836 10 : auto dest = set_host_impl(n, op);
837 10 : std::memcpy(dest, buf, n);
838 10 : impl_.decoded_[id_host] = n;
839 10 : impl_.host_type_ = urls::host_type::ipv6;
840 10 : auto bytes = addr.to_bytes();
841 10 : std::memcpy(
842 10 : impl_.ip_addr_,
843 10 : bytes.data(),
844 : bytes.size());
845 20 : return *this;
846 : }
847 :
848 : url_base&
849 7 : url_base::
850 : set_host_ipvfuture(
851 : core::string_view s)
852 : {
853 8 : op_t op(*this, &s);
854 : // validate
855 1 : grammar::parse(s,
856 : detail::ipvfuture_rule
857 7 : ).value(BOOST_URL_POS);
858 6 : auto dest = set_host_impl(
859 6 : s.size() + 2, op);
860 6 : *dest++ = '[';
861 6 : dest += s.copy(dest, s.size());
862 6 : *dest = ']';
863 6 : impl_.host_type_ =
864 : urls::host_type::ipvfuture;
865 6 : impl_.decoded_[id_host] = s.size() + 2;
866 12 : return *this;
867 : }
868 :
869 : url_base&
870 4 : url_base::
871 : set_host_name(
872 : core::string_view s)
873 : {
874 4 : bool is_ipv4 = false;
875 4 : if(s.size() >= 7) // "0.0.0.0"
876 : {
877 : // IPv4-address
878 3 : if(parse_ipv4_address(s).has_value())
879 1 : is_ipv4 = true;
880 : }
881 4 : auto allowed = detail::host_chars;
882 4 : if(is_ipv4)
883 1 : allowed = allowed - '.';
884 :
885 4 : op_t op(*this, &s);
886 4 : encoding_opts opt;
887 4 : auto const n = encoded_size(
888 : s, allowed, opt);
889 4 : auto dest = set_host_impl(n, op);
890 4 : encode_unsafe(
891 : dest,
892 : n,
893 : s,
894 : allowed,
895 : opt);
896 4 : impl_.host_type_ =
897 : urls::host_type::name;
898 4 : impl_.decoded_[id_host] = s.size();
899 8 : return *this;
900 : }
901 :
902 : url_base&
903 4 : url_base::
904 : set_encoded_host_name(
905 : pct_string_view s)
906 : {
907 4 : bool is_ipv4 = false;
908 4 : if(s.size() >= 7) // "0.0.0.0"
909 : {
910 : // IPv4-address
911 3 : if(parse_ipv4_address(s).has_value())
912 1 : is_ipv4 = true;
913 : }
914 4 : auto allowed = detail::host_chars;
915 4 : if(is_ipv4)
916 1 : allowed = allowed - '.';
917 :
918 4 : op_t op(*this, &detail::ref(s));
919 4 : encoding_opts opt;
920 4 : auto const n = detail::re_encoded_size_unsafe(
921 : s, allowed, opt);
922 4 : auto dest = set_host_impl(n, op);
923 4 : impl_.decoded_[id_host] =
924 4 : detail::re_encode_unsafe(
925 : dest,
926 4 : dest + n,
927 : s,
928 : allowed,
929 : opt);
930 4 : BOOST_ASSERT(
931 : impl_.decoded_[id_host] ==
932 : s.decoded_size());
933 4 : impl_.host_type_ =
934 : urls::host_type::name;
935 8 : return *this;
936 : }
937 :
938 : //------------------------------------------------
939 :
940 : url_base&
941 23 : url_base::
942 : set_port_number(
943 : std::uint16_t n)
944 : {
945 23 : op_t op(*this);
946 : auto s =
947 23 : detail::make_printed(n);
948 23 : auto dest = set_port_impl(
949 23 : s.string().size(), op);
950 23 : std::memcpy(
951 23 : dest, s.string().data(),
952 23 : s.string().size());
953 23 : impl_.port_number_ = n;
954 46 : return *this;
955 : }
956 :
957 : url_base&
958 90 : url_base::
959 : set_port(
960 : core::string_view s)
961 : {
962 109 : op_t op(*this, &s);
963 19 : auto t = grammar::parse(s,
964 19 : detail::port_rule{}
965 90 : ).value(BOOST_URL_POS);
966 : auto dest =
967 71 : set_port_impl(t.str.size(), op);
968 71 : std::memcpy(dest,
969 71 : t.str.data(), t.str.size());
970 71 : if(t.has_number)
971 35 : impl_.port_number_ = t.number;
972 : else
973 36 : impl_.port_number_ = 0;
974 142 : return *this;
975 : }
976 :
977 : url_base&
978 25 : url_base::
979 : remove_port() noexcept
980 : {
981 25 : op_t op(*this);
982 25 : resize_impl(id_port, 0, op);
983 25 : impl_.port_number_ = 0;
984 25 : return *this;
985 : }
986 :
987 : //------------------------------------------------
988 : //
989 : // Compound Fields
990 : //
991 : //------------------------------------------------
992 :
993 : url_base&
994 14 : url_base::
995 : remove_origin()
996 : {
997 : // these two calls perform 2 memmoves instead of 1
998 14 : remove_authority();
999 14 : remove_scheme();
1000 14 : return *this;
1001 : }
1002 :
1003 : //------------------------------------------------
1004 : //
1005 : // Path
1006 : //
1007 : //------------------------------------------------
1008 :
1009 : bool
1010 50 : url_base::
1011 : set_path_absolute(
1012 : bool absolute)
1013 : {
1014 100 : op_t op(*this);
1015 :
1016 : // check if path empty
1017 50 : if(impl_.len(id_path) == 0)
1018 : {
1019 38 : if(! absolute)
1020 : {
1021 : // already not absolute
1022 32 : return true;
1023 : }
1024 :
1025 : // add '/'
1026 6 : auto dest = resize_impl(
1027 : id_path, 1, op);
1028 6 : *dest = '/';
1029 6 : ++impl_.decoded_[id_path];
1030 6 : return true;
1031 : }
1032 :
1033 : // check if path absolute
1034 12 : if(s_[impl_.offset(id_path)] == '/')
1035 : {
1036 9 : if(absolute)
1037 : {
1038 : // already absolute
1039 2 : return true;
1040 : }
1041 :
1042 11 : if( has_authority() &&
1043 4 : impl_.len(id_path) > 1)
1044 : {
1045 : // can't do it, paths are always
1046 : // absolute when authority present!
1047 2 : return false;
1048 : }
1049 :
1050 5 : auto p = encoded_path();
1051 5 : auto pos = p.find_first_of(":/", 1);
1052 6 : if (pos != core::string_view::npos &&
1053 1 : p[pos] == ':')
1054 : {
1055 : // prepend with .
1056 1 : auto n = impl_.len(id_path);
1057 1 : resize_impl(id_path, n + 1, op);
1058 1 : std::memmove(
1059 2 : s_ + impl_.offset(id_path) + 1,
1060 1 : s_ + impl_.offset(id_path), n);
1061 1 : *(s_ + impl_.offset(id_path)) = '.';
1062 1 : ++impl_.decoded_[id_path];
1063 1 : return true;
1064 : }
1065 :
1066 : // remove '/'
1067 4 : auto n = impl_.len(id_port);
1068 4 : impl_.split(id_port, n + 1);
1069 4 : resize_impl(id_port, n, op);
1070 4 : --impl_.decoded_[id_path];
1071 4 : return true;
1072 : }
1073 :
1074 3 : if(! absolute)
1075 : {
1076 : // already not absolute
1077 1 : return true;
1078 : }
1079 :
1080 : // add '/'
1081 2 : auto n = impl_.len(id_port);
1082 2 : auto dest = resize_impl(
1083 2 : id_port, n + 1, op) + n;
1084 2 : impl_.split(id_port, n);
1085 2 : *dest = '/';
1086 2 : ++impl_.decoded_[id_path];
1087 2 : return true;
1088 : }
1089 :
1090 : url_base&
1091 25 : url_base::
1092 : set_path(
1093 : core::string_view s)
1094 : {
1095 50 : op_t op(*this, &s);
1096 25 : encoding_opts opt;
1097 :
1098 : //------------------------------------------------
1099 : //
1100 : // Calculate encoded size
1101 : //
1102 : // - "/"s are not encoded
1103 : // - "%2F"s are not encoded
1104 : //
1105 : // - reserved path chars are re-encoded
1106 : // - colons in first segment might need to be re-encoded
1107 : // - the path might need to receive a prefix
1108 25 : auto const n = encoded_size(
1109 : s, detail::path_chars, opt);
1110 25 : std::size_t n_reencode_colons = 0;
1111 25 : core::string_view first_seg;
1112 25 : if (!has_scheme() &&
1113 40 : !has_authority() &&
1114 15 : !s.starts_with('/'))
1115 : {
1116 : // the first segment with unencoded colons would look
1117 : // like the scheme
1118 6 : first_seg = detail::to_sv(s);
1119 6 : std::size_t p = s.find('/');
1120 6 : if (p != core::string_view::npos)
1121 2 : first_seg = s.substr(0, p);
1122 6 : n_reencode_colons = std::count(
1123 12 : first_seg.begin(), first_seg.end(), ':');
1124 : }
1125 : // the authority can only be followed by an empty or relative path
1126 : // if we have an authority and the path is a non-empty relative path, we
1127 : // add the "/" prefix to make it valid.
1128 : bool make_absolute =
1129 25 : has_authority() &&
1130 30 : !s.starts_with('/') &&
1131 5 : !s.empty();
1132 : // a path starting with "//" might look like the authority.
1133 : // we add a "/." prefix to prevent that
1134 : bool add_dot_segment =
1135 47 : !make_absolute &&
1136 22 : s.starts_with("//");
1137 :
1138 : //------------------------------------------------
1139 : //
1140 : // Re-encode data
1141 : //
1142 50 : auto dest = set_path_impl(
1143 25 : n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
1144 25 : impl_.decoded_[id_path] = 0;
1145 25 : if (!dest)
1146 : {
1147 3 : impl_.nseg_ = 0;
1148 3 : return *this;
1149 : }
1150 22 : if (make_absolute)
1151 : {
1152 3 : *dest++ = '/';
1153 3 : impl_.decoded_[id_path] += 1;
1154 : }
1155 19 : else if (add_dot_segment)
1156 : {
1157 1 : *dest++ = '/';
1158 1 : *dest++ = '.';
1159 1 : impl_.decoded_[id_path] += 2;
1160 : }
1161 44 : dest += encode_unsafe(
1162 : dest,
1163 22 : impl_.get(id_query).data() - dest,
1164 : first_seg,
1165 22 : detail::segment_chars - ':',
1166 : opt);
1167 22 : dest += encode_unsafe(
1168 : dest,
1169 22 : impl_.get(id_query).data() - dest,
1170 : s.substr(first_seg.size()),
1171 : detail::path_chars,
1172 : opt);
1173 22 : impl_.decoded_[id_path] += s.size();
1174 22 : BOOST_ASSERT(!dest || dest == impl_.get(id_query).data());
1175 22 : BOOST_ASSERT(
1176 : impl_.decoded_[id_path] ==
1177 : s.size() + make_absolute + 2 * add_dot_segment);
1178 :
1179 : //------------------------------------------------
1180 : //
1181 : // Update path parameters
1182 : //
1183 : // get the encoded_path with the replacements we applied
1184 22 : if (s == "/")
1185 : {
1186 : // "/" maps to sequence {}
1187 3 : impl_.nseg_ = 0;
1188 : }
1189 19 : else if (!s.empty())
1190 : {
1191 15 : if (s.starts_with("/./"))
1192 1 : s = s.substr(2);
1193 : // count segments as number of '/'s + 1
1194 15 : impl_.nseg_ = std::count(
1195 30 : s.begin() + 1, s.end(), '/') + 1;
1196 : }
1197 : else
1198 : {
1199 : // an empty relative path maps to sequence {}
1200 4 : impl_.nseg_ = 0;
1201 : }
1202 :
1203 22 : check_invariants();
1204 22 : return *this;
1205 : }
1206 :
1207 : url_base&
1208 163 : url_base::
1209 : set_encoded_path(
1210 : pct_string_view s)
1211 : {
1212 326 : op_t op(*this, &detail::ref(s));
1213 163 : encoding_opts opt;
1214 :
1215 : //------------------------------------------------
1216 : //
1217 : // Calculate re-encoded output size
1218 : //
1219 : // - reserved path chars are re-encoded
1220 : // - colons in first segment might need to be re-encoded
1221 : // - the path might need to receive a prefix
1222 163 : auto const n = detail::re_encoded_size_unsafe(
1223 : s, detail::path_chars, opt);
1224 163 : std::size_t n_reencode_colons = 0;
1225 163 : core::string_view first_seg;
1226 163 : if (!has_scheme() &&
1227 180 : !has_authority() &&
1228 17 : !s.starts_with('/'))
1229 : {
1230 : // the first segment with unencoded colons would look
1231 : // like the scheme
1232 10 : first_seg = detail::to_sv(s);
1233 10 : std::size_t p = s.find('/');
1234 10 : if (p != core::string_view::npos)
1235 5 : first_seg = s.substr(0, p);
1236 10 : n_reencode_colons = std::count(
1237 20 : first_seg.begin(), first_seg.end(), ':');
1238 : }
1239 : // the authority can only be followed by an empty or relative path
1240 : // if we have an authority and the path is a non-empty relative path, we
1241 : // add the "/" prefix to make it valid.
1242 : bool make_absolute =
1243 163 : has_authority() &&
1244 211 : !s.starts_with('/') &&
1245 48 : !s.empty();
1246 : // a path starting with "//" might look like the authority
1247 : // we add a "/." prefix to prevent that
1248 : bool add_dot_segment =
1249 313 : !make_absolute &&
1250 200 : !has_authority() &&
1251 37 : s.starts_with("//");
1252 :
1253 : //------------------------------------------------
1254 : //
1255 : // Re-encode data
1256 : //
1257 326 : auto dest = set_path_impl(
1258 163 : n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
1259 163 : impl_.decoded_[id_path] = 0;
1260 163 : if (!dest)
1261 : {
1262 1 : impl_.nseg_ = 0;
1263 1 : return *this;
1264 : }
1265 162 : if (make_absolute)
1266 : {
1267 13 : *dest++ = '/';
1268 13 : impl_.decoded_[id_path] += 1;
1269 : }
1270 149 : else if (add_dot_segment)
1271 : {
1272 3 : *dest++ = '/';
1273 3 : *dest++ = '.';
1274 3 : impl_.decoded_[id_path] += 2;
1275 : }
1276 162 : impl_.decoded_[id_path] +=
1277 162 : detail::re_encode_unsafe(
1278 : dest,
1279 162 : impl_.get(id_query).data(),
1280 : first_seg,
1281 162 : detail::segment_chars - ':',
1282 : opt);
1283 162 : impl_.decoded_[id_path] +=
1284 324 : detail::re_encode_unsafe(
1285 : dest,
1286 162 : impl_.get(id_query).data(),
1287 : s.substr(first_seg.size()),
1288 : detail::path_chars,
1289 : opt);
1290 162 : BOOST_ASSERT(dest == impl_.get(id_query).data());
1291 162 : BOOST_ASSERT(
1292 : impl_.decoded_[id_path] ==
1293 : s.decoded_size() + make_absolute + 2 * add_dot_segment);
1294 :
1295 : //------------------------------------------------
1296 : //
1297 : // Update path parameters
1298 : //
1299 : // get the encoded_path with the replacements we applied
1300 162 : if (s == "/")
1301 : {
1302 : // "/" maps to sequence {}
1303 16 : impl_.nseg_ = 0;
1304 : }
1305 146 : else if (!s.empty())
1306 : {
1307 109 : if (s.starts_with("/./"))
1308 7 : s = s.substr(2);
1309 : // count segments as number of '/'s + 1
1310 109 : impl_.nseg_ = std::count(
1311 218 : s.begin() + 1, s.end(), '/') + 1;
1312 : }
1313 : else
1314 : {
1315 : // an empty relative path maps to sequence {}
1316 37 : impl_.nseg_ = 0;
1317 : }
1318 :
1319 162 : check_invariants();
1320 162 : return *this;
1321 : }
1322 :
1323 : segments_ref
1324 266 : url_base::
1325 : segments() noexcept
1326 : {
1327 266 : return {*this};
1328 : }
1329 :
1330 : segments_encoded_ref
1331 462 : url_base::
1332 : encoded_segments() noexcept
1333 : {
1334 462 : return {*this};
1335 : }
1336 :
1337 : //------------------------------------------------
1338 : //
1339 : // Query
1340 : //
1341 : //------------------------------------------------
1342 :
1343 : url_base&
1344 10 : url_base::
1345 : set_query(
1346 : core::string_view s)
1347 : {
1348 10 : edit_params(
1349 10 : detail::params_iter_impl(impl_),
1350 10 : detail::params_iter_impl(impl_, 0),
1351 20 : detail::query_iter(s, true));
1352 10 : return *this;
1353 : }
1354 :
1355 : url_base&
1356 47 : url_base::
1357 : set_encoded_query(
1358 : pct_string_view s)
1359 : {
1360 47 : op_t op(*this);
1361 47 : encoding_opts opt;
1362 47 : std::size_t n = 0; // encoded size
1363 47 : std::size_t nparam = 1; // param count
1364 47 : auto const end = s.end();
1365 47 : auto p = s.begin();
1366 :
1367 : // measure
1368 195 : while(p != end)
1369 : {
1370 148 : if(*p == '&')
1371 : {
1372 3 : ++p;
1373 3 : ++n;
1374 3 : ++nparam;
1375 : }
1376 145 : else if(*p != '%')
1377 : {
1378 142 : if(detail::query_chars(*p))
1379 139 : n += 1; // allowed
1380 : else
1381 3 : n += 3; // escaped
1382 142 : ++p;
1383 : }
1384 : else
1385 : {
1386 : // escape
1387 3 : n += 3;
1388 3 : p += 3;
1389 : }
1390 : }
1391 :
1392 : // resize
1393 47 : auto dest = resize_impl(
1394 47 : id_query, n + 1, op);
1395 47 : *dest++ = '?';
1396 :
1397 : // encode
1398 47 : impl_.decoded_[id_query] =
1399 47 : detail::re_encode_unsafe(
1400 : dest,
1401 47 : dest + n,
1402 : s,
1403 : detail::query_chars,
1404 : opt);
1405 47 : BOOST_ASSERT(
1406 : impl_.decoded_[id_query] ==
1407 : s.decoded_size());
1408 47 : impl_.nparam_ = nparam;
1409 94 : return *this;
1410 : }
1411 :
1412 : params_ref
1413 92 : url_base::
1414 : params() noexcept
1415 : {
1416 : return params_ref(
1417 : *this,
1418 : encoding_opts{
1419 92 : true, false, false});
1420 : }
1421 :
1422 : params_ref
1423 1 : url_base::
1424 : params(encoding_opts opt) noexcept
1425 : {
1426 1 : return params_ref(*this, opt);
1427 : }
1428 :
1429 : params_encoded_ref
1430 77 : url_base::
1431 : encoded_params() noexcept
1432 : {
1433 77 : return {*this};
1434 : }
1435 :
1436 : url_base&
1437 1 : url_base::
1438 : set_params( std::initializer_list<param_view> ps ) noexcept
1439 : {
1440 1 : params().assign(ps);
1441 1 : return *this;
1442 : }
1443 :
1444 : url_base&
1445 1 : url_base::
1446 : set_encoded_params( std::initializer_list< param_pct_view > ps ) noexcept
1447 : {
1448 1 : encoded_params().assign(ps);
1449 1 : return *this;
1450 : }
1451 :
1452 : url_base&
1453 222 : url_base::
1454 : remove_query() noexcept
1455 : {
1456 222 : op_t op(*this);
1457 222 : resize_impl(id_query, 0, op);
1458 222 : impl_.nparam_ = 0;
1459 222 : impl_.decoded_[id_query] = 0;
1460 222 : return *this;
1461 : }
1462 :
1463 : //------------------------------------------------
1464 : //
1465 : // Fragment
1466 : //
1467 : //------------------------------------------------
1468 :
1469 : url_base&
1470 215 : url_base::
1471 : remove_fragment() noexcept
1472 : {
1473 215 : op_t op(*this);
1474 215 : resize_impl(id_frag, 0, op);
1475 215 : impl_.decoded_[id_frag] = 0;
1476 215 : return *this;
1477 : }
1478 :
1479 : url_base&
1480 7 : url_base::
1481 : set_fragment(core::string_view s)
1482 : {
1483 7 : op_t op(*this, &s);
1484 7 : encoding_opts opt;
1485 7 : auto const n = encoded_size(
1486 : s,
1487 : detail::fragment_chars,
1488 : opt);
1489 7 : auto dest = resize_impl(
1490 : id_frag, n + 1, op);
1491 7 : *dest++ = '#';
1492 7 : encode_unsafe(
1493 : dest,
1494 : n,
1495 : s,
1496 : detail::fragment_chars,
1497 : opt);
1498 7 : impl_.decoded_[id_frag] = s.size();
1499 14 : return *this;
1500 : }
1501 :
1502 : url_base&
1503 57 : url_base::
1504 : set_encoded_fragment(
1505 : pct_string_view s)
1506 : {
1507 57 : op_t op(*this, &detail::ref(s));
1508 57 : encoding_opts opt;
1509 : auto const n =
1510 57 : detail::re_encoded_size_unsafe(
1511 : s,
1512 : detail::fragment_chars,
1513 : opt);
1514 57 : auto dest = resize_impl(
1515 57 : id_frag, n + 1, op);
1516 57 : *dest++ = '#';
1517 57 : impl_.decoded_[id_frag] =
1518 57 : detail::re_encode_unsafe(
1519 : dest,
1520 57 : dest + n,
1521 : s,
1522 : detail::fragment_chars,
1523 : opt);
1524 57 : BOOST_ASSERT(
1525 : impl_.decoded_[id_frag] ==
1526 : s.decoded_size());
1527 114 : return *this;
1528 : }
1529 :
1530 : //------------------------------------------------
1531 : //
1532 : // Resolution
1533 : //
1534 : //------------------------------------------------
1535 :
1536 : system::result<void>
1537 462 : url_base::
1538 : resolve(
1539 : url_view_base const& ref)
1540 : {
1541 465 : if (this == &ref &&
1542 3 : has_scheme())
1543 : {
1544 2 : normalize_path();
1545 2 : return {};
1546 : }
1547 :
1548 460 : if(! has_scheme())
1549 : {
1550 2 : BOOST_URL_RETURN_EC(error::not_a_base);
1551 : }
1552 :
1553 916 : op_t op(*this);
1554 :
1555 : //
1556 : // 5.2.2. Transform References
1557 : // https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.2
1558 : //
1559 :
1560 719 : if( ref.has_scheme() &&
1561 261 : ref.scheme() != scheme())
1562 : {
1563 198 : reserve_impl(ref.size(), op);
1564 198 : copy(ref);
1565 198 : normalize_path();
1566 198 : return {};
1567 : }
1568 260 : if(ref.has_authority())
1569 : {
1570 70 : reserve_impl(
1571 70 : impl_.offset(id_user) + ref.size(), op);
1572 : set_encoded_authority(
1573 70 : ref.encoded_authority());
1574 : set_encoded_path(
1575 70 : ref.encoded_path());
1576 70 : if (ref.encoded_path().empty())
1577 31 : set_path_absolute(false);
1578 : else
1579 39 : normalize_path();
1580 70 : if(ref.has_query())
1581 : set_encoded_query(
1582 5 : ref.encoded_query());
1583 : else
1584 65 : remove_query();
1585 70 : if(ref.has_fragment())
1586 : set_encoded_fragment(
1587 5 : ref.encoded_fragment());
1588 : else
1589 65 : remove_fragment();
1590 70 : return {};
1591 : }
1592 190 : if(ref.encoded_path().empty())
1593 : {
1594 32 : reserve_impl(
1595 32 : impl_.offset(id_query) +
1596 32 : ref.size(), op);
1597 32 : normalize_path();
1598 32 : if(ref.has_query())
1599 : {
1600 : set_encoded_query(
1601 10 : ref.encoded_query());
1602 : }
1603 32 : if(ref.has_fragment())
1604 : set_encoded_fragment(
1605 18 : ref.encoded_fragment());
1606 32 : return {};
1607 : }
1608 158 : if(ref.is_path_absolute())
1609 : {
1610 35 : reserve_impl(
1611 35 : impl_.offset(id_path) +
1612 35 : ref.size(), op);
1613 : set_encoded_path(
1614 35 : ref.encoded_path());
1615 35 : normalize_path();
1616 35 : if(ref.has_query())
1617 : set_encoded_query(
1618 3 : ref.encoded_query());
1619 : else
1620 32 : remove_query();
1621 35 : if(ref.has_fragment())
1622 : set_encoded_fragment(
1623 2 : ref.encoded_fragment());
1624 : else
1625 33 : remove_fragment();
1626 35 : return {};
1627 : }
1628 : // General case: ref is relative path
1629 123 : reserve_impl(
1630 123 : impl_.offset(id_query) +
1631 123 : ref.size(), op);
1632 : // 5.2.3. Merge Paths
1633 123 : auto es = encoded_segments();
1634 123 : if(es.size() > 0)
1635 : {
1636 118 : es.pop_back();
1637 : }
1638 123 : es.insert(es.end(),
1639 123 : ref.encoded_segments().begin(),
1640 246 : ref.encoded_segments().end());
1641 123 : normalize_path();
1642 123 : if(ref.has_query())
1643 : set_encoded_query(
1644 10 : ref.encoded_query());
1645 : else
1646 113 : remove_query();
1647 123 : if(ref.has_fragment())
1648 : set_encoded_fragment(
1649 10 : ref.encoded_fragment());
1650 : else
1651 113 : remove_fragment();
1652 123 : return {};
1653 : }
1654 :
1655 : //------------------------------------------------
1656 : //
1657 : // Normalization
1658 : //
1659 : //------------------------------------------------
1660 :
1661 : template <class Charset>
1662 : void
1663 1917 : url_base::
1664 : normalize_octets_impl(
1665 : int id,
1666 : Charset const& allowed,
1667 : op_t& op) noexcept
1668 : {
1669 1917 : char* it = s_ + impl_.offset(id);
1670 1917 : char* end = s_ + impl_.offset(id + 1);
1671 1917 : char d = 0;
1672 1917 : char* dest = it;
1673 10587 : while (it < end)
1674 : {
1675 8670 : if (*it != '%')
1676 : {
1677 8562 : *dest = *it;
1678 8562 : ++it;
1679 8562 : ++dest;
1680 8562 : continue;
1681 : }
1682 108 : BOOST_ASSERT(end - it >= 3);
1683 :
1684 : // decode unreserved octets
1685 108 : d = detail::decode_one(it + 1);
1686 108 : if (allowed(d))
1687 : {
1688 76 : *dest = d;
1689 76 : it += 3;
1690 76 : ++dest;
1691 76 : continue;
1692 : }
1693 :
1694 : // uppercase percent-encoding triplets
1695 32 : *dest++ = '%';
1696 32 : ++it;
1697 32 : *dest++ = grammar::to_upper(*it++);
1698 32 : *dest++ = grammar::to_upper(*it++);
1699 : }
1700 1917 : if (it != dest)
1701 : {
1702 24 : auto diff = it - dest;
1703 24 : auto n = impl_.len(id) - diff;
1704 24 : shrink_impl(id, n, op);
1705 24 : s_[size()] = '\0';
1706 : }
1707 1917 : }
1708 :
1709 : url_base&
1710 38 : url_base::
1711 : normalize_scheme()
1712 : {
1713 38 : to_lower_impl(id_scheme);
1714 38 : return *this;
1715 : }
1716 :
1717 : url_base&
1718 383 : url_base::
1719 : normalize_authority()
1720 : {
1721 383 : op_t op(*this);
1722 :
1723 : // normalize host
1724 383 : if (host_type() == urls::host_type::name)
1725 : {
1726 247 : normalize_octets_impl(
1727 : id_host,
1728 : detail::reg_name_chars, op);
1729 : }
1730 383 : decoded_to_lower_impl(id_host);
1731 :
1732 : // normalize password
1733 383 : normalize_octets_impl(id_pass, detail::password_chars, op);
1734 :
1735 : // normalize user
1736 383 : normalize_octets_impl(id_user, detail::user_chars, op);
1737 383 : return *this;
1738 : }
1739 :
1740 : url_base&
1741 832 : url_base::
1742 : normalize_path()
1743 : {
1744 832 : op_t op(*this);
1745 832 : normalize_octets_impl(id_path, detail::segment_chars, op);
1746 832 : core::string_view p = impl_.get(id_path);
1747 832 : char* p_dest = s_ + impl_.offset(id_path);
1748 832 : char* p_end = s_ + impl_.offset(id_path + 1);
1749 832 : auto pn = p.size();
1750 832 : auto skip_dot = 0;
1751 832 : bool encode_colons = false;
1752 832 : core::string_view first_seg;
1753 :
1754 : //------------------------------------------------
1755 : //
1756 : // Determine unnecessary initial dot segments to skip and
1757 : // if we need to encode colons in the first segment
1758 : //
1759 832 : if (
1760 1096 : !has_authority() &&
1761 264 : p.starts_with("/./"))
1762 : {
1763 : // check if removing the "/./" would result in "//"
1764 : // ex: "/.//", "/././/", "/././/", ...
1765 14 : skip_dot = 2;
1766 15 : while (p.substr(skip_dot, 3).starts_with("/./"))
1767 1 : skip_dot += 2;
1768 14 : if (p.substr(skip_dot).starts_with("//"))
1769 11 : skip_dot = 2;
1770 : else
1771 3 : skip_dot = 0;
1772 : }
1773 818 : else if (
1774 848 : !has_scheme() &&
1775 30 : !has_authority())
1776 : {
1777 30 : if (p.starts_with("./"))
1778 : {
1779 : // check if removing the "./" would result in "//"
1780 : // ex: ".//", "././/", "././/", ...
1781 7 : skip_dot = 1;
1782 10 : while (p.substr(skip_dot, 3).starts_with("/./"))
1783 3 : skip_dot += 2;
1784 7 : if (p.substr(skip_dot).starts_with("//"))
1785 2 : skip_dot = 2;
1786 : else
1787 5 : skip_dot = 0;
1788 :
1789 7 : if ( !skip_dot )
1790 : {
1791 : // check if removing "./"s would leave us
1792 : // a first segment with an ambiguous ":"
1793 5 : first_seg = p.substr(2);
1794 7 : while (first_seg.starts_with("./"))
1795 2 : first_seg = first_seg.substr(2);
1796 5 : auto i = first_seg.find('/');
1797 5 : if (i != core::string_view::npos)
1798 1 : first_seg = first_seg.substr(0, i);
1799 5 : encode_colons = first_seg.contains(':');
1800 : }
1801 : }
1802 : else
1803 : {
1804 : // check if normalize_octets_impl
1805 : // didn't already create a ":"
1806 : // in the first segment
1807 23 : first_seg = p;
1808 23 : auto i = first_seg.find('/');
1809 23 : if (i != core::string_view::npos)
1810 17 : first_seg = p.substr(0, i);
1811 23 : encode_colons = first_seg.contains(':');
1812 : }
1813 : }
1814 :
1815 : //------------------------------------------------
1816 : //
1817 : // Encode colons in the first segment
1818 : //
1819 832 : if (encode_colons)
1820 : {
1821 : // prepend with "./"
1822 : // (resize_impl never throws)
1823 : auto cn =
1824 5 : std::count(
1825 : first_seg.begin(),
1826 : first_seg.end(),
1827 5 : ':');
1828 5 : resize_impl(
1829 5 : id_path, pn + (2 * cn), op);
1830 : // move the 2nd, 3rd, ... segments
1831 5 : auto begin = s_ + impl_.offset(id_path);
1832 5 : auto it = begin;
1833 5 : auto end = begin + pn;
1834 11 : while (core::string_view(it, 2) == "./")
1835 6 : it += 2;
1836 57 : while (*it != '/' &&
1837 : it != end)
1838 52 : ++it;
1839 : // we don't need op here because this is
1840 : // an internal operation
1841 5 : std::memmove(it + (2 * cn), it, end - it);
1842 :
1843 : // move 1st segment
1844 5 : auto src = s_ + impl_.offset(id_path) + pn;
1845 5 : auto dest = s_ + impl_.offset(id_query);
1846 5 : src -= end - it;
1847 5 : dest -= end - it;
1848 5 : pn -= end - it;
1849 59 : do {
1850 64 : --src;
1851 64 : --dest;
1852 64 : if (*src != ':')
1853 : {
1854 57 : *dest = *src;
1855 : }
1856 : else
1857 : {
1858 : // use uppercase as required by
1859 : // syntax-based normalization
1860 7 : *dest-- = 'A';
1861 7 : *dest-- = '3';
1862 7 : *dest = '%';
1863 : }
1864 64 : --pn;
1865 64 : } while (pn);
1866 5 : skip_dot = 0;
1867 5 : p = impl_.get(id_path);
1868 5 : pn = p.size();
1869 5 : p_dest = s_ + impl_.offset(id_path);
1870 5 : p_end = s_ + impl_.offset(id_path + 1);
1871 : }
1872 :
1873 : //------------------------------------------------
1874 : //
1875 : // Remove "." and ".." segments
1876 : //
1877 832 : p.remove_prefix(skip_dot);
1878 832 : p_dest += skip_dot;
1879 832 : auto n = detail::remove_dot_segments(
1880 : p_dest, p_end, p);
1881 :
1882 : //------------------------------------------------
1883 : //
1884 : // Update path parameters
1885 : //
1886 832 : if (n != pn)
1887 : {
1888 134 : BOOST_ASSERT(n < pn);
1889 134 : shrink_impl(id_path, n + skip_dot, op);
1890 134 : p = encoded_path();
1891 134 : if (p == "/")
1892 9 : impl_.nseg_ = 0;
1893 125 : else if (!p.empty())
1894 123 : impl_.nseg_ = std::count(
1895 246 : p.begin() + 1, p.end(), '/') + 1;
1896 : else
1897 2 : impl_.nseg_ = 0;
1898 134 : impl_.decoded_[id_path] =
1899 134 : detail::decode_bytes_unsafe(impl_.get(id_path));
1900 : }
1901 1664 : return *this;
1902 : }
1903 :
1904 : url_base&
1905 36 : url_base::
1906 : normalize_query()
1907 : {
1908 36 : op_t op(*this);
1909 36 : normalize_octets_impl(
1910 : id_query, detail::query_chars, op);
1911 36 : return *this;
1912 : }
1913 :
1914 : url_base&
1915 36 : url_base::
1916 : normalize_fragment()
1917 : {
1918 36 : op_t op(*this);
1919 36 : normalize_octets_impl(
1920 : id_frag, detail::fragment_chars, op);
1921 36 : return *this;
1922 : }
1923 :
1924 : url_base&
1925 36 : url_base::
1926 : normalize()
1927 : {
1928 36 : normalize_fragment();
1929 36 : normalize_query();
1930 36 : normalize_path();
1931 36 : normalize_authority();
1932 36 : normalize_scheme();
1933 36 : return *this;
1934 : }
1935 :
1936 : //------------------------------------------------
1937 : //
1938 : // Implementation
1939 : //
1940 : //------------------------------------------------
1941 :
1942 : void
1943 17757 : url_base::
1944 : check_invariants() const noexcept
1945 : {
1946 17757 : BOOST_ASSERT(pi_);
1947 17757 : BOOST_ASSERT(
1948 : impl_.len(id_scheme) == 0 ||
1949 : impl_.get(id_scheme).ends_with(':'));
1950 17757 : BOOST_ASSERT(
1951 : impl_.len(id_user) == 0 ||
1952 : impl_.get(id_user).starts_with("//"));
1953 17757 : BOOST_ASSERT(
1954 : impl_.len(id_pass) == 0 ||
1955 : impl_.get(id_user).starts_with("//"));
1956 17757 : BOOST_ASSERT(
1957 : impl_.len(id_pass) == 0 ||
1958 : (impl_.len(id_pass) == 1 &&
1959 : impl_.get(id_pass) == "@") ||
1960 : (impl_.len(id_pass) > 1 &&
1961 : impl_.get(id_pass).starts_with(':') &&
1962 : impl_.get(id_pass).ends_with('@')));
1963 17757 : BOOST_ASSERT(
1964 : impl_.len(id_user, id_path) == 0 ||
1965 : impl_.get(id_user).starts_with("//"));
1966 17757 : BOOST_ASSERT(impl_.decoded_[id_path] >=
1967 : ((impl_.len(id_path) + 2) / 3));
1968 17757 : BOOST_ASSERT(
1969 : impl_.len(id_port) == 0 ||
1970 : impl_.get(id_port).starts_with(':'));
1971 17757 : BOOST_ASSERT(
1972 : impl_.len(id_query) == 0 ||
1973 : impl_.get(id_query).starts_with('?'));
1974 17757 : BOOST_ASSERT(
1975 : (impl_.len(id_query) == 0 && impl_.nparam_ == 0) ||
1976 : (impl_.len(id_query) > 0 && impl_.nparam_ > 0));
1977 17757 : BOOST_ASSERT(
1978 : impl_.len(id_frag) == 0 ||
1979 : impl_.get(id_frag).starts_with('#'));
1980 17757 : BOOST_ASSERT(c_str()[size()] == '\0');
1981 17757 : }
1982 :
1983 : char*
1984 1512 : url_base::
1985 : resize_impl(
1986 : int id,
1987 : std::size_t new_size,
1988 : op_t& op)
1989 : {
1990 1512 : return resize_impl(
1991 1512 : id, id + 1, new_size, op);
1992 : }
1993 :
1994 : char*
1995 1781 : url_base::
1996 : resize_impl(
1997 : int first,
1998 : int last,
1999 : std::size_t new_len,
2000 : op_t& op)
2001 : {
2002 1781 : auto const n0 = impl_.len(first, last);
2003 1781 : if(new_len == 0 && n0 == 0)
2004 371 : return s_ + impl_.offset(first);
2005 1410 : if(new_len <= n0)
2006 501 : return shrink_impl(
2007 501 : first, last, new_len, op);
2008 :
2009 : // growing
2010 909 : std::size_t n = new_len - n0;
2011 909 : reserve_impl(size() + n, op);
2012 : auto const pos =
2013 909 : impl_.offset(last);
2014 : // adjust chars
2015 909 : op.move(
2016 909 : s_ + pos + n,
2017 909 : s_ + pos,
2018 909 : impl_.offset(id_end) -
2019 : pos + 1);
2020 : // collapse (first, last)
2021 909 : impl_.collapse(first, last,
2022 909 : impl_.offset(last) + n);
2023 : // shift (last, end) right
2024 909 : impl_.adjust_right(last, id_end, n);
2025 909 : s_[size()] = '\0';
2026 909 : return s_ + impl_.offset(first);
2027 : }
2028 :
2029 : char*
2030 158 : url_base::
2031 : shrink_impl(
2032 : int id,
2033 : std::size_t new_size,
2034 : op_t& op)
2035 : {
2036 158 : return shrink_impl(
2037 158 : id, id + 1, new_size, op);
2038 : }
2039 :
2040 : char*
2041 659 : url_base::
2042 : shrink_impl(
2043 : int first,
2044 : int last,
2045 : std::size_t new_len,
2046 : op_t& op)
2047 : {
2048 : // shrinking
2049 659 : auto const n0 = impl_.len(first, last);
2050 659 : BOOST_ASSERT(new_len <= n0);
2051 659 : std::size_t n = n0 - new_len;
2052 : auto const pos =
2053 659 : impl_.offset(last);
2054 : // adjust chars
2055 659 : op.move(
2056 659 : s_ + pos - n,
2057 659 : s_ + pos,
2058 659 : impl_.offset(
2059 659 : id_end) - pos + 1);
2060 : // collapse (first, last)
2061 659 : impl_.collapse(first, last,
2062 659 : impl_.offset(last) - n);
2063 : // shift (last, end) left
2064 659 : impl_.adjust_left(last, id_end, n);
2065 659 : s_[size()] = '\0';
2066 659 : return s_ + impl_.offset(first);
2067 : }
2068 :
2069 : //------------------------------------------------
2070 :
2071 : void
2072 61 : url_base::
2073 : set_scheme_impl(
2074 : core::string_view s,
2075 : urls::scheme id)
2076 : {
2077 122 : op_t op(*this, &s);
2078 61 : check_invariants();
2079 13 : grammar::parse(
2080 13 : s, detail::scheme_rule()
2081 61 : ).value(BOOST_URL_POS);
2082 48 : auto const n = s.size();
2083 48 : auto const p = impl_.offset(id_path);
2084 :
2085 : // check for "./" prefix
2086 : bool const has_dot =
2087 75 : [this, p]
2088 : {
2089 48 : if(impl_.nseg_ == 0)
2090 30 : return false;
2091 18 : if(first_segment().size() < 2)
2092 9 : return false;
2093 9 : auto const src = s_ + p;
2094 9 : if(src[0] != '.')
2095 6 : return false;
2096 3 : if(src[1] != '/')
2097 0 : return false;
2098 3 : return true;
2099 48 : }();
2100 :
2101 : // Remove "./"
2102 48 : if(has_dot)
2103 : {
2104 : // do this first, for
2105 : // strong exception safety
2106 3 : reserve_impl(
2107 3 : size() + n + 1 - 2, op);
2108 3 : op.move(
2109 3 : s_ + p,
2110 3 : s_ + p + 2,
2111 3 : size() + 1 -
2112 : (p + 2));
2113 3 : impl_.set_size(
2114 : id_path,
2115 3 : impl_.len(id_path) - 2);
2116 3 : s_[size()] = '\0';
2117 : }
2118 :
2119 48 : auto dest = resize_impl(
2120 : id_scheme, n + 1, op);
2121 48 : s.copy(dest, n);
2122 48 : dest[n] = ':';
2123 48 : impl_.scheme_ = id;
2124 48 : check_invariants();
2125 48 : }
2126 :
2127 : char*
2128 101 : url_base::
2129 : set_user_impl(
2130 : std::size_t n,
2131 : op_t& op)
2132 : {
2133 101 : check_invariants();
2134 101 : if(impl_.len(id_pass) != 0)
2135 : {
2136 : // keep "//"
2137 50 : auto dest = resize_impl(
2138 : id_user, 2 + n, op);
2139 50 : check_invariants();
2140 50 : return dest + 2;
2141 : }
2142 : // add authority
2143 : bool const make_absolute =
2144 91 : !is_path_absolute() &&
2145 40 : !impl_.get(id_path).empty();
2146 102 : auto dest = resize_impl(
2147 51 : id_user, 2 + n + 1 + make_absolute, op);
2148 51 : impl_.split(id_user, 2 + n);
2149 51 : dest[0] = '/';
2150 51 : dest[1] = '/';
2151 51 : dest[2 + n] = '@';
2152 51 : if (make_absolute)
2153 : {
2154 4 : impl_.split(id_pass, 1);
2155 4 : impl_.split(id_host, 0);
2156 4 : impl_.split(id_port, 0);
2157 4 : dest[3 + n] = '/';
2158 : }
2159 51 : check_invariants();
2160 51 : return dest + 2;
2161 : }
2162 :
2163 : char*
2164 82 : url_base::
2165 : set_password_impl(
2166 : std::size_t n,
2167 : op_t& op)
2168 : {
2169 82 : check_invariants();
2170 82 : if(impl_.len(id_user) != 0)
2171 : {
2172 : // already have authority
2173 66 : auto const dest = resize_impl(
2174 : id_pass, 1 + n + 1, op);
2175 66 : dest[0] = ':';
2176 66 : dest[n + 1] = '@';
2177 66 : check_invariants();
2178 66 : return dest + 1;
2179 : }
2180 : // add authority
2181 : bool const make_absolute =
2182 25 : !is_path_absolute() &&
2183 9 : !impl_.get(id_path).empty();
2184 : auto const dest =
2185 32 : resize_impl(
2186 : id_user, id_host,
2187 16 : 2 + 1 + n + 1 + make_absolute, op);
2188 16 : impl_.split(id_user, 2);
2189 16 : dest[0] = '/';
2190 16 : dest[1] = '/';
2191 16 : dest[2] = ':';
2192 16 : dest[2 + n + 1] = '@';
2193 16 : if (make_absolute)
2194 : {
2195 2 : impl_.split(id_pass, 2 + n);
2196 2 : impl_.split(id_host, 0);
2197 2 : impl_.split(id_port, 0);
2198 2 : dest[4 + n] = '/';
2199 : }
2200 16 : check_invariants();
2201 16 : return dest + 3;
2202 : }
2203 :
2204 : char*
2205 99 : url_base::
2206 : set_userinfo_impl(
2207 : std::size_t n,
2208 : op_t& op)
2209 : {
2210 : // "//" {dest} "@"
2211 99 : check_invariants();
2212 : bool const make_absolute =
2213 180 : !is_path_absolute() &&
2214 81 : !impl_.get(id_path).empty();
2215 198 : auto dest = resize_impl(
2216 99 : id_user, id_host, n + 3 + make_absolute, op);
2217 99 : impl_.split(id_user, n + 2);
2218 99 : dest[0] = '/';
2219 99 : dest[1] = '/';
2220 99 : dest[n + 2] = '@';
2221 99 : if (make_absolute)
2222 : {
2223 2 : impl_.split(id_pass, 1);
2224 2 : impl_.split(id_host, 0);
2225 2 : impl_.split(id_port, 0);
2226 2 : dest[3 + n] = '/';
2227 : }
2228 99 : check_invariants();
2229 99 : return dest + 2;
2230 : }
2231 :
2232 : char*
2233 206 : url_base::
2234 : set_host_impl(
2235 : std::size_t n,
2236 : op_t& op)
2237 : {
2238 206 : check_invariants();
2239 206 : if(impl_.len(id_user) == 0)
2240 : {
2241 : // add authority
2242 : bool make_absolute =
2243 184 : !is_path_absolute() &&
2244 90 : impl_.len(id_path) != 0;
2245 94 : auto pn = impl_.len(id_path);
2246 188 : auto dest = resize_impl(
2247 94 : id_user, n + 2 + make_absolute, op);
2248 94 : impl_.split(id_user, 2);
2249 94 : impl_.split(id_pass, 0);
2250 94 : impl_.split(id_host, n);
2251 94 : impl_.split(id_port, 0);
2252 94 : impl_.split(id_path, pn + make_absolute);
2253 94 : if (make_absolute)
2254 : {
2255 7 : dest[n + 2] = '/';
2256 7 : ++impl_.decoded_[id_path];
2257 : }
2258 94 : dest[0] = '/';
2259 94 : dest[1] = '/';
2260 94 : check_invariants();
2261 94 : return dest + 2;
2262 : }
2263 : // already have authority
2264 112 : auto const dest = resize_impl(
2265 : id_host, n, op);
2266 112 : check_invariants();
2267 112 : return dest;
2268 : }
2269 :
2270 : char*
2271 107 : url_base::
2272 : set_port_impl(
2273 : std::size_t n,
2274 : op_t& op)
2275 : {
2276 107 : check_invariants();
2277 107 : if(impl_.len(id_user) != 0)
2278 : {
2279 : // authority exists
2280 85 : auto dest = resize_impl(
2281 : id_port, n + 1, op);
2282 85 : dest[0] = ':';
2283 85 : check_invariants();
2284 85 : return dest + 1;
2285 : }
2286 : bool make_absolute =
2287 38 : !is_path_absolute() &&
2288 16 : impl_.len(id_path) != 0;
2289 44 : auto dest = resize_impl(
2290 22 : id_user, 3 + n + make_absolute, op);
2291 22 : impl_.split(id_user, 2);
2292 22 : impl_.split(id_pass, 0);
2293 22 : impl_.split(id_host, 0);
2294 22 : dest[0] = '/';
2295 22 : dest[1] = '/';
2296 22 : dest[2] = ':';
2297 22 : if (make_absolute)
2298 : {
2299 2 : impl_.split(id_port, n + 1);
2300 2 : dest[n + 3] = '/';
2301 2 : ++impl_.decoded_[id_path];
2302 : }
2303 22 : check_invariants();
2304 22 : return dest + 3;
2305 : }
2306 :
2307 : char*
2308 188 : url_base::
2309 : set_path_impl(
2310 : std::size_t n,
2311 : op_t& op)
2312 : {
2313 188 : check_invariants();
2314 188 : auto const dest = resize_impl(
2315 : id_path, n, op);
2316 188 : return dest;
2317 : }
2318 :
2319 :
2320 : //------------------------------------------------
2321 :
2322 : // return the first segment of the path.
2323 : // this is needed for some algorithms.
2324 : core::string_view
2325 45 : url_base::
2326 : first_segment() const noexcept
2327 : {
2328 45 : if(impl_.nseg_ == 0)
2329 7 : return {};
2330 38 : auto const p0 = impl_.cs_ +
2331 38 : impl_.offset(id_path) +
2332 38 : detail::path_prefix(
2333 38 : impl_.get(id_path));
2334 38 : auto const end = impl_.cs_ +
2335 38 : impl_.offset(id_query);
2336 38 : if(impl_.nseg_ == 1)
2337 42 : return core::string_view(
2338 21 : p0, end - p0);
2339 17 : auto p = p0;
2340 39 : while(*p != '/')
2341 22 : ++p;
2342 17 : BOOST_ASSERT(p < end);
2343 17 : return core::string_view(p0, p - p0);
2344 : }
2345 :
2346 : detail::segments_iter_impl
2347 597 : url_base::
2348 : edit_segments(
2349 : detail::segments_iter_impl const& it0,
2350 : detail::segments_iter_impl const& it1,
2351 : detail::any_segments_iter&& src,
2352 : // -1 = preserve
2353 : // 0 = make relative (can fail)
2354 : // 1 = make absolute
2355 : int absolute)
2356 : {
2357 : // Iterator doesn't belong to this url
2358 597 : BOOST_ASSERT(it0.ref.alias_of(impl_));
2359 :
2360 : // Iterator doesn't belong to this url
2361 597 : BOOST_ASSERT(it1.ref.alias_of(impl_));
2362 :
2363 : // Iterator is in the wrong order
2364 597 : BOOST_ASSERT(it0.index <= it1.index);
2365 :
2366 : // Iterator is out of range
2367 597 : BOOST_ASSERT(it0.index <= impl_.nseg_);
2368 597 : BOOST_ASSERT(it0.pos <= impl_.len(id_path));
2369 :
2370 : // Iterator is out of range
2371 597 : BOOST_ASSERT(it1.index <= impl_.nseg_);
2372 597 : BOOST_ASSERT(it1.pos <= impl_.len(id_path));
2373 :
2374 : //------------------------------------------------
2375 : //
2376 : // Calculate output prefix
2377 : //
2378 : // 0 = ""
2379 : // 1 = "/"
2380 : // 2 = "./"
2381 : // 3 = "/./"
2382 : //
2383 597 : bool const is_abs = is_path_absolute();
2384 597 : if(has_authority())
2385 : {
2386 : // Check if the new
2387 : // path would be empty
2388 213 : if( src.fast_nseg == 0 &&
2389 108 : it0.index == 0 &&
2390 18 : it1.index == impl_.nseg_)
2391 : {
2392 : // VFALCO we don't have
2393 : // access to nchar this early
2394 : //
2395 : //BOOST_ASSERT(nchar == 0);
2396 15 : absolute = 0;
2397 : }
2398 : else
2399 : {
2400 : // prefix "/" required
2401 198 : absolute = 1;
2402 : }
2403 : }
2404 384 : else if(absolute < 0)
2405 : {
2406 384 : absolute = is_abs; // preserve
2407 : }
2408 597 : auto const path_pos = impl_.offset(id_path);
2409 :
2410 597 : std::size_t nchar = 0;
2411 597 : std::size_t prefix = 0;
2412 597 : bool encode_colons = false;
2413 597 : bool cp_src_prefix = false;
2414 597 : if(it0.index > 0)
2415 : {
2416 : // first segment unchanged
2417 323 : prefix = src.fast_nseg > 0;
2418 : }
2419 274 : else if(src.fast_nseg > 0)
2420 : {
2421 : // first segment from src
2422 221 : if(! src.front.empty())
2423 : {
2424 162 : if( src.front == "." &&
2425 7 : src.fast_nseg > 1)
2426 4 : if (src.s.empty())
2427 : {
2428 : // if front is ".", we need the extra "." in the prefix
2429 : // which will maintain the invariant that segments represent
2430 : // {"."}
2431 4 : prefix = 2 + absolute;
2432 : }
2433 : else
2434 : {
2435 : // if the "." prefix is explicitly required from set_path
2436 : // we do not include an extra "." segment
2437 0 : prefix = absolute;
2438 0 : cp_src_prefix = true;
2439 : }
2440 151 : else if(absolute)
2441 79 : prefix = 1;
2442 140 : else if(has_scheme() ||
2443 68 : ! src.front.contains(':'))
2444 67 : prefix = 0;
2445 : else
2446 : {
2447 5 : prefix = 0;
2448 5 : encode_colons = true;
2449 : }
2450 : }
2451 : else
2452 : {
2453 66 : prefix = 2 + absolute;
2454 : }
2455 : }
2456 : else
2457 : {
2458 : // first segment from it1
2459 53 : auto const p =
2460 53 : impl_.cs_ + path_pos + it1.pos;
2461 106 : switch(impl_.cs_ +
2462 53 : impl_.offset(id_query) - p)
2463 : {
2464 34 : case 0:
2465 : // points to end
2466 34 : prefix = absolute;
2467 34 : break;
2468 11 : default:
2469 11 : BOOST_ASSERT(*p == '/');
2470 11 : if(p[1] != '/')
2471 : {
2472 11 : if(absolute)
2473 5 : prefix = 1;
2474 11 : else if(has_scheme() ||
2475 11 : ! it1.dereference().contains(':'))
2476 5 : prefix = 0;
2477 : else
2478 1 : prefix = 2;
2479 11 : break;
2480 : }
2481 : // empty
2482 : BOOST_FALLTHROUGH;
2483 : case 1:
2484 : // empty
2485 8 : BOOST_ASSERT(*p == '/');
2486 8 : prefix = 2 + absolute;
2487 8 : break;
2488 : }
2489 : }
2490 :
2491 : // append '/' to new segs
2492 : // if inserting at front.
2493 597 : std::size_t const suffix =
2494 776 : it1.index == 0 &&
2495 660 : impl_.nseg_ > 0 &&
2496 63 : src.fast_nseg > 0;
2497 :
2498 : //------------------------------------------------
2499 : //
2500 : // Measure the number of encoded characters
2501 : // of output, and the number of inserted
2502 : // segments including internal separators.
2503 : //
2504 597 : src.encode_colons = encode_colons;
2505 597 : std::size_t nseg = 0;
2506 597 : if(src.measure(nchar))
2507 : {
2508 408 : src.encode_colons = false;
2509 : for(;;)
2510 : {
2511 733 : ++nseg;
2512 733 : if(! src.measure(nchar))
2513 406 : break;
2514 325 : ++nchar;
2515 : }
2516 : }
2517 :
2518 595 : switch(src.fast_nseg)
2519 : {
2520 189 : case 0:
2521 189 : BOOST_ASSERT(nseg == 0);
2522 189 : break;
2523 219 : case 1:
2524 219 : BOOST_ASSERT(nseg == 1);
2525 219 : break;
2526 187 : case 2:
2527 187 : BOOST_ASSERT(nseg >= 2);
2528 187 : break;
2529 : }
2530 :
2531 : //------------------------------------------------
2532 : //
2533 : // Calculate [pos0, pos1) to remove
2534 : //
2535 595 : auto pos0 = it0.pos;
2536 595 : if(it0.index == 0)
2537 : {
2538 : // patch pos for prefix
2539 272 : pos0 = 0;
2540 : }
2541 595 : auto pos1 = it1.pos;
2542 595 : if(it1.index == 0)
2543 : {
2544 : // patch pos for prefix
2545 179 : pos1 = detail::path_prefix(
2546 : impl_.get(id_path));
2547 : }
2548 416 : else if(
2549 416 : it0.index == 0 &&
2550 93 : it1.index < impl_.nseg_ &&
2551 : nseg == 0)
2552 : {
2553 : // Remove the slash from segment it1
2554 : // if it is becoming the new first
2555 : // segment.
2556 19 : ++pos1;
2557 : }
2558 : // calc decoded size of old range
2559 : auto const dn0 =
2560 595 : detail::decode_bytes_unsafe(
2561 : core::string_view(
2562 595 : impl_.cs_ +
2563 595 : impl_.offset(id_path) +
2564 : pos0,
2565 : pos1 - pos0));
2566 :
2567 : //------------------------------------------------
2568 : //
2569 : // Resize
2570 : //
2571 1190 : op_t op(*this, &src.s);
2572 : char* dest;
2573 : char const* end;
2574 : {
2575 595 : auto const nremove = pos1 - pos0;
2576 : // check overflow
2577 1190 : if( nchar <= max_size() && (
2578 595 : prefix + suffix <=
2579 595 : max_size() - nchar))
2580 : {
2581 595 : nchar = prefix + nchar + suffix;
2582 939 : if( nchar <= nremove ||
2583 344 : nchar - nremove <=
2584 344 : max_size() - size())
2585 595 : goto ok;
2586 : }
2587 : // too large
2588 0 : detail::throw_length_error();
2589 595 : ok:
2590 : auto const new_size =
2591 595 : size() + nchar - nremove;
2592 595 : reserve_impl(new_size, op);
2593 595 : dest = s_ + path_pos + pos0;
2594 595 : op.move(
2595 595 : dest + nchar,
2596 595 : s_ + path_pos + pos1,
2597 595 : size() - path_pos - pos1);
2598 1190 : impl_.set_size(
2599 : id_path,
2600 595 : impl_.len(id_path) + nchar - nremove);
2601 595 : BOOST_ASSERT(size() == new_size);
2602 595 : end = dest + nchar;
2603 595 : impl_.nseg_ = impl_.nseg_ + nseg - (
2604 595 : it1.index - it0.index) - cp_src_prefix;
2605 595 : if(s_)
2606 593 : s_[size()] = '\0';
2607 : }
2608 :
2609 : //------------------------------------------------
2610 : //
2611 : // Output segments and internal separators:
2612 : //
2613 : // prefix [ segment [ '/' segment ] ] suffix
2614 : //
2615 595 : auto const dest0 = dest;
2616 595 : switch(prefix)
2617 : {
2618 38 : case 3:
2619 38 : *dest++ = '/';
2620 38 : *dest++ = '.';
2621 38 : *dest++ = '/';
2622 38 : break;
2623 41 : case 2:
2624 41 : *dest++ = '.';
2625 : BOOST_FALLTHROUGH;
2626 323 : case 1:
2627 323 : *dest++ = '/';
2628 323 : break;
2629 234 : default:
2630 234 : break;
2631 : }
2632 595 : src.rewind();
2633 595 : if(nseg > 0)
2634 : {
2635 406 : src.encode_colons = encode_colons;
2636 : for(;;)
2637 : {
2638 731 : src.copy(dest, end);
2639 731 : if(--nseg == 0)
2640 406 : break;
2641 325 : *dest++ = '/';
2642 325 : src.encode_colons = false;
2643 : }
2644 406 : if(suffix)
2645 63 : *dest++ = '/';
2646 : }
2647 595 : BOOST_ASSERT(dest == dest0 + nchar);
2648 :
2649 : // calc decoded size of new range,
2650 : auto const dn =
2651 595 : detail::decode_bytes_unsafe(
2652 595 : core::string_view(dest0, dest - dest0));
2653 595 : impl_.decoded_[id_path] += dn - dn0;
2654 :
2655 : return detail::segments_iter_impl(
2656 1190 : impl_, pos0, it0.index);
2657 : }
2658 :
2659 : //------------------------------------------------
2660 :
2661 : auto
2662 138 : url_base::
2663 : edit_params(
2664 : detail::params_iter_impl const& it0,
2665 : detail::params_iter_impl const& it1,
2666 : detail::any_params_iter&& src) ->
2667 : detail::params_iter_impl
2668 : {
2669 138 : auto pos0 = impl_.offset(id_query);
2670 138 : auto pos1 = pos0 + it1.pos;
2671 138 : pos0 = pos0 + it0.pos;
2672 :
2673 : // Iterator doesn't belong to this url
2674 138 : BOOST_ASSERT(it0.ref.alias_of(impl_));
2675 :
2676 : // Iterator doesn't belong to this url
2677 138 : BOOST_ASSERT(it1.ref.alias_of(impl_));
2678 :
2679 : // Iterator is in the wrong order
2680 138 : BOOST_ASSERT(it0.index <= it1.index);
2681 :
2682 : // Iterator is out of range
2683 138 : BOOST_ASSERT(it0.index <= impl_.nparam_);
2684 138 : BOOST_ASSERT(pos0 <= impl_.offset(id_frag));
2685 :
2686 : // Iterator is out of range
2687 138 : BOOST_ASSERT(it1.index <= impl_.nparam_);
2688 138 : BOOST_ASSERT(pos1 <= impl_.offset(id_frag));
2689 :
2690 : // calc decoded size of old range,
2691 : // minus one if '?' or '&' prefixed
2692 : auto const dn0 =
2693 138 : detail::decode_bytes_unsafe(
2694 : core::string_view(
2695 138 : impl_.cs_ + pos0,
2696 : pos1 - pos0)) - (
2697 138 : impl_.len(id_query) > 0);
2698 :
2699 : //------------------------------------------------
2700 : //
2701 : // Measure the number of encoded characters
2702 : // of output, and the number of inserted
2703 : // segments including internal separators.
2704 : //
2705 :
2706 138 : std::size_t nchar = 0;
2707 138 : std::size_t nparam = 0;
2708 138 : if(src.measure(nchar))
2709 : {
2710 111 : ++nchar; // for '?' or '&'
2711 : for(;;)
2712 : {
2713 176 : ++nparam;
2714 176 : if(! src.measure(nchar))
2715 111 : break;
2716 65 : ++nchar; // for '&'
2717 : }
2718 : }
2719 :
2720 : //------------------------------------------------
2721 : //
2722 : // Resize
2723 : //
2724 133 : op_t op(*this, &src.s0, &src.s1);
2725 : char* dest;
2726 : char const* end;
2727 : {
2728 133 : auto const nremove = pos1 - pos0;
2729 : // check overflow
2730 228 : if( nchar > nremove &&
2731 95 : nchar - nremove >
2732 95 : max_size() - size())
2733 : {
2734 : // too large
2735 0 : detail::throw_length_error();
2736 : }
2737 133 : auto const nparam1 =
2738 133 : impl_.nparam_ + nparam - (
2739 133 : it1.index - it0.index);
2740 133 : reserve_impl(size() + nchar - nremove, op);
2741 133 : dest = s_ + pos0;
2742 133 : end = dest + nchar;
2743 133 : if(impl_.nparam_ > 0)
2744 : {
2745 : // needed when we move
2746 : // the beginning of the query
2747 99 : s_[impl_.offset(id_query)] = '&';
2748 : }
2749 133 : op.move(
2750 133 : dest + nchar,
2751 133 : impl_.cs_ + pos1,
2752 133 : size() - pos1);
2753 266 : impl_.set_size(
2754 : id_query,
2755 133 : impl_.len(id_query) +
2756 : nchar - nremove);
2757 133 : impl_.nparam_ = nparam1;
2758 133 : if(nparam1 > 0)
2759 : {
2760 : // needed when we erase
2761 : // the beginning of the query
2762 133 : s_[impl_.offset(id_query)] = '?';
2763 : }
2764 133 : if(s_)
2765 133 : s_[size()] = '\0';
2766 : }
2767 133 : auto const dest0 = dest;
2768 :
2769 : //------------------------------------------------
2770 : //
2771 : // Output params and internal separators:
2772 : //
2773 : // [ '?' param ] [ '&' param ]
2774 : //
2775 133 : if(nparam > 0)
2776 : {
2777 111 : if(it0.index == 0)
2778 68 : *dest++ = '?';
2779 : else
2780 43 : *dest++ = '&';
2781 111 : src.rewind();
2782 : for(;;)
2783 : {
2784 176 : src.copy(dest, end);
2785 176 : if(--nparam == 0)
2786 111 : break;
2787 65 : *dest++ = '&';
2788 : }
2789 : }
2790 :
2791 : // calc decoded size of new range,
2792 : // minus one if '?' or '&' prefixed
2793 : auto const dn =
2794 133 : detail::decode_bytes_unsafe(
2795 133 : core::string_view(dest0, dest - dest0)) - (
2796 133 : impl_.len(id_query) > 0);
2797 :
2798 133 : impl_.decoded_[id_query] += (dn - dn0);
2799 :
2800 : return detail::params_iter_impl(
2801 133 : impl_,
2802 133 : pos0 - impl_.offset_[id_query],
2803 266 : it0.index);
2804 : }
2805 :
2806 : //------------------------------------------------
2807 :
2808 : void
2809 383 : url_base::
2810 : decoded_to_lower_impl(int id) noexcept
2811 : {
2812 383 : char* it = s_ + impl_.offset(id);
2813 383 : char const* const end = s_ + impl_.offset(id + 1);
2814 2213 : while(it < end)
2815 : {
2816 1830 : if (*it != '%')
2817 : {
2818 3650 : *it = grammar::to_lower(
2819 1825 : *it);
2820 1825 : ++it;
2821 1825 : continue;
2822 : }
2823 5 : it += 3;
2824 : }
2825 383 : }
2826 :
2827 : void
2828 38 : url_base::
2829 : to_lower_impl(int id) noexcept
2830 : {
2831 38 : char* it = s_ + impl_.offset(id);
2832 38 : char const* const end = s_ + impl_.offset(id + 1);
2833 155 : while(it < end)
2834 : {
2835 234 : *it = grammar::to_lower(
2836 117 : *it);
2837 117 : ++it;
2838 : }
2839 38 : }
2840 :
2841 : } // urls
2842 : } // boost
2843 :
|