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_view_base.hpp>
14 : #include <boost/url/url_view.hpp>
15 : #include <boost/url/detail/except.hpp>
16 : #include "detail/normalize.hpp"
17 : #include "detail/over_allocator.hpp"
18 :
19 : namespace boost {
20 : namespace urls {
21 :
22 : // construct empty view
23 4090 : url_view_base::
24 4090 : url_view_base() noexcept
25 : : impl_(from::url)
26 4090 : , pi_(&impl_)
27 : {
28 4090 : }
29 :
30 : // construct reference
31 20785 : url_view_base::
32 : url_view_base(
33 20785 : detail::url_impl const& impl) noexcept
34 : : impl_(impl)
35 20785 : , pi_(&impl_)
36 : {
37 20785 : }
38 :
39 : //------------------------------------------------
40 :
41 : std::size_t
42 304 : url_view_base::
43 : digest(std::size_t salt) const noexcept
44 : {
45 304 : detail::fnv_1a h(salt);
46 304 : detail::ci_digest(pi_->get(id_scheme), h);
47 304 : detail::digest_encoded(pi_->get(id_user), h);
48 304 : detail::digest_encoded(pi_->get(id_pass), h);
49 304 : detail::ci_digest_encoded(pi_->get(id_host), h);
50 304 : h.put(pi_->get(id_port));
51 304 : detail::normalized_path_digest(
52 304 : pi_->get(id_path), is_path_absolute(), h);
53 304 : detail::digest_encoded(pi_->get(id_query), h);
54 304 : detail::digest_encoded(pi_->get(id_frag), h);
55 304 : return h.digest();
56 : }
57 :
58 : //------------------------------------------------
59 : //
60 : // Observers
61 : //
62 : //------------------------------------------------
63 :
64 : struct url_view_base::shared_impl
65 : : url_view
66 : {
67 : virtual
68 2 : ~shared_impl()
69 2 : {
70 2 : }
71 :
72 2 : shared_impl(
73 : url_view const& u) noexcept
74 2 : : url_view(u)
75 : {
76 2 : impl_.cs_ = reinterpret_cast<
77 : char const*>(this + 1);
78 2 : }
79 : };
80 :
81 : std::shared_ptr<url_view const>
82 2 : url_view_base::
83 : persist() const
84 : {
85 : using T = shared_impl;
86 : using Alloc = std::allocator<char>;
87 4 : Alloc a;
88 : auto p = std::allocate_shared<T>(
89 2 : detail::over_allocator<T, Alloc>(
90 6 : size(), a), url_view(*pi_));
91 2 : std::memcpy(
92 : reinterpret_cast<char*>(
93 2 : p.get() + 1), data(), size());
94 4 : return p;
95 : }
96 :
97 : //------------------------------------------------
98 : //
99 : // Scheme
100 : //
101 : //------------------------------------------------
102 :
103 : bool
104 2836 : url_view_base::
105 : has_scheme() const noexcept
106 : {
107 2836 : auto const n = pi_->len(
108 : id_scheme);
109 2836 : if(n == 0)
110 579 : return false;
111 2257 : BOOST_ASSERT(n > 1);
112 2257 : BOOST_ASSERT(
113 : pi_->get(id_scheme
114 : ).ends_with(':'));
115 2257 : return true;
116 : }
117 :
118 : core::string_view
119 1432 : url_view_base::
120 : scheme() const noexcept
121 : {
122 1432 : auto s = pi_->get(id_scheme);
123 1432 : if(! s.empty())
124 : {
125 1336 : BOOST_ASSERT(s.size() > 1);
126 1336 : BOOST_ASSERT(s.ends_with(':'));
127 1336 : s.remove_suffix(1);
128 : }
129 1432 : return s;
130 : }
131 :
132 : urls::scheme
133 44 : url_view_base::
134 : scheme_id() const noexcept
135 : {
136 44 : return pi_->scheme_;
137 : }
138 :
139 : //------------------------------------------------
140 : //
141 : // Authority
142 : //
143 : //------------------------------------------------
144 :
145 : authority_view
146 479 : url_view_base::
147 : authority() const noexcept
148 : {
149 479 : detail::url_impl u(from::authority);
150 479 : u.cs_ = encoded_authority().data();
151 479 : if(has_authority())
152 : {
153 479 : u.set_size(id_user, pi_->len(id_user) - 2);
154 479 : u.set_size(id_pass, pi_->len(id_pass));
155 479 : u.set_size(id_host, pi_->len(id_host));
156 479 : u.set_size(id_port, pi_->len(id_port));
157 : }
158 : else
159 : {
160 0 : u.set_size(id_user, pi_->len(id_user));
161 0 : BOOST_ASSERT(pi_->len(id_pass) == 0);
162 0 : BOOST_ASSERT(pi_->len(id_host) == 0);
163 0 : BOOST_ASSERT(pi_->len(id_port) == 0);
164 : }
165 479 : u.decoded_[id_user] = pi_->decoded_[id_user];
166 479 : u.decoded_[id_pass] = pi_->decoded_[id_pass];
167 479 : u.decoded_[id_host] = pi_->decoded_[id_host];
168 8143 : for (int i = 0; i < 16; ++i)
169 7664 : u.ip_addr_[i] = pi_->ip_addr_[i];
170 479 : u.port_number_ = pi_->port_number_;
171 479 : u.host_type_ = pi_->host_type_;
172 479 : return u.construct_authority();
173 : }
174 :
175 : pct_string_view
176 637 : url_view_base::
177 : encoded_authority() const noexcept
178 : {
179 637 : auto s = pi_->get(id_user, id_path);
180 637 : if(! s.empty())
181 : {
182 597 : BOOST_ASSERT(has_authority());
183 597 : s.remove_prefix(2);
184 : }
185 : return make_pct_string_view_unsafe(
186 : s.data(),
187 : s.size(),
188 637 : pi_->decoded_[id_user] +
189 637 : pi_->decoded_[id_pass] +
190 637 : pi_->decoded_[id_host] +
191 637 : pi_->decoded_[id_port] +
192 637 : has_password());
193 : }
194 :
195 : //------------------------------------------------
196 : //
197 : // Userinfo
198 : //
199 : //------------------------------------------------
200 :
201 : bool
202 261 : url_view_base::
203 : has_userinfo() const noexcept
204 : {
205 261 : auto n = pi_->len(id_pass);
206 261 : if(n == 0)
207 97 : return false;
208 164 : BOOST_ASSERT(has_authority());
209 164 : BOOST_ASSERT(pi_->get(
210 : id_pass).ends_with('@'));
211 164 : return true;
212 : }
213 :
214 : bool
215 772 : url_view_base::
216 : has_password() const noexcept
217 : {
218 772 : auto const n = pi_->len(id_pass);
219 772 : if(n > 1)
220 : {
221 114 : BOOST_ASSERT(pi_->get(id_pass
222 : ).starts_with(':'));
223 114 : BOOST_ASSERT(pi_->get(id_pass
224 : ).ends_with('@'));
225 114 : return true;
226 : }
227 658 : BOOST_ASSERT(n == 0 || pi_->get(
228 : id_pass).ends_with('@'));
229 658 : return false;
230 : }
231 :
232 : pct_string_view
233 119 : url_view_base::
234 : encoded_userinfo() const noexcept
235 : {
236 119 : auto s = pi_->get(
237 119 : id_user, id_host);
238 119 : if(s.empty())
239 8 : return s;
240 111 : BOOST_ASSERT(
241 : has_authority());
242 111 : s.remove_prefix(2);
243 111 : if(s.empty())
244 34 : return s;
245 77 : BOOST_ASSERT(
246 : s.ends_with('@'));
247 77 : s.remove_suffix(1);
248 : return make_pct_string_view_unsafe(
249 : s.data(),
250 : s.size(),
251 77 : pi_->decoded_[id_user] +
252 77 : pi_->decoded_[id_pass] +
253 77 : has_password());
254 : }
255 :
256 : pct_string_view
257 131 : url_view_base::
258 : encoded_user() const noexcept
259 : {
260 131 : auto s = pi_->get(id_user);
261 131 : if(! s.empty())
262 : {
263 130 : BOOST_ASSERT(
264 : has_authority());
265 130 : s.remove_prefix(2);
266 : }
267 : return make_pct_string_view_unsafe(
268 : s.data(),
269 : s.size(),
270 131 : pi_->decoded_[id_user]);
271 : }
272 :
273 : pct_string_view
274 94 : url_view_base::
275 : encoded_password() const noexcept
276 : {
277 94 : auto s = pi_->get(id_pass);
278 94 : switch(s.size())
279 : {
280 24 : case 1:
281 24 : BOOST_ASSERT(
282 : s.starts_with('@'));
283 24 : s.remove_prefix(1);
284 : BOOST_FALLTHROUGH;
285 42 : case 0:
286 : return make_pct_string_view_unsafe(
287 42 : s.data(), s.size(), 0);
288 52 : default:
289 52 : break;
290 : }
291 52 : BOOST_ASSERT(s.ends_with('@'));
292 52 : BOOST_ASSERT(s.starts_with(':'));
293 : return make_pct_string_view_unsafe(
294 52 : s.data() + 1,
295 52 : s.size() - 2,
296 52 : pi_->decoded_[id_pass]);
297 : }
298 :
299 : //------------------------------------------------
300 : //
301 : // Host
302 : //
303 : //------------------------------------------------
304 : /*
305 : host_type host_type() // ipv4, ipv6, ipvfuture, name
306 :
307 : std::string host() // return encoded_host().decode()
308 : pct_string_view encoded_host() // return host part, as-is
309 : std::string host_address() // return encoded_host_address().decode()
310 : pct_string_view encoded_host_address() // ipv4, ipv6, ipvfut, or encoded name, no brackets
311 :
312 : ipv4_address host_ipv4_address() // return ipv4_address or {}
313 : ipv6_address host_ipv6_address() // return ipv6_address or {}
314 : core::string_view host_ipvfuture() // return ipvfuture or {}
315 : std::string host_name() // return decoded name or ""
316 : pct_string_view encoded_host_name() // return encoded host name or ""
317 : */
318 :
319 : pct_string_view
320 521 : url_view_base::
321 : encoded_host() const noexcept
322 : {
323 521 : return pi_->pct_get(id_host);
324 : }
325 :
326 : pct_string_view
327 117 : url_view_base::
328 : encoded_host_address() const noexcept
329 : {
330 117 : core::string_view s = pi_->get(id_host);
331 : std::size_t n;
332 117 : switch(pi_->host_type_)
333 : {
334 41 : default:
335 : case urls::host_type::none:
336 41 : BOOST_ASSERT(s.empty());
337 41 : n = 0;
338 41 : break;
339 :
340 53 : case urls::host_type::name:
341 : case urls::host_type::ipv4:
342 53 : n = pi_->decoded_[id_host];
343 53 : break;
344 :
345 23 : case urls::host_type::ipv6:
346 : case urls::host_type::ipvfuture:
347 : {
348 23 : BOOST_ASSERT(
349 : pi_->decoded_[id_host] ==
350 : s.size());
351 23 : BOOST_ASSERT(s.size() >= 2);
352 23 : BOOST_ASSERT(s.front() == '[');
353 23 : BOOST_ASSERT(s.back() == ']');
354 23 : s = s.substr(1, s.size() - 2);
355 23 : n = pi_->decoded_[id_host] - 2;
356 23 : break;
357 : }
358 : }
359 : return make_pct_string_view_unsafe(
360 : s.data(),
361 : s.size(),
362 117 : n);
363 : }
364 :
365 : urls::ipv4_address
366 51 : url_view_base::
367 : host_ipv4_address() const noexcept
368 : {
369 51 : if(pi_->host_type_ !=
370 : urls::host_type::ipv4)
371 35 : return {};
372 16 : ipv4_address::bytes_type b{{}};
373 16 : std::memcpy(
374 16 : &b[0], &pi_->ip_addr_[0], b.size());
375 16 : return urls::ipv4_address(b);
376 : }
377 :
378 : urls::ipv6_address
379 51 : url_view_base::
380 : host_ipv6_address() const noexcept
381 : {
382 51 : if(pi_->host_type_ !=
383 : urls::host_type::ipv6)
384 43 : return {};
385 8 : ipv6_address::bytes_type b{{}};
386 8 : std::memcpy(
387 8 : &b[0], &pi_->ip_addr_[0], b.size());
388 8 : return urls::ipv6_address(b);
389 : }
390 :
391 : core::string_view
392 51 : url_view_base::
393 : host_ipvfuture() const noexcept
394 : {
395 51 : if(pi_->host_type_ !=
396 : urls::host_type::ipvfuture)
397 44 : return {};
398 7 : core::string_view s = pi_->get(id_host);
399 7 : BOOST_ASSERT(s.size() >= 6);
400 7 : BOOST_ASSERT(s.front() == '[');
401 7 : BOOST_ASSERT(s.back() == ']');
402 7 : s = s.substr(1, s.size() - 2);
403 7 : return s;
404 : }
405 :
406 : pct_string_view
407 146 : url_view_base::
408 : encoded_host_name() const noexcept
409 : {
410 146 : if(pi_->host_type_ !=
411 : urls::host_type::name)
412 78 : return {};
413 68 : core::string_view s = pi_->get(id_host);
414 : return make_pct_string_view_unsafe(
415 : s.data(),
416 : s.size(),
417 68 : pi_->decoded_[id_host]);
418 : }
419 :
420 : pct_string_view
421 10 : url_view_base::
422 : encoded_zone_id() const noexcept
423 : {
424 10 : if(pi_->host_type_ !=
425 : urls::host_type::ipv6)
426 2 : return {};
427 8 : core::string_view s = pi_->get(id_host);
428 8 : BOOST_ASSERT(s.front() == '[');
429 8 : BOOST_ASSERT(s.back() == ']');
430 8 : s = s.substr(1, s.size() - 2);
431 8 : auto pos = s.find("%25");
432 8 : if (pos == core::string_view::npos)
433 2 : return {};
434 6 : s.remove_prefix(pos + 3);
435 6 : return *make_pct_string_view(s);
436 : }
437 :
438 : //------------------------------------------------
439 :
440 : bool
441 364 : url_view_base::
442 : has_port() const noexcept
443 : {
444 364 : auto const n = pi_->len(id_port);
445 364 : if(n == 0)
446 87 : return false;
447 277 : BOOST_ASSERT(
448 : pi_->get(id_port).starts_with(':'));
449 277 : return true;
450 : }
451 :
452 : core::string_view
453 179 : url_view_base::
454 : port() const noexcept
455 : {
456 179 : auto s = pi_->get(id_port);
457 179 : if(s.empty())
458 58 : return s;
459 121 : BOOST_ASSERT(has_port());
460 121 : return s.substr(1);
461 : }
462 :
463 : std::uint16_t
464 101 : url_view_base::
465 : port_number() const noexcept
466 : {
467 101 : BOOST_ASSERT(
468 : has_port() ||
469 : pi_->port_number_ == 0);
470 101 : return pi_->port_number_;
471 : }
472 :
473 : //------------------------------------------------
474 : //
475 : // Path
476 : //
477 : //------------------------------------------------
478 :
479 : pct_string_view
480 1314 : url_view_base::
481 : encoded_path() const noexcept
482 : {
483 1314 : return pi_->pct_get(id_path);
484 : }
485 :
486 : segments_view
487 45 : url_view_base::
488 : segments() const noexcept
489 : {
490 45 : return {detail::path_ref(*pi_)};
491 : }
492 :
493 : segments_encoded_view
494 659 : url_view_base::
495 : encoded_segments() const noexcept
496 : {
497 : return segments_encoded_view(
498 659 : detail::path_ref(*pi_));
499 : }
500 :
501 : //------------------------------------------------
502 : //
503 : // Query
504 : //
505 : //------------------------------------------------
506 :
507 : bool
508 731 : url_view_base::
509 : has_query() const noexcept
510 : {
511 731 : auto const n = pi_->len(
512 : id_query);
513 731 : if(n == 0)
514 613 : return false;
515 118 : BOOST_ASSERT(
516 : pi_->get(id_query).
517 : starts_with('?'));
518 118 : return true;
519 : }
520 :
521 : pct_string_view
522 274 : url_view_base::
523 : encoded_query() const noexcept
524 : {
525 274 : auto s = pi_->get(id_query);
526 274 : if(s.empty())
527 10 : return s;
528 264 : BOOST_ASSERT(
529 : s.starts_with('?'));
530 264 : return s.substr(1);
531 : }
532 :
533 : params_encoded_view
534 55 : url_view_base::
535 : encoded_params() const noexcept
536 : {
537 55 : return params_encoded_view(*pi_);
538 : }
539 :
540 : params_view
541 46 : url_view_base::
542 : params() const noexcept
543 : {
544 : return params_view(
545 46 : *pi_,
546 : encoding_opts{
547 46 : true,false,false});
548 : }
549 :
550 : params_view
551 0 : url_view_base::
552 : params(encoding_opts opt) const noexcept
553 : {
554 0 : return params_view(*pi_, opt);
555 : }
556 :
557 : //------------------------------------------------
558 : //
559 : // Fragment
560 : //
561 : //------------------------------------------------
562 :
563 : bool
564 639 : url_view_base::
565 : has_fragment() const noexcept
566 : {
567 639 : auto const n = pi_->len(id_frag);
568 639 : if(n == 0)
569 514 : return false;
570 125 : BOOST_ASSERT(
571 : pi_->get(id_frag).
572 : starts_with('#'));
573 125 : return true;
574 : }
575 :
576 : pct_string_view
577 155 : url_view_base::
578 : encoded_fragment() const noexcept
579 : {
580 155 : auto s = pi_->get(id_frag);
581 155 : if(! s.empty())
582 : {
583 153 : BOOST_ASSERT(
584 : s.starts_with('#'));
585 153 : s.remove_prefix(1);
586 : }
587 : return make_pct_string_view_unsafe(
588 : s.data(),
589 : s.size(),
590 155 : pi_->decoded_[id_frag]);
591 : }
592 :
593 : //------------------------------------------------
594 : //
595 : // Compound Fields
596 : //
597 : //------------------------------------------------
598 :
599 : pct_string_view
600 120 : url_view_base::
601 : encoded_host_and_port() const noexcept
602 : {
603 120 : return pi_->pct_get(id_host, id_path);
604 : }
605 :
606 : pct_string_view
607 16 : url_view_base::
608 : encoded_origin() const noexcept
609 : {
610 16 : if(pi_->len(id_user) < 2)
611 14 : return {};
612 2 : return pi_->get(id_scheme, id_path);
613 : }
614 :
615 : pct_string_view
616 1 : url_view_base::
617 : encoded_resource() const noexcept
618 : {
619 1 : auto n =
620 1 : pi_->decoded_[id_path] +
621 1 : pi_->decoded_[id_query] +
622 1 : pi_->decoded_[id_frag];
623 1 : if(has_query())
624 1 : ++n;
625 1 : if(has_fragment())
626 1 : ++n;
627 1 : BOOST_ASSERT(pct_string_view(
628 : pi_->get(id_path, id_end)
629 : ).decoded_size() == n);
630 1 : auto s = pi_->get(id_path, id_end);
631 : return make_pct_string_view_unsafe(
632 1 : s.data(), s.size(), n);
633 : }
634 :
635 : pct_string_view
636 2 : url_view_base::
637 : encoded_target() const noexcept
638 : {
639 2 : auto n =
640 2 : pi_->decoded_[id_path] +
641 2 : pi_->decoded_[id_query];
642 2 : if(has_query())
643 1 : ++n;
644 2 : BOOST_ASSERT(pct_string_view(
645 : pi_->get(id_path, id_frag)
646 : ).decoded_size() == n);
647 2 : auto s = pi_->get(id_path, id_frag);
648 : return make_pct_string_view_unsafe(
649 2 : s.data(), s.size(), n);
650 : }
651 :
652 : //------------------------------------------------
653 : //
654 : // Comparisons
655 : //
656 : //------------------------------------------------
657 :
658 : int
659 276 : url_view_base::
660 : compare(const url_view_base& other) const noexcept
661 : {
662 : int comp =
663 276 : static_cast<int>(has_scheme()) -
664 276 : static_cast<int>(other.has_scheme());
665 276 : if ( comp != 0 )
666 0 : return comp;
667 :
668 276 : if (has_scheme())
669 : {
670 196 : comp = detail::ci_compare(
671 : scheme(),
672 : other.scheme());
673 196 : if ( comp != 0 )
674 14 : return comp;
675 : }
676 :
677 262 : comp =
678 262 : static_cast<int>(has_authority()) -
679 262 : static_cast<int>(other.has_authority());
680 262 : if ( comp != 0 )
681 0 : return comp;
682 :
683 262 : if (has_authority())
684 : {
685 182 : comp = authority().compare(other.authority());
686 182 : if ( comp != 0 )
687 89 : return comp;
688 : }
689 :
690 173 : comp = detail::segments_compare(
691 : encoded_segments(),
692 : other.encoded_segments());
693 173 : if ( comp != 0 )
694 43 : return comp;
695 :
696 130 : comp =
697 130 : static_cast<int>(has_query()) -
698 130 : static_cast<int>(other.has_query());
699 130 : if ( comp != 0 )
700 0 : return comp;
701 :
702 130 : if (has_query())
703 : {
704 34 : comp = detail::compare_encoded(
705 17 : encoded_query(),
706 17 : other.encoded_query());
707 17 : if ( comp != 0 )
708 14 : return comp;
709 : }
710 :
711 116 : comp =
712 116 : static_cast<int>(has_fragment()) -
713 116 : static_cast<int>(other.has_fragment());
714 116 : if ( comp != 0 )
715 0 : return comp;
716 :
717 116 : if (has_fragment())
718 : {
719 44 : comp = detail::compare_encoded(
720 22 : encoded_fragment(),
721 22 : other.encoded_fragment());
722 22 : if ( comp != 0 )
723 21 : return comp;
724 : }
725 :
726 95 : return 0;
727 : }
728 :
729 : } // urls
730 : } // boost
731 :
|