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 4116 : url_view_base::
24 4116 : url_view_base() noexcept
25 4116 : : impl_(from::url)
26 4116 : , pi_(&impl_)
27 : {
28 4116 : }
29 :
30 : // construct reference
31 20989 : url_view_base::
32 : url_view_base(
33 20989 : detail::url_impl const& impl) noexcept
34 20989 : : impl_(impl)
35 20989 : , pi_(&impl_)
36 : {
37 20989 : }
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 : Alloc a;
88 : auto p = std::allocate_shared<T>(
89 4 : detail::over_allocator<T, Alloc>(
90 4 : 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 2 : }
96 :
97 : //------------------------------------------------
98 : //
99 : // Scheme
100 : //
101 : //------------------------------------------------
102 :
103 : bool
104 2878 : url_view_base::
105 : has_scheme() const noexcept
106 : {
107 2878 : auto const n = pi_->len(
108 : id_scheme);
109 2878 : if(n == 0)
110 582 : return false;
111 2296 : BOOST_ASSERT(n > 1);
112 2296 : BOOST_ASSERT(
113 : pi_->get(id_scheme
114 : ).ends_with(':'));
115 2296 : return true;
116 : }
117 :
118 : core::string_view
119 1448 : url_view_base::
120 : scheme() const noexcept
121 : {
122 1448 : auto s = pi_->get(id_scheme);
123 1448 : if(! s.empty())
124 : {
125 1352 : BOOST_ASSERT(s.size() > 1);
126 1352 : BOOST_ASSERT(s.ends_with(':'));
127 1352 : s.remove_suffix(1);
128 : }
129 1448 : 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 495 : url_view_base::
147 : authority() const noexcept
148 : {
149 495 : detail::url_impl u(from::authority);
150 495 : u.cs_ = encoded_authority().data();
151 495 : if(has_authority())
152 : {
153 495 : u.set_size(id_user, pi_->len(id_user) - 2);
154 495 : u.set_size(id_pass, pi_->len(id_pass));
155 495 : u.set_size(id_host, pi_->len(id_host));
156 495 : 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 495 : u.decoded_[id_user] = pi_->decoded_[id_user];
166 495 : u.decoded_[id_pass] = pi_->decoded_[id_pass];
167 495 : u.decoded_[id_host] = pi_->decoded_[id_host];
168 8415 : for (int i = 0; i < 16; ++i)
169 7920 : u.ip_addr_[i] = pi_->ip_addr_[i];
170 495 : u.port_number_ = pi_->port_number_;
171 495 : u.host_type_ = pi_->host_type_;
172 495 : return u.construct_authority();
173 : }
174 :
175 : pct_string_view
176 653 : url_view_base::
177 : encoded_authority() const noexcept
178 : {
179 653 : auto s = pi_->get(id_user, id_path);
180 653 : if(! s.empty())
181 : {
182 613 : BOOST_ASSERT(has_authority());
183 613 : s.remove_prefix(2);
184 : }
185 653 : return make_pct_string_view_unsafe(
186 : s.data(),
187 : s.size(),
188 653 : pi_->decoded_[id_user] +
189 653 : pi_->decoded_[id_pass] +
190 653 : pi_->decoded_[id_host] +
191 653 : pi_->decoded_[id_port] +
192 1306 : 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 788 : url_view_base::
216 : has_password() const noexcept
217 : {
218 788 : auto const n = pi_->len(id_pass);
219 788 : 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 674 : BOOST_ASSERT(n == 0 || pi_->get(
228 : id_pass).ends_with('@'));
229 674 : 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 : 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 77 : 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 154 : 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 131 : return make_pct_string_view_unsafe(
268 : s.data(),
269 : s.size(),
270 262 : 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 42 : 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 52 : return make_pct_string_view_unsafe(
294 52 : s.data() + 1,
295 52 : s.size() - 2,
296 104 : 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 523 : url_view_base::
321 : encoded_host() const noexcept
322 : {
323 523 : return pi_->pct_get(id_host);
324 : }
325 :
326 : pct_string_view
327 119 : url_view_base::
328 : encoded_host_address() const noexcept
329 : {
330 119 : core::string_view s = pi_->get(id_host);
331 : std::size_t n;
332 119 : 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 25 : case urls::host_type::ipv6:
346 : case urls::host_type::ipvfuture:
347 : {
348 25 : BOOST_ASSERT(
349 : pi_->decoded_[id_host] ==
350 : s.size() ||
351 : !this->encoded_zone_id().empty());
352 25 : BOOST_ASSERT(s.size() >= 2);
353 25 : BOOST_ASSERT(s.front() == '[');
354 25 : BOOST_ASSERT(s.back() == ']');
355 25 : s = s.substr(1, s.size() - 2);
356 25 : n = pi_->decoded_[id_host] - 2;
357 25 : break;
358 : }
359 : }
360 119 : return make_pct_string_view_unsafe(
361 : s.data(),
362 : s.size(),
363 119 : n);
364 : }
365 :
366 : urls::ipv4_address
367 51 : url_view_base::
368 : host_ipv4_address() const noexcept
369 : {
370 51 : if(pi_->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], &pi_->ip_addr_[0], b.size());
376 16 : return urls::ipv4_address(b);
377 : }
378 :
379 : urls::ipv6_address
380 59 : url_view_base::
381 : host_ipv6_address() const noexcept
382 : {
383 59 : if(pi_->host_type_ !=
384 : urls::host_type::ipv6)
385 45 : return {};
386 14 : ipv6_address::bytes_type b{{}};
387 28 : std::memcpy(
388 14 : &b[0], &pi_->ip_addr_[0], b.size());
389 14 : return {b};
390 : }
391 :
392 : core::string_view
393 51 : url_view_base::
394 : host_ipvfuture() const noexcept
395 : {
396 51 : if(pi_->host_type_ !=
397 : urls::host_type::ipvfuture)
398 44 : return {};
399 7 : core::string_view s = pi_->get(id_host);
400 7 : BOOST_ASSERT(s.size() >= 6);
401 7 : BOOST_ASSERT(s.front() == '[');
402 7 : BOOST_ASSERT(s.back() == ']');
403 7 : s = s.substr(1, s.size() - 2);
404 7 : return s;
405 : }
406 :
407 : pct_string_view
408 146 : url_view_base::
409 : encoded_host_name() const noexcept
410 : {
411 146 : if(pi_->host_type_ !=
412 : urls::host_type::name)
413 78 : return {};
414 68 : core::string_view s = pi_->get(id_host);
415 68 : return make_pct_string_view_unsafe(
416 : s.data(),
417 : s.size(),
418 136 : pi_->decoded_[id_host]);
419 : }
420 :
421 : pct_string_view
422 24 : url_view_base::
423 : encoded_zone_id() const noexcept
424 : {
425 24 : if(pi_->host_type_ !=
426 : urls::host_type::ipv6)
427 6 : return {};
428 18 : core::string_view s = pi_->get(id_host);
429 18 : BOOST_ASSERT(s.front() == '[');
430 18 : BOOST_ASSERT(s.back() == ']');
431 18 : s = s.substr(1, s.size() - 2);
432 18 : auto pos = s.find("%25");
433 18 : if (pos == core::string_view::npos)
434 2 : return {};
435 16 : s.remove_prefix(pos + 3);
436 16 : return *make_pct_string_view(s);
437 : }
438 :
439 : //------------------------------------------------
440 :
441 : bool
442 364 : url_view_base::
443 : has_port() const noexcept
444 : {
445 364 : auto const n = pi_->len(id_port);
446 364 : if(n == 0)
447 87 : return false;
448 277 : BOOST_ASSERT(
449 : pi_->get(id_port).starts_with(':'));
450 277 : return true;
451 : }
452 :
453 : core::string_view
454 179 : url_view_base::
455 : port() const noexcept
456 : {
457 179 : auto s = pi_->get(id_port);
458 179 : if(s.empty())
459 58 : return s;
460 121 : BOOST_ASSERT(has_port());
461 121 : return s.substr(1);
462 : }
463 :
464 : std::uint16_t
465 101 : url_view_base::
466 : port_number() const noexcept
467 : {
468 101 : BOOST_ASSERT(
469 : has_port() ||
470 : pi_->port_number_ == 0);
471 101 : return pi_->port_number_;
472 : }
473 :
474 : //------------------------------------------------
475 : //
476 : // Path
477 : //
478 : //------------------------------------------------
479 :
480 : pct_string_view
481 1332 : url_view_base::
482 : encoded_path() const noexcept
483 : {
484 1332 : return pi_->pct_get(id_path);
485 : }
486 :
487 : segments_view
488 46 : url_view_base::
489 : segments() const noexcept
490 : {
491 46 : return {detail::path_ref(*pi_)};
492 : }
493 :
494 : segments_encoded_view
495 675 : url_view_base::
496 : encoded_segments() const noexcept
497 : {
498 : return segments_encoded_view(
499 675 : detail::path_ref(*pi_));
500 : }
501 :
502 : //------------------------------------------------
503 : //
504 : // Query
505 : //
506 : //------------------------------------------------
507 :
508 : bool
509 758 : url_view_base::
510 : has_query() const noexcept
511 : {
512 758 : auto const n = pi_->len(
513 : id_query);
514 758 : if(n == 0)
515 618 : return false;
516 140 : BOOST_ASSERT(
517 : pi_->get(id_query).
518 : starts_with('?'));
519 140 : return true;
520 : }
521 :
522 : pct_string_view
523 289 : url_view_base::
524 : encoded_query() const noexcept
525 : {
526 289 : auto s = pi_->get(id_query);
527 289 : if(s.empty())
528 10 : return s;
529 279 : BOOST_ASSERT(
530 : s.starts_with('?'));
531 279 : return s.substr(1);
532 : }
533 :
534 : params_encoded_view
535 55 : url_view_base::
536 : encoded_params() const noexcept
537 : {
538 55 : return params_encoded_view(*pi_);
539 : }
540 :
541 : params_view
542 62 : url_view_base::
543 : params() const noexcept
544 : {
545 : return params_view(
546 62 : *pi_,
547 : encoding_opts{
548 62 : true,false,false});
549 : }
550 :
551 : params_view
552 0 : url_view_base::
553 : params(encoding_opts opt) const noexcept
554 : {
555 0 : return params_view(*pi_, opt);
556 : }
557 :
558 : //------------------------------------------------
559 : //
560 : // Fragment
561 : //
562 : //------------------------------------------------
563 :
564 : bool
565 652 : url_view_base::
566 : has_fragment() const noexcept
567 : {
568 652 : auto const n = pi_->len(id_frag);
569 652 : if(n == 0)
570 527 : return false;
571 125 : BOOST_ASSERT(
572 : pi_->get(id_frag).
573 : starts_with('#'));
574 125 : return true;
575 : }
576 :
577 : pct_string_view
578 155 : url_view_base::
579 : encoded_fragment() const noexcept
580 : {
581 155 : auto s = pi_->get(id_frag);
582 155 : if(! s.empty())
583 : {
584 153 : BOOST_ASSERT(
585 : s.starts_with('#'));
586 153 : s.remove_prefix(1);
587 : }
588 155 : return make_pct_string_view_unsafe(
589 : s.data(),
590 : s.size(),
591 310 : pi_->decoded_[id_frag]);
592 : }
593 :
594 : //------------------------------------------------
595 : //
596 : // Compound Fields
597 : //
598 : //------------------------------------------------
599 :
600 : pct_string_view
601 120 : url_view_base::
602 : encoded_host_and_port() const noexcept
603 : {
604 120 : return pi_->pct_get(id_host, id_path);
605 : }
606 :
607 : pct_string_view
608 16 : url_view_base::
609 : encoded_origin() const noexcept
610 : {
611 16 : if(pi_->len(id_user) < 2)
612 14 : return {};
613 2 : return pi_->get(id_scheme, id_path);
614 : }
615 :
616 : pct_string_view
617 1 : url_view_base::
618 : encoded_resource() const noexcept
619 : {
620 1 : auto n =
621 1 : pi_->decoded_[id_path] +
622 1 : pi_->decoded_[id_query] +
623 1 : pi_->decoded_[id_frag];
624 1 : if(has_query())
625 1 : ++n;
626 1 : if(has_fragment())
627 1 : ++n;
628 1 : BOOST_ASSERT(pct_string_view(
629 : pi_->get(id_path, id_end)
630 : ).decoded_size() == n);
631 1 : auto s = pi_->get(id_path, id_end);
632 1 : return make_pct_string_view_unsafe(
633 1 : s.data(), s.size(), n);
634 : }
635 :
636 : pct_string_view
637 2 : url_view_base::
638 : encoded_target() const noexcept
639 : {
640 2 : auto n =
641 2 : pi_->decoded_[id_path] +
642 2 : pi_->decoded_[id_query];
643 2 : if(has_query())
644 1 : ++n;
645 2 : BOOST_ASSERT(pct_string_view(
646 : pi_->get(id_path, id_frag)
647 : ).decoded_size() == n);
648 2 : auto s = pi_->get(id_path, id_frag);
649 2 : return make_pct_string_view_unsafe(
650 2 : s.data(), s.size(), n);
651 : }
652 :
653 : //------------------------------------------------
654 : //
655 : // Comparisons
656 : //
657 : //------------------------------------------------
658 :
659 : int
660 284 : url_view_base::
661 : compare(const url_view_base& other) const noexcept
662 : {
663 : int comp =
664 284 : static_cast<int>(has_scheme()) -
665 284 : static_cast<int>(other.has_scheme());
666 284 : if ( comp != 0 )
667 0 : return comp;
668 :
669 284 : if (has_scheme())
670 : {
671 204 : comp = detail::ci_compare(
672 : scheme(),
673 : other.scheme());
674 204 : if ( comp != 0 )
675 14 : return comp;
676 : }
677 :
678 270 : comp =
679 270 : static_cast<int>(has_authority()) -
680 270 : static_cast<int>(other.has_authority());
681 270 : if ( comp != 0 )
682 0 : return comp;
683 :
684 270 : if (has_authority())
685 : {
686 190 : comp = authority().compare(other.authority());
687 190 : if ( comp != 0 )
688 89 : return comp;
689 : }
690 :
691 181 : comp = detail::segments_compare(
692 : encoded_segments(),
693 : other.encoded_segments());
694 181 : if ( comp != 0 )
695 43 : return comp;
696 :
697 138 : comp =
698 138 : static_cast<int>(has_query()) -
699 138 : static_cast<int>(other.has_query());
700 138 : if ( comp != 0 )
701 0 : return comp;
702 :
703 138 : if (has_query())
704 : {
705 48 : comp = detail::compare_encoded_query(
706 24 : encoded_query(),
707 24 : other.encoded_query());
708 24 : if ( comp != 0 )
709 19 : return comp;
710 : }
711 :
712 119 : comp =
713 119 : static_cast<int>(has_fragment()) -
714 119 : static_cast<int>(other.has_fragment());
715 119 : if ( comp != 0 )
716 0 : return comp;
717 :
718 119 : if (has_fragment())
719 : {
720 44 : comp = detail::compare_encoded(
721 22 : encoded_fragment(),
722 22 : other.encoded_fragment());
723 22 : if ( comp != 0 )
724 21 : return comp;
725 : }
726 :
727 98 : return 0;
728 : }
729 :
730 : } // urls
731 : } // boost
732 :
|