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