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