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