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