From 25e7b44246a12aa190927f67784dfd4f20d93ca2 Mon Sep 17 00:00:00 2001 From: alandefreitas Date: Mon, 8 Aug 2022 23:20:27 -0300 Subject: [PATCH] improve docs fix #339 --- doc/AuthorityDiagram.odg | Bin 0 -> 18607 bytes doc/PartsDiagram.odg | Bin 23399 -> 35917 bytes doc/images/AuthorityDiagram.svg | 160 ++++ doc/images/PartsDiagram.svg | 16 +- doc/qbk/0.main.qbk | 40 +- doc/qbk/1.0.overview.qbk | 74 +- .../{3.0.urls.qbk => 1.1.nomenclature.qbk} | 2 +- doc/qbk/2.0.quicklook.qbk | 70 +- doc/qbk/3.0.containers.qbk | 71 ++ doc/qbk/3.1.parsing.qbk | 134 +++ doc/qbk/3.2.scheme.qbk | 142 ++++ doc/qbk/3.3.authority.qbk | 178 ++++ doc/qbk/3.4.path.qbk | 92 +++ doc/qbk/3.5.query.qbk | 116 +++ doc/qbk/3.6.fragment.qbk | 43 + doc/qbk/{6.0.grammar.qbk => 4.0.grammar.qbk} | 10 +- doc/qbk/4.1.parsing.qbk | 278 ------- doc/qbk/{6.1.rules.qbk => 4.1.rules.qbk} | 0 doc/qbk/{6.2.charset.qbk => 4.2.charset.qbk} | 0 doc/qbk/4.2.modifying.qbk | 132 --- ....3.combinators.qbk => 4.3.combinators.qbk} | 0 doc/qbk/{6.4.range.qbk => 4.4.range.qbk} | 0 doc/qbk/{6.5.rfc3986.qbk => 4.5.rfc3986.qbk} | 0 .../{7.0.concepts.qbk => 5.0.concepts.qbk} | 6 +- doc/qbk/{7.1.CharSet.qbk => 5.1.CharSet.qbk} | 0 doc/qbk/5.1.scheme.qbk | 267 ------ ...utableString.qbk => 5.2.MutableString.qbk} | 0 doc/qbk/5.2.authority.qbk | 580 ------------- doc/qbk/{7.1.Rule.qbk => 5.3.Rule.qbk} | 0 doc/qbk/5.3.path.qbk | 396 --------- doc/qbk/5.4.query.qbk | 312 ------- doc/qbk/5.5.fragment.qbk | 194 ----- .../{8.0.examples.qbk => 6.0.examples.qbk} | 0 .../{2.1.HelpCard.qbk => 7.1.HelpCard.qbk} | 2 +- example/magnet/magnet.cpp | 36 +- include/boost/url/url.hpp | 2 +- test/unit/CMakeLists.txt | 11 +- test/unit/snippets.cpp | 767 ++++++++++++------ 38 files changed, 1563 insertions(+), 2568 deletions(-) create mode 100644 doc/AuthorityDiagram.odg create mode 100644 doc/images/AuthorityDiagram.svg rename doc/qbk/{3.0.urls.qbk => 1.1.nomenclature.qbk} (99%) create mode 100644 doc/qbk/3.0.containers.qbk create mode 100644 doc/qbk/3.1.parsing.qbk create mode 100644 doc/qbk/3.2.scheme.qbk create mode 100644 doc/qbk/3.3.authority.qbk create mode 100644 doc/qbk/3.4.path.qbk create mode 100644 doc/qbk/3.5.query.qbk create mode 100644 doc/qbk/3.6.fragment.qbk rename doc/qbk/{6.0.grammar.qbk => 4.0.grammar.qbk} (94%) delete mode 100644 doc/qbk/4.1.parsing.qbk rename doc/qbk/{6.1.rules.qbk => 4.1.rules.qbk} (100%) rename doc/qbk/{6.2.charset.qbk => 4.2.charset.qbk} (100%) delete mode 100644 doc/qbk/4.2.modifying.qbk rename doc/qbk/{6.3.combinators.qbk => 4.3.combinators.qbk} (100%) rename doc/qbk/{6.4.range.qbk => 4.4.range.qbk} (100%) rename doc/qbk/{6.5.rfc3986.qbk => 4.5.rfc3986.qbk} (100%) rename doc/qbk/{7.0.concepts.qbk => 5.0.concepts.qbk} (82%) rename doc/qbk/{7.1.CharSet.qbk => 5.1.CharSet.qbk} (100%) delete mode 100644 doc/qbk/5.1.scheme.qbk rename doc/qbk/{7.1.MutableString.qbk => 5.2.MutableString.qbk} (100%) delete mode 100644 doc/qbk/5.2.authority.qbk rename doc/qbk/{7.1.Rule.qbk => 5.3.Rule.qbk} (100%) delete mode 100644 doc/qbk/5.3.path.qbk delete mode 100644 doc/qbk/5.4.query.qbk delete mode 100644 doc/qbk/5.5.fragment.qbk rename doc/qbk/{8.0.examples.qbk => 6.0.examples.qbk} (100%) rename doc/qbk/{2.1.HelpCard.qbk => 7.1.HelpCard.qbk} (91%) diff --git a/doc/AuthorityDiagram.odg b/doc/AuthorityDiagram.odg new file mode 100644 index 0000000000000000000000000000000000000000..4c7f783c409e1d0188a20aa67c05a2a56092b401 GIT binary patch literal 18607 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 delta 21846 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 diff --git a/doc/images/AuthorityDiagram.svg b/doc/images/AuthorityDiagram.svg new file mode 100644 index 00000000..3a936b55 --- /dev/null +++ b/doc/images/AuthorityDiagram.svg @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [ user ] + + + + + + + + [ “:” port ] + + + + + + + + host + + + + + + + + [ “:” password ] + + + + + + userinfo + + + + + + authority + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/images/PartsDiagram.svg b/doc/images/PartsDiagram.svg index 69014c57..f0319687 100644 --- a/doc/images/PartsDiagram.svg +++ b/doc/images/PartsDiagram.svg @@ -1,12 +1,12 @@ - + - + - + @@ -155,7 +155,7 @@ - + [ scheme”:” ] @@ -163,7 +163,7 @@ - + [ “#” fragment ] @@ -171,7 +171,7 @@ - + [ “?” query ] @@ -179,7 +179,7 @@ - + path @@ -187,7 +187,7 @@ - + [ “//” authority ] diff --git a/doc/qbk/0.main.qbk b/doc/qbk/0.main.qbk index 31543a0d..2d754949 100644 --- a/doc/qbk/0.main.qbk +++ b/doc/qbk/0.main.qbk @@ -36,6 +36,7 @@ [def __rfc1738__ [@https://tools.ietf.org/html/rfc1738 rfc1738]] [/ URIs, URLs, URNs: Clarifications and Recommendations ] [def __rfc3305__ [@https://tools.ietf.org/html/rfc3305 rfc3305]] +[def __rfc7230__ [@https://tools.ietf.org/html/rfc7230 rfc7230]] [/ Named Requirements ] @@ -61,6 +62,7 @@ [def __std_string__ [@https://en.cppreference.com/w/cpp/string/basic_string `std::string`]] [def __std_tuple__ [@https://en.cppreference.com/w/cpp/utility/tuple `std::tuple`]] [def __std_get__ [@https://en.cppreference.com/w/cpp/utility/tuple/get `std::get`]] +[def __std_string_view__ [@https://en.cppreference.com/w/cpp/string/basic_string_view `std::string_view`]] [def __absolute_uri_rule__ [link url.ref.boost__urls__absolute_uri_rule `absolute_uri_rule`]] [def __authority_rule__ [link url.ref.boost__urls__authority_rule `authority_rule`]] @@ -71,12 +73,18 @@ [def __optional__ [link url.ref.boost__urls__optional `optional`]] [def __origin_form_rule__ [link url.ref.boost__urls__origin_form_rule `origin_form_rule`]] [def __parse_uri__ [link url.ref.boost__urls__parse_uri `parse_uri`]] +[def __params__ [link url.ref.boost__urls__params `params`]] +[def __params_view__ [link url.ref.boost__urls__params_view `params_view`]] +[def __params_encoded__ [link url.ref.boost__urls__params_encoded `params_encoded`]] +[def __params_encoded_view__ [link url.ref.boost__urls__params_encoded_view `encoded_params_view`]] [def __pchars__ [link url.ref.boost__urls__pchars `pchars`]] [def __pct_encoded_view__ [link url.ref.boost__urls__pct_encoded_view `pct_encoded_view`]] [def __pct_encoded_rule__ [link url.ref.boost__urls__pct_encoded_rule `pct_encoded_rule`]] [def __query_param_view__ [link url.ref.boost__urls__query_param_view `query_param_view`]] [def __result__ [link url.ref.boost__urls__result `result`]] +[def __segments__ [link url.ref.boost__urls__segments `segments`]] [def __segments_view__ [link url.ref.boost__urls__segments_view `segments_view`]] +[def __segments_encoded__ [link url.ref.boost__urls__segments_encoded `segments_encoded`]] [def __segments_encoded_view__ [link url.ref.boost__urls__segments_encoded_view `segments_encoded_view`]] [def __static_url__ [link url.ref.boost__urls__static_url `static_url`]] [def __string_view__ [link url.ref.boost__urls__string_view `string_view`]] @@ -121,37 +129,19 @@ [include 1.0.overview.qbk] [include 2.0.quicklook.qbk] -[include 2.1.HelpCard.qbk] -[include 3.0.urls.qbk] - -[section Containers] -[include 4.1.parsing.qbk] -[include 4.2.modifying.qbk] -[endsect] - -[section URL Components] -[heading Generic Syntax] -The five parts of the ['generic URI syntax] are shown below: -[$url/images/PartsDiagram.svg] - -[include 5.1.scheme.qbk] -[include 5.2.authority.qbk] -[include 5.3.path.qbk] -[include 5.4.query.qbk] -[include 5.5.fragment.qbk] -[endsect] - -[include 6.0.grammar.qbk] -[include 7.0.concepts.qbk] -[include 8.0.examples.qbk] +[include 3.0.containers.qbk] +[include 4.0.grammar.qbk] +[include 5.0.concepts.qbk] +[include 6.0.examples.qbk] [section:ref Reference] [xinclude quickref.xml] [block''''''] [include reference.qbk] [block''''''] + +[include 7.1.HelpCard.qbk] + [endsect] - - [xinclude index.xml] diff --git a/doc/qbk/1.0.overview.qbk b/doc/qbk/1.0.overview.qbk index 28415109..a2898674 100644 --- a/doc/qbk/1.0.overview.qbk +++ b/doc/qbk/1.0.overview.qbk @@ -25,14 +25,12 @@ this is a valid URL which satisfies the ['absolute-URI] grammar: https://www.example.com/path/to/file.txt?userid=1001&page=2&results=full ``` -[/ Introduction to URL ] - This library understands the various grammars related to URLs and provides functionalities for: -* validating and parsing of strings, -* manipulation of URL strings, and -* algorithms operating on URLs such as normalization and resolution. +* Validating and parsing of strings, +* Manipulation of URL strings, and +* Algorithms such as normalization and resolution. [heading Features] @@ -43,9 +41,9 @@ case where the inputs come from untrusted sources. Interfaces are provided for using error codes instead of exceptions as needed, and all algorithms provide a mechanism for avoiding memory allocations entirely -if desired. Another feature of the library is that all container mutations leave the -URL in a valid state. Code which uses Boost.URL will be easy to read, -flexible, and performant. +if desired. Another feature of the library is that all container mutations +leave the URL in a valid state. Code which uses Boost.URL will be easy to +read, flexible, and performant. Boost.URL offers these features: @@ -56,8 +54,8 @@ Boost.URL offers these features: * Allocator control, including avoiding allocation entirely * Optional header-only, without linking to a library * Supports `-fno-exceptions`, detected automatically -* Link to a built static or dynamic Boost library, or use [link header-only header-only] -* Work well on [link embedded-devices embedded devices]. +* Link to a built static or dynamic Boost library, or use header-only +* Work well on embedded devices [/-----------------------------------------------------------------------------] @@ -69,23 +67,21 @@ The library requires Boost 1.80.0 and a compiler supporting at least C++11. Boost [*1.80.0] or higher is a requirement for installation of this library proposal. ] -Aliases for standard types, such as __string_view__, use their Boost equivalents. +Aliases for standard types, such as __error_code__ or __string_view__, +use their Boost equivalents. -[#header-only] [heading Header-Only] -To use the library as header-only; that is, to eliminate the requirement to -link a program to a static or dynamic Boost.URL library, simply -place the following line in exactly one new or existing source -file in your project. +To use the library as header-only; that is, to eliminate the requirement +to link a program to a static or dynamic Boost.URL library, simply place +the following line in [*exactly one] source file in your project. [c++] ``` #include ``` -[#cmake] -[heading CMake Integration] +[heading CMake] The library can work both as a module of the Boost super-project or as an independent library that depends on Boost 1.78.0. In both cases, @@ -111,7 +107,7 @@ is designed to work without exceptions if desired. [/-----------------------------------------------------------------------------] -[section Supported Compilers] +[heading Tested Compilers] Boost.URL has been tested with the following compilers: @@ -119,26 +115,52 @@ Boost.URL has been tested with the following compilers: * gcc: 4.8, 4.9, 5, 6, 7, 8, 9, 10, 11 * msvc: 14.0, 14.1, 14.2, 14.3 -[h3 Quality Assurance] +and these architectures: x86, x64, ARM64, S390x. + +[heading Quality Assurance] The development infrastructure for the library includes these per-commit analyses: * Coverage reports -* Compilation and tests on Drone.io, Azure Pipelines, Appveyor +* Compilation and tests on Drone.io and GitHub Actions * Fuzzing using clang-llvm and machine learning +* Regular code audits for security + +[/-----------------------------------------------------------------------------] + +[section Nomenclature] + +Various names have been used historically to refer to different +flavors of resource identifiers, including ['URI], ['URL], +and ['URN]. Over time, the distinction between URIs and URLs +has disappeared when discussed in technical documents and +informal works. In this library we use the term [*URL] to +refer to all strings which are valid according to the +top-level grammar rules found in __rfc3986__. [endsect] +[heading ABNF] + +This documentation uses the Augmented +[@https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form Backus-Naur Form] +(ABNF) notation of +[@https://datatracker.ietf.org/doc/html/rfc5234 rfc5234] +to specify particular grammars used by algorithms and containers. While +a complete understanding of the notation is not a requirement for using +the library, it may help for an understanding of how valid components of +URLs are defined. In particular, this will be of interest to users who +wish to compose parsing algorithms using the combinators provided by +the library. + [/-----------------------------------------------------------------------------] [section:security_review Security Review (Bishop Fox)] To be announced [endsect] -[/-----------------------------------------------------------------------------] - -[h1 Acknowledgments] +[heading Acknowledgments] This library wouldn't be where it is today without the help of [@https://github.com/pdimov Peter Dimov] @@ -146,8 +168,4 @@ for design advice and general assistance. [/-----------------------------------------------------------------------------] -[h1 Documentation] - -Visit [@https://master.url.cpp.al/ https://master.url.cpp.al/] for complete documentation. - [endsect] diff --git a/doc/qbk/3.0.urls.qbk b/doc/qbk/1.1.nomenclature.qbk similarity index 99% rename from doc/qbk/3.0.urls.qbk rename to doc/qbk/1.1.nomenclature.qbk index 00613360..48862926 100644 --- a/doc/qbk/3.0.urls.qbk +++ b/doc/qbk/1.1.nomenclature.qbk @@ -7,7 +7,7 @@ Official repository: https://github.com/CPPAlliance/url ] -[section URLs, URIs, and URNs] +[section Nomenclature] Uniform Resource Locators (URL - __rfc1738__), also informally called "web addresses", are able to describe the name and location of a resource. A URL scheme, such as `http`, diff --git a/doc/qbk/2.0.quicklook.qbk b/doc/qbk/2.0.quicklook.qbk index f9590a07..32384efd 100644 --- a/doc/qbk/2.0.quicklook.qbk +++ b/doc/qbk/2.0.quicklook.qbk @@ -83,8 +83,7 @@ You can parse the string by calling this function: The function __parse_uri__ returns an object of type `__result__<__url_view__>` which is a container resembling a variant that holds either an error or an object. -A number of [link section.url_view functions] are available to parse different -types of URL. +A number of functions are available to parse different types of URL. We can immediately call `result::value` to obtain a __url_view__. @@ -121,27 +120,7 @@ also be used to check if the string has been parsed without errors. Accessing the parts of the URL is easy: -[table [[Code][Output]] [[ -[c++] [snippet_accessing_1] -][ -[teletype] -``` - - - url : https://user:pass@example.com:443/path/to/my%2dfile.txt?id=42&name=John%20Doe+Jingleheimer%2DSchmidt#page%20anchor - scheme : https - authority : user:pass@example.com:443 - userinfo : user:pass - user : user - password : pass - host : example.com - port : 443 - path : /path/to/my-file.txt - query : id=42&name=John Doe Jingleheimer-Schmidt - fragment : page anchor -``` -]]] URL paths can be further divided into path segments with the function [link url.ref.boost__urls__url_view.segments `url_view::segments`]. @@ -173,49 +152,13 @@ By simply referencing the relevant portion of the URL string, its components can represent percent-decoded strings without any need to allocate memory. -These functions might also return empty strings both for empty and absent -components. +These functions might also return empty strings -[table [[Code][Output]] [[ -[c++] [snippet_accessing_2a] -][ -[teletype] -``` - fragment 1 : -``` -]][[ -[c++] + +for both for empty and absent components + [snippet_accessing_2b] -][ -[teletype] -``` - fragment 2 : -``` -]]] - -To differentiate between empty and absent components, we can use -functions such as [link url.ref.boost__urls__url_view.has_fragment `has_fragment`]: - -[table [[Code][Output]] [[ -[c++] -[snippet_accessing_3a] -][ -[teletype] -``` - has fragment 1 : 0 - fragment 1 : -``` -]][[ -[c++] -[snippet_accessing_3b] -][ -[teletype] -``` - has fragment 2 : 1 - fragment 2 : -``` -]]] Many components do not have corresponding functions such as [link url.ref.boost__urls__url_view.has_authority `has_authority`] @@ -299,6 +242,9 @@ in a web server: [c++] [snippet_decoding_4a] +This allows us to easily match files in the document +root directory of a web server: + [c++] [snippet_decoding_4b] diff --git a/doc/qbk/3.0.containers.qbk b/doc/qbk/3.0.containers.qbk new file mode 100644 index 00000000..ef23f527 --- /dev/null +++ b/doc/qbk/3.0.containers.qbk @@ -0,0 +1,71 @@ +[/ + Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + Official repository: https://github.com/CPPAlliance/url +] + +[section Containers] + +All URLs conform to the ['generic syntax]. Every URL is +either ['hierarchical], or ['opaque], with the hierarchical +URLs further distinguished by whether they are ['relative] +or ['absolute]. Here are some examples of URLs and their +identification: + +[teletype] +[table URL Examples [ + [URL] + [Notes] +][ + [`https://www.boost.org/index.html`] + [Hierarchical URL with `https` protocol. Resource in the HTTP protocol.] +][ + [`ftp://host.dom/etc/motd`] + [Hierarchical URL with `ftp` scheme. Resource in the FTP protocol.] +][ + [`urn:isbn:045145052`] + [Opaque URL with `urn` scheme. Identifies `isbn` resource.] +][ + [`mailto:person@example.com`] + [Opaque URL with `mailto` scheme. Identifies e-mail address.] +][ + [`index.html`] + [URL reference. Missing scheme and host.] +][ + [`www.boost.org`] + [A Protocol-Relative Link (PRL). Not a URL.] +]] + +This library provides the following containers, which +are capable of storing any possible URL: + +* __url__: A modifiable container for a URL. +* __url_view__: A non-owning reference to a valid URL. +* __static_url__: A URL with fixed-capacity storage. + +These containers maintain a useful invariant: they +always contain a valid URL. + +[heading Generic Syntax] + +This diagram shows the generic syntax which all URLs +conform to: + +[$url/images/PartsDiagram.svg] + +In the sections that follow we discuss the main parts +of the URL, parsing strings into the provided containers, +modifying parts of the container while preserving invariants, +and how to invoke common algorithms used with URLs. + +[include 3.1.parsing.qbk] +[include 3.2.scheme.qbk] +[include 3.3.authority.qbk] +[include 3.4.path.qbk] +[include 3.5.query.qbk] +[include 3.6.fragment.qbk] + +[endsect] diff --git a/doc/qbk/3.1.parsing.qbk b/doc/qbk/3.1.parsing.qbk new file mode 100644 index 00000000..610b5e6b --- /dev/null +++ b/doc/qbk/3.1.parsing.qbk @@ -0,0 +1,134 @@ +[/ + Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) + Copyright (c) 2022 Alan de Freitas (vinnie.falco@gmail.com) + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + Official repository: https://github.com/CPPAlliance/url +] + +[section Parsing] + +Algorithms which parse URLs return a view which references the +underlying character buffer without taking ownership, avoiding +memory allocations and copies. The following example parses a +string literal containing a +[@https://datatracker.ietf.org/doc/html/rfc3986#section-3 ['URI]]: + +[c++] +[snippet_parsing_url_1] + +The function returns a __result__ which holds a __url_view__ +if the string is a valid URL. Otherwise it holds an __error_code__. +It is impossible to construct a __url_view__ which refers to an +invalid URL. + +[warning + The caller is responsible for ensuring that the lifetime + of the character buffer extends until it is no longer + referenced by the view. These are the same semantics + as that of __std_string_view__. +] + +When a view is directly constructed from a __string_view__ it parses it +according to the +[@https://datatracker.ietf.org/doc/html/rfc3986#section-4.1 ['URI-reference]] +grammar, throwing an exception upon failure: + +[snippet_parsing_url_1b] + +The URL is stored in its serialized form. Therefore, it can +always be easily output, sent, or embedded as part of a +protocol: + +[snippet_parsing_url_1bb] + +A __url__ is an allocating container which owns its character buffer. +Upon construction from __url_view__, it allocates dynamic storage +to hold a copy of the string. + +[snippet_parsing_url_1bc] + +A __static_url__ is a container which owns its character buffer for +a URL whose maximum size is known. Upon construction from +__url_view__, it does not perform any dynamic memory allocations. + +[snippet_parsing_url_1bd] + +[heading Result Type] + +In many places, functions in the library have a return type which uses the +__result__ alias template. This class allows the parsing algorithms to +report errors without referring to exceptions. + +The functions `result::has_value` and `result::has_error` can be used to +check if the result contains an error. + +[snippet_parsing_url_1] + +This ensures `result::value` will not throw an error. In contexts where +it is acceptable to throw errors, `result::value` can be used directly. + +Check the reference for __result__ for a synopsis of the type. For complete +information please consult the full +[@boost:/libs/system/doc/html/system.html#ref_resultt_e `result`] +documentation in [@boost:/libs/system/doc/html/system.html Boost.System]. + +[heading URL types] + +Parsing functions are functions which start with the word "parse", +and are suffixed with the name of the grammar applied to the string. +For example, the function +[link url.ref.boost__urls__parse_relative_ref `parse_relative_ref`] +parses the grammar +found in +[@https://datatracker.ietf.org/doc/html/rfc3986#section-4.2 4.2.Relative Reference (rfc3986)]. +A URL string can also be parsed using one of the following functions: + +[table Parsing Functions [ + [Function] + [Grammar] + [Example] +][ + [[link url.ref.boost__urls__parse_uri `parse_uri`]] + [[@https://datatracker.ietf.org/doc/html/rfc3986#section-3 ['URI]]] + [[teletype]`http://www.boost.org/index.html?field=value#downloads`] +][ + [[link url.ref.boost__urls__parse_absolute_uri `parse_absolute_uri`]] + [[@https://datatracker.ietf.org/doc/html/rfc3986#section-4.3 ['absolute-URI]]] + [[teletype]`http://www.boost.org/index.html?field=value` (Does not support fragment)] +][ + [[link url.ref.boost__urls__parse_relative_ref `parse_relative_ref`]] + [[@https://datatracker.ietf.org/doc/html/rfc3986#section-4.2 ['relative-ref]]] + [[teletype]`//www.boost.org/index.html?field=value#downloads`] +][ + [[link url.ref.boost__urls__parse_uri_reference `parse_uri_reference`]] + [[@https://datatracker.ietf.org/doc/html/rfc3986#section-4.1 ['URI-reference]]] + [[teletype]`http://www.boost.org/index.html` (Any `URI` or `relative-ref`)] +][ + [[link url.ref.boost__urls__parse_origin_form `parse_origin_form`]] + [[@https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.1 ['origin-form]]] + [[teletype]`/index.html?field=value` (Used in HTTP requests (__rfc7230__))] +]] + +The functions [link url.ref.boost__urls__parse_uri `parse_uri`] and +[link url.ref.boost__urls__parse_absolute_uri `parse_absolute_uri`] +are only valid if the URL contains the scheme component. +On the other hand, relative URI references are not required to +have a scheme. For instance, we can parse a relative URL that +only contains a path as + +[snippet_parsing_url_1c] + +These relative references can be combined with base absolute URLs: + +[snippet_parsing_url_1d] + +The [link url.ref.helpcard Help Card] provides a visual summary +of all of the member functions and types used by the library +with URL containers. In the sections that follow we discuss +each of the five major parts of the URL and how they may be +inspected and modified. + +[endsect] diff --git a/doc/qbk/3.2.scheme.qbk b/doc/qbk/3.2.scheme.qbk new file mode 100644 index 00000000..667a0d28 --- /dev/null +++ b/doc/qbk/3.2.scheme.qbk @@ -0,0 +1,142 @@ +[/ + Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + Official repository: https://github.com/CPPAlliance/url +] + +[/-----------------------------------------------------------------------------] + +[section Scheme] + +Every URL has a scheme that determines how the rest of +the URL should be interpreted, and each scheme might have +its own rules, such as mandatory components and values. +For instance, the term web address is often used informally +to describe URLs with the `https` scheme, whose semantics +are defined by +[@https://datatracker.ietf.org/doc/html/rfc7230#section-2.7.1 rfc7230]. + +[snippet_components_2a] + +In relative URL references, the scheme is implied by the context. +For instance, HTTP GET request identify resources through relative +references: + +[teletype] +``` + GET /pub/WWW/TheProject.html HTTP/1.1 +``` + +The scheme is implied as `http` or `https`, according to the +protocol being used. + +One of the conventions of the HTTP scheme is that when the port 80 is +implicitly assumed when it is not provided. Such conventions are not +part of the URL protocol. + +The scheme is the top-level component defining the semantics of +the URL. It usually represents a protocol, such as HTTP +or FTP. In these protocols, the path describes a resource +and the host describes how to access it. + +[snippet_components_2b] + +A scheme must start with a letter, and may contain only letters, +digits, plus and minus signs, and periods. It is always followed +by a colon when it appears in a URL. + +Schemes do not necessarily represent protocols. The `file` +scheme is used to identify files from within one's own +computer. + +[teletype] +``` + file:///usr/local/bin/ +``` + +Other schemes use opaque paths that only identify resources. +They usually represent resources through the path. The `mailto` +scheme is used to represent email addresses with the path +component. It is intented to produce hyperlinks that +allow users to send an email to specific addresses: + +[snippet_components_2c] + +The `urn` scheme uses an opaque path to unique identifiers +to a resource. A typical URN namespace is `isbn`, for +International Standard Book Numbers. A book can be identified +as: + +[snippet_components_2d] + +Magnet links, which use the `magnet` scheme, identify files +by their cryptographic hash value. They have empty paths +and use queries to describe the resource. The scheme +allows more fields related to the file, such as a +display name, keywords, and file sources. + +[snippet_components_2e] + +A number of +[@https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml official URL schemes] +registered with the Internet Assigned Numbers Authority (IANA) have their +own grammars. + +The function [link url.ref.boost__urls__url_view.scheme `scheme`] can +be used to obtain the scheme from a __url_view__: + +[snippet_parsing_scheme_1] + +If the URL has no scheme, this function returns an empty string. To check whether +a URL contains a scheme the function +[link url.ref.boost__urls__url_view.has_scheme `url_view::has_scheme`] might be used. + +The library also defines an enumeration of values for some well-known scheme +identifiers: + +[c++] +[snippet_parsing_scheme_3] + +These may be used instead of their corresponding strings: + +[table Scheme IDs [ + [ID] + [Description] +][ + [[link url.ref.boost__urls__scheme `scheme::ftp`]] + [File Transfer Protocol ("FTP")] +][ + [[link url.ref.boost__urls__scheme `scheme::file`]] + [File URI Scheme] +][ + [[link url.ref.boost__urls__scheme `scheme::http`]] + [Hypertext Transfer Protocol] +][ + [[link url.ref.boost__urls__scheme `scheme::https`]] + [Secure Hypertext Transfer Protocol] +][ + [[link url.ref.boost__urls__scheme `scheme::ws`]] + [WebSocket Protocol] +][ + [[link url.ref.boost__urls__scheme `scheme::wss`]] + [Secure WebSocket Protocol] +]] + +[note + None of these functions throw exceptions. If the URL has no scheme, + [link url.ref.boost__urls__url_view.scheme `scheme`] returns an empty + string. If the function + [link url.ref.boost__urls__url_view.scheme_id `scheme_id`] identifies a valid + but unknown scheme, the value [link url.ref.boost__urls__scheme `scheme::unknown`] + is returned. +] + +[note + Since schemes may contain only letters, digits, plus signs, minus signs, and periods, + there is no distinction in the functions for encoded and decoded schemes. +] + +[endsect] diff --git a/doc/qbk/3.3.authority.qbk b/doc/qbk/3.3.authority.qbk new file mode 100644 index 00000000..55f7d6cd --- /dev/null +++ b/doc/qbk/3.3.authority.qbk @@ -0,0 +1,178 @@ +[/ + Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + Official repository: https://github.com/CPPAlliance/url +] + +[/-----------------------------------------------------------------------------] + +[section Authority] + +The authority determines how a resource can be accessed. +It divides into three subcomponents: + +* The mandatory `host` can be a registered name or an IP address. +* The `port` number preceded by a colon, which can be left out. +* An optional `userinfo` with a username and password. The password can be omitted. + +[$url/images/AuthorityDiagram.svg] + +The function [link url.ref.boost__urls__url_view.authority `authority`] can +be used to obtain the __authority_view__ from a __url_view__: + +[snippet_parsing_authority_3a] + +Notice that [link url.ref.boost__urls__url_view.authority `authority`] +does not return a __pct_encoded_view__. The reason is any decoded character +`/` could make it ambiguous with the path component. The authority +is represented through an __authority_view__, a read-only container +to a non-owning character buffer containing a valid authority. + +An __authority_view__ has functions for obtaining its subcomponents: + +[snippet_parsing_authority_3b] + +These functions do not throw. If the URL has no authority, +[link url.ref.boost__urls__url_view.authority `authority`] +returns an empty __authority_view__. +The function +[link url.ref.boost__urls__url_view.has_authority `has_authority`] +can be used to check whether this empty authority means +there is no authority or an empty authority in the URL. + +In contexts where an authority can appear by itself, an __authority_view__ +can be constructed directly from a string. +For instance, the grammar for the +[@https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.1 ['request-target]] +of an `HTTP/1 CONNECT` request uses +[@https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.3 ['authority-form]]. +This is what such a request looks like: + +[teletype] +``` + CONNECT www.example.com:80 HTTP/1.1 +``` + +In that case, we have an authority that cannot be parsed directly +with __parse_uri__ as a URL. Instead, we can use the analogous function +[link url.ref.boost__urls__parse_authority `parse_authority`] to +obtain an __authority_view__. + +[snippet_parsing_authority_12] + +The authority view provides the subset of observer member functions found in +__url_view__ which are relevant to the authority. However, when an authority +is parsed on its own, the leading double slashes ("//") are not present. + +Authority string with `userinfo` are also valid for +[link url.ref.boost__urls__parse_authority `parse_authority`]: + +[snippet_parsing_authority_13] + +[heading Host] + +The host subcomponent represents where resources +are located. The functions +[link url.ref.boost__urls__url_view.host `host`] +and [link url.ref.boost__urls__url_view.port `port`] +can be used to obtain the host from a __url_view__ +or __authority_view__: + +The host might be a registered name + +[snippet_parsing_authority_8] + +or an IP address + +[snippet_parsing_authority_9] + +Although this is not mandatory, note that the encoded host is rarely +different from its encoded counterpart. + +[snippet_parsing_authority_9b] + +Registered names usually need to be handled differently from IP addresses. +The function +[link url.ref.boost__urls__url_view.host_type `host_type`] +can be used to identify which type of host is described in the URL. + +[c++] +[snippet_parsing_authority_10] + +When the [link url.ref.boost__urls__url_view.host_type `host_type`] +matches an IP address, the functions +[link url.ref.boost__urls__url_view.ipv4_address `ipv4_address`], +[link url.ref.boost__urls__url_view.ipv6_address `ipv6_address`] +can be used to obtain the decoded addresses as integers. + +[note + Note that if an authority is present, the host is always + defined even if it is the empty string (corresponding + to a zero-length ['reg-name] in the BNF). + + [snippet_parsing_authority_10a] +] + +The authority component also influences how we should +interpret the URL path. If the authority is present, +the path component must either be empty or begin with +a slash. This is a common pattern where the path is +empty: + +[snippet_parsing_authority_10b] + +When both the authority and path exist, the path +must begin with a slash: + +[snippet_parsing_authority_10c] + +This rule also affects the path "`/`": + +[snippet_parsing_authority_10d] + +When there is no authority component, the path +cannot begin with an empty segment. This means +the path cannot begin with two slashes `//` to +avoid these characters being interpreted as the +beginning of the authority component. For +instance, consider the following valid URL: + +[snippet_parsing_authority_10d] + +Note that including a double slash would make the +path be interpreted as the authority: + +[snippet_parsing_authority_10f] + +[heading Userinfo] + +In complete authority components, we can also extract +the `userinfo` and `port` subcomponents. + +[snippet_parsing_authority_11a] + +When not treated as an opaque field, the optional +`userinfo` subcomponent consists of a user name +and an optional password. Analogous functions are +provided for the userinfo subcomponents. + +[snippet_parsing_authority_11b] + +Analogous to other observers, the functions +[link url.ref.boost__urls__url_view.has_userinfo `has_userinfo`] and +[link url.ref.boost__urls__url_view.has_password `has_password`] are provided +to differentiate empty components from absent components. +Note that there is no function `has_user`. The user component is available +whenever `userinfo` exists. + +[note Although the specification allows the format `username:password`, +the password component should be used with care. + +It is not recommended to transfer password data through URLs +unless this is an empty string indicating no password.] + + +[endsect] diff --git a/doc/qbk/3.4.path.qbk b/doc/qbk/3.4.path.qbk new file mode 100644 index 00000000..6527997e --- /dev/null +++ b/doc/qbk/3.4.path.qbk @@ -0,0 +1,92 @@ +[/ + Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + Official repository: https://github.com/CPPAlliance/url +] + +[/-----------------------------------------------------------------------------] + +[section Path] + +The path contains data, usually organized hierarchically, which is combined +with the [link section.query query] to identify a resource within the scope of +the scheme and authority. + +If the authority is present, the path needs to be empty or start with a +slash `/`. + +[snippet_parsing_path_use_case_2] + +A URL always contains a path, even if it is empty: + +[snippet_parsing_path_use_case_3] + +The function [link url.ref.boost__urls__url_view.path `path`] +treats this component as an opaque substring. This is useful +in non-hierarchical paths, such as URN identifiers: + +[snippet_parsing_path_3a] + +Most schemes interpret the path as a sequence of slash delimited ['segments]. +These segments can map to file system paths, which is useful for file servers, +but do not always need to imply this relationship. The library +provides container adaptors modeling ranges of individual path segments. +The URL below contains a path with three segments: + +[snippet_parsing_path_0] + +These segment view containers are lightweight references to +the underlying path that map every path to a unique +sequence of segments. + +[warning + Ownership of the string is not + transferred; the caller is responsible for ensuring that the + lifetime of the string extends until the container is destroyed. +] + +In contexts where a path can appear by itself, such as HTTP +requests, segment views may also be constructed directly from +strings. + +[snippet_parsing_path_9] + +The semantics of a path can be absolute or relative. +An absolute path in a URL begins with `/`: + +[snippet_modifying_path_1_2] + +The complete path segments `"."` and `".."` are intended +only for use within relative references +(__rfc3986__ sec. 4.1) and are removed as part of the +reference resolution process (__rfc3986__ sec. 5.2). +Normalizing an absolute URI resolves these dot-segments +(__rfc3986__ sec. 5.2.4). + +[snippet_modifying_path_3] + +Empty segments are also possible, resulting in consecutive slashes. + +[snippet_parsing_path_use_case_6] + +The grammar for URLs places some restrictions on where and +how certain delimiters may appear in the first path segment, +to prevent ambiguities between the scheme, host, and path. +For example, a relative-ref containing a path which is not +absolute, may not have a colon in the first segment. + +When the library detects that a modification to a URL would result +in an ambiguous configuration, it adjusts the result to +maintain the same semantics while avoiding the ambiguity: + +[snippet_modifying_path_9_10] + +This ensures the result is predictable when the user +changes something the path or any segment. + +[/-----------------------------------------------------------------------------] + +[endsect] diff --git a/doc/qbk/3.5.query.qbk b/doc/qbk/3.5.query.qbk new file mode 100644 index 00000000..9e3f55c4 --- /dev/null +++ b/doc/qbk/3.5.query.qbk @@ -0,0 +1,116 @@ +[/ + Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + Official repository: https://github.com/CPPAlliance/url +] + +[/-----------------------------------------------------------------------------] + +[#section.query] +[section:query Query] + +The URL query augments the information in the path to identify a resource +within the scope of the scheme and authority. Unlike the URL path, the +query string always contains non-hierarchical data. + +Although the field is opaque and there is no mandatory syntax for +interpreting queries, its strings are usually interpreted as key-value +parameters delimited by the '&' or ';' character. In contexts where a +query is interpreted as key/value pairs, it is called the +['query parameters], ['query params], or just [*params]. +In addition to interacting with the query as a single string, the +library provides container adaptors modeling ranges of individual +query parameters. + +The URL below contains the query "[teletype]`?id=409&name=Joe&individual`" +with the three parameters "[teletype]`id=409`", "[teletype]`name=Joe`", and +"[teletype]`individual`": + +[snippet_parsing_query_0] + +If the URL has no query, [link url.ref.boost__urls__url_view.query `query`] +returns an empty string. The function +[link url.ref.boost__urls__url_view.has_query `has_query`] +can be used to determine whether this empty string means there is +no query or an empty query string in the URL. + +[snippet_parsing_query_5] + +When using the query string as parameters, note that decoded +query strings might include ambiguous `&` and `=` characters. +In the following example, the decoded query implies there are +two query parameters while there is only one parameter whose +value includes a `&` character. + +[snippet_parsing_query_7] + +The reason the decoded variant of a query is still allowed is +because protocols are also allowed to interpret queries as +opaque strings, in which case the `&` character is not ambiguous. + +When the query string represents parameters, the decoded +parameters can be accessed safely through a parameter view. + +[snippet_parsing_query_1] + +Parameter views are lightweight references to the underlying path string. +Each parameter is represented as a structure with fields to refer to the +key and value. An extra field `has_value` is used to indicate whether +the value is absent. + +[snippet_parsing_query_1a] + +[warning + Ownership of the string is not transferred; the caller is responsible for + ensuring that the lifetime of the string extends until the container is + destroyed. +] + +In addition to accessor functions which treat the query as a single string, +the library provides container adaptors modeling ranges of query parameters. + +Note that a parameter value might be either empty or absent. The +presence of a value is indicated by the presence of an equals ('=') +sign appearing after the key. This means the value may be absent, +empty, or contain characters. + +The key of a query parameter might also be empty. This means that +a query parameter may be completely empty. In this case the +parameter is said to have a zero-length or empty key, and +no value. + +The URL below demonstrates all the ways that keys and values may +appear in query parameters: + +[snippet_parsing_query_8] + +1) The regular key and value pair: + +[snippet_parsing_query_8a] + +2) A key with an empty value: + +[snippet_parsing_query_8b] + +3) A key with no value: + +[snippet_parsing_query_8c] + +4) A key with no value: + +[snippet_parsing_query_8d] + +[note + The URL reserved characters `:`, `@`, `?`, and `/` may appear + unencoded with URL queries, as they are not ambiguous with + other URL components. + + [snippet_parsing_query_9] +] + +[/-----------------------------------------------------------------------------] + +[endsect] diff --git a/doc/qbk/3.6.fragment.qbk b/doc/qbk/3.6.fragment.qbk new file mode 100644 index 00000000..c9a205c9 --- /dev/null +++ b/doc/qbk/3.6.fragment.qbk @@ -0,0 +1,43 @@ +[/ + Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + Official repository: https://github.com/CPPAlliance/url +] + +[/-----------------------------------------------------------------------------] + +[section Fragment] + +The fragment provides a refinement of the resource specification +usually interpreted as a single string. It provides directions +to a secondary resource related to such main resource, such as +the section in an article or a time-point in a video. + +As usual, its semantics vary depending on the scheme, authority, path, +and media type of the resource. In HTML, fragments are used as internal +page references. This usage is called a "named anchor," referring to a +section within a web page. The URL below points to the anchor "section2": + +[snippet_parsing_fragment_4] + +These functions do not throw. The URL fragment might also be empty +or absent. If the URL has no fragment, these functions return an +empty string. The function +[link url.ref.boost__urls__url_view.has_fragment `has_fragment`] +can be used to determine whether this empty string means there is +no fragment or an empty fragment string in the URL. + +[snippet_parsing_fragment_5] + +The URL reserved characters `:`, `@`, `?`, and `/` may appear +unencoded with URL fragments, as they are not ambiguous with +other URL components. + +[snippet_parsing_fragment_6] + +[/-----------------------------------------------------------------------------] + +[endsect] diff --git a/doc/qbk/6.0.grammar.qbk b/doc/qbk/4.0.grammar.qbk similarity index 94% rename from doc/qbk/6.0.grammar.qbk rename to doc/qbk/4.0.grammar.qbk index b851c59c..97fcf34c 100644 --- a/doc/qbk/6.0.grammar.qbk +++ b/doc/qbk/4.0.grammar.qbk @@ -62,10 +62,10 @@ features. [import ../../test/unit/doc_grammar.cpp] -[include 6.1.rules.qbk] -[include 6.2.charset.qbk] -[include 6.3.combinators.qbk] -[include 6.4.range.qbk] -[include 6.5.rfc3986.qbk] +[include 4.1.rules.qbk] +[include 4.2.charset.qbk] +[include 4.3.combinators.qbk] +[include 4.4.range.qbk] +[include 4.5.rfc3986.qbk] [endsect] diff --git a/doc/qbk/4.1.parsing.qbk b/doc/qbk/4.1.parsing.qbk deleted file mode 100644 index 267ee2fc..00000000 --- a/doc/qbk/4.1.parsing.qbk +++ /dev/null @@ -1,278 +0,0 @@ -[/ - Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) - Copyright (c) 2022 Alan de Freitas (vinnie.falco@gmail.com) - - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - - Official repository: https://github.com/CPPAlliance/url -] - -[#section.parsing] -[#section.url_view] -[section Parsing] - -[#url_notation] -[heading Notation] - -Following the syntax in __rfc3986__, a single algorithm is used for URLs, URIs -and IRIs. When discussing particular grammars, its rules are presented -exactly as it appears in the literature. - -A URL string can be parsed using one of the parsing functions. - -[table Parsing Functions [ - [Function] - [Grammar] - [Example] - [Notes] -][ - [[link url.ref.boost__urls__parse_uri `parse_uri`]] - [[@https://datatracker.ietf.org/doc/html/rfc3986#section-3 ['URI]]] - [[teletype]`http://www.boost.org/index.html?field=value#downloads`] - [Supports fragment `#downloads`] -][ - [[link url.ref.boost__urls__parse_absolute_uri `parse_absolute_uri`]] - [[@https://datatracker.ietf.org/doc/html/rfc3986#section-4.3 ['absolute-URI]]] - [[teletype]`http://www.boost.org/index.html?field=value`] - [Does not support fragment] -][ - [[link url.ref.boost__urls__parse_origin_form `parse_origin_form`]] - [[@https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.1 ['origin-form]]] - [[teletype]`/index.html?field=value`] - [Used in HTTP requests] -][ - [[link url.ref.boost__urls__parse_relative_ref `parse_relative_ref`]] - [[@https://datatracker.ietf.org/doc/html/rfc3986#section-4.2 ['relative-ref]]] - [[teletype]`//www.boost.org/index.html?field=value#downloads`] - [Does not require scheme] -][ - [[link url.ref.boost__urls__parse_uri_reference `parse_uri_reference`]] - [[@https://datatracker.ietf.org/doc/html/rfc3986#section-4.1 ['URI-reference]]] - [[teletype]`http://www.boost.org/index.html`] - [Any `URI` or `relative-ref`] -]] - -The library uses the convention that each function `parse_` operates according -to the particular grammar rule `` specified in __rfc3986__. The document -inherits from [@https://datatracker.ietf.org/doc/html/rfc2396 rfc2396], where there are -no `URL`, `absolute-URL`, `URL-reference` rules. Thus, for consistency, the main parsing -functions also make reference to `uri`s rather than `url`s. - -The collective grammars parsed by these algorithms are specified below. - -[teletype] -``` - absolute-URI = scheme ":" hier-part [ "?" query ] - - relative-ref = relative-part [ "?" query ] [ "#" fragment ] - - URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] - - URI-reference = URI / relative-ref - - hier-part = "//" authority path-abempty - / path-absolute - / path-rootless - / path-empty - - relative-part = "//" authority path-abempty - / path-absolute - / path-noscheme - / path-empty -``` - -[heading Example] - -The following is an example URI and its main components: - -[teletype] -``` - foo://example.com:8042/over/there?name=ferret#nose - \_/ \______________/\_________/ \_________/ \__/ - | | | | | - scheme authority path query fragment -``` - -For the complete specification please refer to __rfc3986__: - -[note - This documentation refers to the Augmented Backus-Naur Form (ABNF) - notation of - [@https://tools.ietf.org/html/rfc2234 rfc2234] - to specify particular grammars used by algorithms and containers. While - a complete understanding of the notation is not a requirement for using the - library, it may help for understanding how valid components of URLs are - defined. In particular, this will be of interest to users who wish to - compose parsing algorithms using the combinators provided by the library. -] - -[heading Functions] - -All parsing functions accept a __string_view__ and return a -`__result__<__url_view__>`. The following example parses a string literal -containing a [@https://datatracker.ietf.org/doc/html/rfc3986#section-3 ['URI]]: - -[c++] -[snippet_parsing_url_1] - -The parsing function refers to the [@https://datatracker.ietf.org/doc/html/rfc3986#section-3 ['URI]] -grammar rule and the result refers to a __url_view__. The convention `parse_` -produces `parse_uri` for the [@https://datatracker.ietf.org/doc/html/rfc3986#section-3 ['URI]] -grammar rule defined in __rfc3986__. However, as the library adheres to -the [link contemporary_view Contemporary View] of URI/URL partitioning -and standardizes on the term "URL", it makes reference to the term "URL" elsewhere. - -When the input does not match the URL grammar, the error is reported through -the `__result__<__url_view__>`. The result is a variant-like object -which holds a __url_view__ or an __error_code__ in the case where the parsing -failed. - -[heading Copying] - -Like a __string_view__, the __url_view__ does not own the underlying - character buffer. Instead, it references the string passed to the - parsing function. The caller is required to ensure that the lifetime -of the string extends until the view is destroyed, or use other containers -such as __url__ and __static_url__. - -The function -[link url.ref.boost__urls__url_view.persist `url_view::persist`] -may also be used to create a copy of the underlying character buffer and attach -ownership of the buffer to a newly returned view, which is wrapped in a -shared pointer. The following code calls `persist` to create a read-only -copy: - -[c++] -[snippet_parsing_url_2] - -The interface of __url_view__ decomposes the URL into its individual parts and -allows for inspection of the various parts as well as returning metadata about -the URL itself. All the non-modifying observer operations are described in the -sections that follow. - -[table [[Code][Output]] [[ -[c++] -[snippet_accessing_1] -][ -[teletype] -``` - - - url : https://user:pass@example.com:443/path/to/my%2dfile.txt?id=42&name=John%20Doe+Jingleheimer%2DSchmidt#page%20anchor - scheme : https - authority : user:pass@example.com:443 - userinfo : user:pass - user : user - password : pass - host : example.com - port : 443 - path : /path/to/my-file.txt - query : id=42&name=John Doe Jingleheimer-Schmidt - fragment : page anchor -``` -]]] - -To create a mutable copy of the __url_view__, one can create a __url__ -or __static_url__: - -[c++] -[snippet_parsing_url_3] - -[heading Return Type] - -In many places, functions in the library have a return type which uses the -__result__ alias template. This class allows the parsing algorithms to -report errors without referring to exceptions. - -The functions `result::has_value` and `result::has_error` can be used to -check if the result contains an error. - -[snippet_parsing_url_1] - -This ensures `result::value` will not throw an error. In contexts where -it is acceptable to throw errors, `result::value` can be used directly. - -Check the reference for __result__ for a synopsis of the type. For complete -information please consult the full -[@boost:/libs/system/doc/html/system.html#ref_resultt_e `result`] -documentation in [@boost:/libs/system/doc/html/system.html Boost.System]. - -[heading Summary] - -[table [[Component][Decoded][Encoded][Check]] -[ - [authority] - [] - [[link url.ref.boost__urls__url_view.encoded_authority `encoded_authority`]] - [[link url.ref.boost__urls__url_view.has_authority `has_authority`]]] - -[ - [fragment] - [[link url.ref.boost__urls__url_view.fragment `fragment`]] - [[link url.ref.boost__urls__url_view.encoded_fragment `encoded_fragment`]] - [[link url.ref.boost__urls__url_view.has_fragment `has_fragment`]] -] -[ - [host] - [[link url.ref.boost__urls__url_view.host `host`]] - [[link url.ref.boost__urls__url_view.encoded_host `encoded_host`]] - [] -] -[ - [host_and_port] - [] - [[link url.ref.boost__urls__url_view.encoded_host_and_port `encoded_host_and_port`]] - [] -] -[ - [origin] - [] - [[link url.ref.boost__urls__url_view.encoded_origin `encoded_origin`]] - [] -] -[ - [params] - [[link url.ref.boost__urls__url_view.params `params`]] - [[link url.ref.boost__urls__url_view.encoded_params `encoded_params`]] - [] -] -[ - [password] - [[link url.ref.boost__urls__url_view.password `password`]] - [[link url.ref.boost__urls__url_view.encoded_password `encoded_password`]] - [[link url.ref.boost__urls__url_view.has_password `has_password`]] -] -[ - [path] - [] - [[link url.ref.boost__urls__url_view.encoded_path `encoded_path`]] - [] -] -[ - [query] - [[link url.ref.boost__urls__url_view.query `query`]] - [[link url.ref.boost__urls__url_view.encoded_query `encoded_query`]] - [[link url.ref.boost__urls__url_view.has_query `has_query`]] -] -[ - [segments] - [[link url.ref.boost__urls__url_view.segments `segments`]] - [[link url.ref.boost__urls__url_view.encoded_segments `encoded_segments`]] - [] -] -[ - [user] - [[link url.ref.boost__urls__url_view.user `user`]] - [[link url.ref.boost__urls__url_view.encoded_user `encoded_user`]] - [] -] -[ - [userinfo] - [[link url.ref.boost__urls__url_view.userinfo `userinfo`]] - [[link url.ref.boost__urls__url_view.encoded_userinfo `encoded_userinfo`]] - [[link url.ref.boost__urls__url_view.has_userinfo `has_userinfo`]] -] -] - -[endsect] diff --git a/doc/qbk/6.1.rules.qbk b/doc/qbk/4.1.rules.qbk similarity index 100% rename from doc/qbk/6.1.rules.qbk rename to doc/qbk/4.1.rules.qbk diff --git a/doc/qbk/6.2.charset.qbk b/doc/qbk/4.2.charset.qbk similarity index 100% rename from doc/qbk/6.2.charset.qbk rename to doc/qbk/4.2.charset.qbk diff --git a/doc/qbk/4.2.modifying.qbk b/doc/qbk/4.2.modifying.qbk deleted file mode 100644 index 57767b71..00000000 --- a/doc/qbk/4.2.modifying.qbk +++ /dev/null @@ -1,132 +0,0 @@ -[/ - Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) - Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) - - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - - Official repository: https://github.com/CPPAlliance/url -] - -[section Modifying] - -[heading Constructing Containers] - -The class __url__ is a container used to store and produce URLs. -The [link section.url_view URL parsing] functions can be used to create -a new container from a __url_view__: - -[snippet_modifying_1] - -All __url_view__ observers are also available for a __url__: - -[snippet_modifying_2] - -The interface of __url_view__ decomposes the URL into its individual parts and -allows for inspection of the various parts as well as returning metadata about -the URL itself. - -[heading Modifiers] - -The decoded variant of the modifier functions will encode any -characters that would be invalid for the specified component. - -[table [[Code][Output]] [[ -[c++] -[snippet_modifying_5] -][ -[teletype] -``` - http://www.my%20example.com -``` -]][[ -[c++] -[snippet_modifying_3] -][ -[teletype] -``` - https://my%20website.com/my%20file.txt?id=42&name=John%20Doe -``` -]]] - -[heading Encoded Modifiers] - -The encoded variant of the modifier functions require that the -encoded strings are valid for the specified component. The -functions will throw if the input string contains any character -invalid for the specified component. - -[table [[Code][Output]] [[ -[c++] -[snippet_modifying_4] -][ -[teletype] -``` - http://www.example.com -``` -]]] - -[heading Summary] - -For each observer function in __url_view__, an instance of __url__ provides a -corresponding `set` function to define the value of the specified component. - -[table [[Component][Decoded][Encoded]] -[ - [authority] - [] - [[link url.ref.boost__urls__url.set_encoded_authority `set_encoded_authority`]] -] -[ - [fragment] - [[link url.ref.boost__urls__url.set_fragment `set_fragment`]] - [[link url.ref.boost__urls__url.set_encoded_fragment `set_encoded_fragment`]] -] -[ - [host] - [[link url.ref.boost__urls__url.set_host `set_host`]] - [[link url.ref.boost__urls__url.set_encoded_host `set_encoded_host`]] -] -[ - [password] - [[link url.ref.boost__urls__url.set_password `set_password`]] - [[link url.ref.boost__urls__url.set_encoded_password `set_encoded_password`]] -] -[ - [path] - [[link url.ref.boost__urls__url.set_path `set_path`]] - [[link url.ref.boost__urls__url.set_encoded_path `set_encoded_path`]] -] -[ - [path_absolute] - [[link url.ref.boost__urls__url.set_path_absolute `set_path_absolute`]] - [[link url.ref.boost__urls__url.set_path_absolute `set_path_absolute`]] -] -[ - [port] - [[link url.ref.boost__urls__url.set_port `set_port`]] - [[link url.ref.boost__urls__url.set_port `set_port`]] -] -[ - [query] - [[link url.ref.boost__urls__url.set_query `set_query`]] - [[link url.ref.boost__urls__url.set_encoded_query `set_encoded_query`]] -] -[ - [scheme] - [[link url.ref.boost__urls__url.set_scheme `set_scheme`]] - [[link url.ref.boost__urls__url.set_scheme `set_scheme`]] -] -[ - [user] - [[link url.ref.boost__urls__url.set_user `set_user`]] - [[link url.ref.boost__urls__url.set_encoded_user `set_encoded_user`]] -] -[ - [userinfo] - [[link url.ref.boost__urls__url.set_userinfo `set_userinfo`]] - [[link url.ref.boost__urls__url.set_encoded_userinfo `set_encoded_userinfo`]] -] -] - -[endsect] \ No newline at end of file diff --git a/doc/qbk/6.3.combinators.qbk b/doc/qbk/4.3.combinators.qbk similarity index 100% rename from doc/qbk/6.3.combinators.qbk rename to doc/qbk/4.3.combinators.qbk diff --git a/doc/qbk/6.4.range.qbk b/doc/qbk/4.4.range.qbk similarity index 100% rename from doc/qbk/6.4.range.qbk rename to doc/qbk/4.4.range.qbk diff --git a/doc/qbk/6.5.rfc3986.qbk b/doc/qbk/4.5.rfc3986.qbk similarity index 100% rename from doc/qbk/6.5.rfc3986.qbk rename to doc/qbk/4.5.rfc3986.qbk diff --git a/doc/qbk/7.0.concepts.qbk b/doc/qbk/5.0.concepts.qbk similarity index 82% rename from doc/qbk/7.0.concepts.qbk rename to doc/qbk/5.0.concepts.qbk index e932b2f5..525ee38c 100644 --- a/doc/qbk/7.0.concepts.qbk +++ b/doc/qbk/5.0.concepts.qbk @@ -11,8 +11,8 @@ This section describes all of the concepts defined by the library. -[include 7.1.CharSet.qbk] -[include 7.1.MutableString.qbk] -[include 7.1.Rule.qbk] +[include 5.1.CharSet.qbk] +[include 5.2.MutableString.qbk] +[include 5.3.Rule.qbk] [endsect] diff --git a/doc/qbk/7.1.CharSet.qbk b/doc/qbk/5.1.CharSet.qbk similarity index 100% rename from doc/qbk/7.1.CharSet.qbk rename to doc/qbk/5.1.CharSet.qbk diff --git a/doc/qbk/5.1.scheme.qbk b/doc/qbk/5.1.scheme.qbk deleted file mode 100644 index 5969a974..00000000 --- a/doc/qbk/5.1.scheme.qbk +++ /dev/null @@ -1,267 +0,0 @@ -[/ - Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) - - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - - Official repository: https://github.com/CPPAlliance/url -] - -[/-----------------------------------------------------------------------------] - -[section Scheme] - -[heading Notation] - -The scheme is the top-level hierarchical element which defines the -syntax and semantics of the rest of the URL. The scheme identifier -is always followed by a colon when it appears in a URL. - -Here are some examples of URLs with the schemes `https` and `file`: - -[teletype] -``` - https://www.example.com/path/to/file.txt?page=2 -``` - -``` - file:///usr/local/bin/ -``` - -A scheme must start with a letter, and may contain only letters, -digits, plus and minus signs, and periods: - -[table Scheme BNF [[ -[teletype] -``` - scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) - - absolute-URI = scheme ":" hier-part [ "?" query ] - - URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] -``` -]]] - -[heading Observers] - -The function [link url.ref.boost__urls__url_view.scheme `scheme`] can -be used to obtain the scheme from a __url_view__: - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_scheme_1] -][ -[teletype] -``` - mailto -``` -]]] - -If the URL has no scheme, this function returns an empty string. To check whether -a URL contains a scheme the function -[link url.ref.boost__urls__url_view.has_scheme `url_view::has_scheme`] might be used. - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_scheme_2] -][ -[teletype] -``` - mailto -``` -]]] - -[heading Common schemes] - -The library defines an enumeration of values for some well-known scheme -identifiers. - -[c++] -[snippet_parsing_scheme_3] - -These may be used instead of their corresponding strings: - -[table Scheme IDs [ - [ID] - [Description] -][ - [[link url.ref.boost__urls__scheme `scheme::none`]] - [Indicates no scheme present] -][ - [[link url.ref.boost__urls__scheme `scheme::unknown`]] - [A valid but unknown scheme] -][ - [[link url.ref.boost__urls__scheme `scheme::ftp`]] - [File Transfer Protocol ("FTP")] -][ - [[link url.ref.boost__urls__scheme `scheme::file`]] - [File URI Scheme] -][ - [[link url.ref.boost__urls__scheme `scheme::http`]] - [Hypertext Transfer Protocol] -][ - [[link url.ref.boost__urls__scheme `scheme::https`]] - [Secure Hypertext Transfer Protocol] -][ - [[link url.ref.boost__urls__scheme `scheme::ws`]] - [WebSocket Protocol] -][ - [[link url.ref.boost__urls__scheme `scheme::wss`]] - [Secure WebSocket Protocol] -]] - - -[heading Use Cases] - -A number of schemes are used to define the semantics of URLs. For instance, -the term web address is often used informally to describe URLs with the -`http` scheme, whose semantics are defined by -[@https://datatracker.ietf.org/doc/html/rfc2616#section-3.2.2 rfc3986]. -One of the conventions of the HTTP scheme is that when the port 80 is -implicitly assumed when it is not provided. Such conventions are not -part of the URL protocol. - -Schemes are also different from protocols. Although the scheme `http` is -used to interact with resources via the HTTP protocol, the scheme `file` -has no corresponding protocol. - -Some noteworthy IANA-registered schemes are - -[table Scheme IDs [ - [Scheme] - [Resource] -][ - [[@https://tools.ietf.org/html/rfc2392 `cid`]] - [SMTP/MIME messages] -][ - [[@https://tools.ietf.org/html/rfc2397 `data`]] - [Inline data] -][ - [[@https://tools.ietf.org/html/rfc4918 `dav`]] - [WebDAV] -][ - [[@https://tools.ietf.org/html/rfc4501 `dns`]] - [Domain Name System] -][ - [[@https://tools.ietf.org/html/rfc8089 `file`]] - [File systems] -][ - [[@https://tools.ietf.org/html/rfc1738 `ftp`]] - [FTP resources] -][ - [[@https://www.iana.org/assignments/uri-schemes/prov/git `git`]] - [GIT repository] -][ - [[@https://tools.ietf.org/html/rfc7230 `http`]] - [HTTP resources] -][ - [[@https://tools.ietf.org/html/rfc7230 `https`]] - [HTTP secured using SSL/TLS] -][ - [[@https://tools.ietf.org/html/rfc6068 `mailto`]] - [SMTP email addresses] -][ - [[@https://www.bittorrent.org/beps/bep_0009.html `magnet`]] - [Identify files by content] -][ - [[@https://tools.ietf.org/html/rfc2224 `nfs`]] - [Network File System] -][ - [[@https://tools.ietf.org/html/rfc2384 `pop`]] - [POP3] -][ - [[@https://docs.aws.amazon.com/cli/latest/reference/s3/ `s3`]] - [Amazon S3] -][ - [[@https://tools.ietf.org/html/rfc5724 `sms`]] - [SMS messages] -][ - [[@https://www.iana.org/assignments/uri-schemes/prov/svn `svn`]] - [Subversion (SVN) repository] -][ - [[@https://tools.ietf.org/html/rfc2806 `tel`]] - [Telephone number] -][ - [[@https://www.iana.org/assignments/uri-schemes/prov/udp `udp`]] - [Streaming protocols over UDP] -][ - [[@https://tools.ietf.org/html/rfc2141 `urn`]] - [Uniform Resource Names] -][ - [[@https://tools.ietf.org/html/rfc6455 `ws`]] - [WebSocket Protocol] -][ - [[@https://tools.ietf.org/html/rfc6455 `wss`]] - [Secure WebSocket Protocol] -]] - -Many other valid but unofficial schemes are common: - -[table Scheme IDs [ - [Scheme] - [Resource] -][ - [[@http://tools.ietf.org/html/draft-paskin-doi-uri `doi`]] - [Digital Object Identifier] -][ - [[@https://datatracker.ietf.org/doc/html/draft-hoehrmann-javascript-scheme `javascript`]] - [Javascript Code] -][ - [[@https://datatracker.ietf.org/doc/html/draft-patrick-lambert-odbc-uri-scheme `odbc`]] - [Open Database Connectivity] -][ - [[@https://api.slack.com/reference/deep-linking `slack`]] - [Slack Client] -]] - -[/-----------------------------------------------------------------------------] - -[heading Member Functions] - -The functions for inspecting the scheme in a __url_view__ are as follows: - -[table Scheme Observers [ - [Function] - [Description] -][ - [[link url.ref.boost__urls__url_view.has_scheme `has_scheme`]] - [Return true if a scheme is present] -][ - [[link url.ref.boost__urls__url_view.scheme `scheme`]] - [Return the scheme as a string] -][ - [[link url.ref.boost__urls__url_view.scheme_id `scheme_id`]] - [Return the scheme as a known-scheme enumeration constant] -]] - -[note - None of these functions throw exceptions. If the URL has no scheme, - [link url.ref.boost__urls__url_view.scheme `scheme`] returns an empty - string. If the function - [link url.ref.boost__urls__url_view.scheme_id `scheme_id`] identifies a valid - but unknown scheme, the value [link url.ref.boost__urls__scheme `scheme::unknown`] - is returned. -] - -The functions for modifying the scheme in a __url__ are as follows: - -[table Scheme Modifiers [ - [Function] - [Description] -][ - [[link url.ref.boost__urls__url.set_scheme `set_scheme`]] - [Set the scheme] -][ - [[link url.ref.boost__urls__url.remove_scheme `remove_scheme`]] - [Set the scheme] -][ - [[link url.ref.boost__urls__url.normalize_scheme `normalize_scheme`]] - [Set the scheme] -]] - -[note - Since schemes may contain only letters, digits, plus signs, minus signs, and periods, - there is no distinction in the functions for encoded and decoded schemes. -] - -[endsect] diff --git a/doc/qbk/7.1.MutableString.qbk b/doc/qbk/5.2.MutableString.qbk similarity index 100% rename from doc/qbk/7.1.MutableString.qbk rename to doc/qbk/5.2.MutableString.qbk diff --git a/doc/qbk/5.2.authority.qbk b/doc/qbk/5.2.authority.qbk deleted file mode 100644 index 561ea6bb..00000000 --- a/doc/qbk/5.2.authority.qbk +++ /dev/null @@ -1,580 +0,0 @@ -[/ - Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) - - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - - Official repository: https://github.com/CPPAlliance/url -] - -[/-----------------------------------------------------------------------------] - -[section Authority] - -[heading Notation] - -The authority is a hierarchical element which names an entity governing -the namespace defined by the remainder of the URL. It divides into -three subcomponents: - -[teletype] -``` - authority = [userinfo "@"] host [":" port] -``` - -* The `host` subcomponent of the authority can be a registered name or an IP address. -* The optional `port` number subcomponent is preceded by a colon ":" -* The optional `userinfo` subcomponent consists of a username and an optional password - -In a URL, the authority component is always preceded by a double slash ("//"). - -[table Authority BNF [ - [ - [teletype] - ``` - authority = [ userinfo "@" ] host [ ":" port ] - - userinfo = user [ ":" [ password ] ] - - host = IP-literal / IPv4address / reg-name - - port = *DIGIT - - user = *( unreserved / pct-encoded / sub-delims ) - password = *( unreserved / pct-encoded / sub-delims / ":" ) - - IP-literal = "[" ( IPv6address / IPvFuture ) "]" - - reg-name = *( unreserved / pct-encoded / "-" / ".") - ``` - ] -]] - -[heading Observers] - -The function [link url.ref.boost__urls__url_view.authority `authority`] can -be used to obtain the authority from a __url_view__: - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_authority_3] -][ -[teletype] -``` - https://www.boost.org/users/download/ - scheme: https - has authority: 1 - authority: www.boost.org - path: /users/download/ -``` -]]] - -These functions do not throw. If the URL has no authority, -[link url.ref.boost__urls__url_view.authority `authority`] -returns an empty __authority_view__. - -The function [link url.ref.boost__urls__url_view.has_authority `has_authority`] -can be used to check whether this empty authority means there is no authority -or an empty authority in the URL. - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_authority_1] -][ -[teletype] -``` - https:///path/to_resource - scheme: https - has authority: 1 - authority: - path: /path/to_resource -``` -]] -[[ -[c++] -[snippet_parsing_authority_6] -][ -[teletype] -``` - mailto://John.Doe@example.com - scheme: mailto - has authority: 1 - authority: John.Doe@example.com - path: -``` -]]] - -Notice that the decoded counterpart of -[link url.ref.boost__urls__url_view.encoded_authority `encoded_authority`] -does not return a __pct_encoded_view__. The reason is any decoded character -`/` could make it ambiguous with the path component so the authority -is represented through an __authority_view__. - -[heading Host] - -The host subcomponent represents where resources -are located. The functions -[link url.ref.boost__urls__url_view.host `host`] -and [link url.ref.boost__urls__url_view.encoded_host `encoded_host`] -can be used to obtain the host from a __url_view__, while -[link url.ref.boost__urls__url_view.encoded_host_and_port `encoded_host_and_port`] -allows us to directly obtain the host with the corresponding port number. - -The host might be a registered name - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_authority_8] -][ -[teletype] -``` - https://john.doe@www.example.com:123/forum/questions/ - encoded host: www.example.com - host: www.example.com - host and port: www.example.com:123 - port: 123 - port number: 123 -``` -]]] - -or an IP address - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_authority_9] -][ -[teletype] -``` - https://john.doe@192.168.2.1:123/forum/questions/ - encoded host: 192.168.2.1 - host: 192.168.2.1 - host and port: 192.168.2.1:123 - port: 123 - port number: 123 -``` -]]] - -Although this is not mandatory, note that the encoded host is rarely -different from its encoded counterpart. -The function [link url.ref.boost__urls__url_view.port_number `port_number`] -returns the decoded port as an integer. - -Registered names usually need to be handled differently from IP addresses. -The function -[link url.ref.boost__urls__url_view.host_type `host_type`] -can be used to identify which type of host is described in the URL. - -[c++] -[snippet_parsing_authority_10] - -When the [link url.ref.boost__urls__url_view.host_type `host_type`] -matches an IP address, the functions -[link url.ref.boost__urls__url_view.ipv4_address `ipv4_address`], -[link url.ref.boost__urls__url_view.ipv6_address `ipv6_address`] -can be used to obtain the decoded addresses as integers. - -[heading Userinfo] - -The optional `userinfo` subcomponent consists of a user name and -an optional password. The function -[link url.ref.boost__urls__url_view.encoded_userinfo `encoded_userinfo`] -can be used to retrieve the userinfo from a __url_view__. Analogous -functions are provided for the userinfo subcomponents. - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_authority_11] -][ -[teletype] -``` - https://john.doe:123456@www.somehost.com/forum/questions/ - - has_userinfo: 1 - userinfo: john.doe:123456 - user: john.doe - - has_password: 1 - password: 123456 -``` -]]] - -Analogous to other observers, the functions -[link url.ref.boost__urls__url_view.has_userinfo `has_userinfo`] and -[link url.ref.boost__urls__url_view.has_password `has_password`] are provided -to differentiate empty components from absent components. - -Note that there is no function `has_user`. The user component is available -whenever `userinfo` exists. - -[note Although the specification allows the format `username:password`, -the password component should be used with care. - -It is not recommended to transfer password data through URLs -unless this is an empty string indicating no password.] - -[heading Authority View] - -In contexts where an authority can appear by itself, the library provides the -__authority_view__, a read-only container to a non-owning character buffer -containing a valid authority. - -As an example, the grammar for the -[@https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.1 ['request-target]] -of an HTTP/1 CONNECT request uses -[@https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.3 ['authority-form]]. -This is what such a request looks like: - -[teletype] -``` - CONNECT www.example.com:80 HTTP/1.1 -``` - -In that case, we have an authority that cannot be parsed directly -with __parse_uri__ as a URL. Instead, we can use the analogous function -[link url.ref.boost__urls__parse_authority `parse_authority`] to -obtain an __authority_view__. - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_authority_12] -][ -[teletype] -``` - www.example.com:80 - - host_and_port: www.example.com:80 - host: www.example.com - port: 80 - port number: 80 - - has_userinfo: 0 - userinfo: - user: - - has_password: 0 - password: -``` -]]] - -The authority view provides the subset of observer member functions found in -__url_view__ which are relevant to the authority. However, when an authority -is parsed on its own, the leading double slashes ("//") are not present. - -The following authority string is also valid for -[link url.ref.boost__urls__parse_authority `parse_authority`]: - -[teletype] -``` - user:pass@www.example.com:443 -``` - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_authority_13] -][ -[teletype] -``` - user:pass@www.example.com:443 - - encoded_host_and_port: www.example.com:443 - host: www.example.com - port: 443 - port number: 443 - - has_userinfo: 1 - userinfo: user:pass - - user: user - - has_password: 1 - password: pass -``` -]]] - - -[heading Use Cases] - -Note that if an authority is present, the host is always defined even if it -is the empty string (corresponding to a zero-length ['reg-name] in the BNF). - -[teletype] -``` - https:///path/to_resource - \____/\/\_______________/ - | | | -scheme authority path -``` - -[table - [[Component] [Value] ] - [[URL] [`https:///path/to_resource`] ] - [[Scheme] [`https`] ] - [[Has authority] [Yes] ] - [[Authority] [] ] - [[Path] [`/path/to_resource`] ] -] - -The authority component also influences how we should interpret the URL path. -If the authority is present, the path component must either be empty or begin -with a slash. - -This is a common pattern where the path is empty: - -[teletype] -``` - https://www.boost.org - \___/ \___________/ - scheme authority (path is empty) -``` - -[table - [[Component] [Value] ] - [[URL] [`https://www.boost.org`] ] - [[Scheme] [`https`] ] - [[Has authority] [Yes] ] - [[Authority] [`www.boost.org`] ] - [[Path] [] ] -] - -When both the authority and path exist, the path needs to begin with a slash: - -[teletype] -``` - https://www.boost.org/users/download/ - \___/ \___________/\______________/ - scheme authority path (begins with a slash) -``` - -[table - [[Component] [Value] ] - [[URL] [`https://www.boost.org/users/download/`] ] - [[Scheme] [`https`] ] - [[Has authority] [Yes] ] - [[Authority] [`www.boost.org`] ] - [[Path] [`/users/download/`] ] -] - -This rule also affects the path "`/`": - -[teletype] -``` - https://www.boost.org/ - \___/ \___________/\/ - scheme authority path (begins with a slash) -``` - -[table - [[Component] [Value] ] - [[URL] [`https://www.boost.org/`] ] - [[Scheme] [`https`] ] - [[Has authority] [Yes] ] - [[Authority] [`www.boost.org`] ] - [[Path] [`/`] ] -] - -When there is no authority component, the path cannot begin with an empty -segment. This means the path cannot begin with two slashes `//` to avoid these -characters being interpreted as the beginning of the authority component. - -For instance, consider the following valid URL: - -[teletype] -``` - mailto:John.Doe@example.com - \____/ \__________________/ - scheme path -``` - -[table - [[Component] [Value] ] - [[URL] [`mailto:John.Doe@example.com`] ] - [[Scheme] [`mailto`] ] - [[Has authority] [No] ] - [[Authority] [] ] - [[Path] [`John.Doe@example.com`] ] -] - -Note how including a double slash would make the path be interpreted as the authority: - -[teletype] -``` - mailto://John.Doe@example.com - \____/ \____________________/ - scheme authority -``` - -[table - [[Component] [Value] ] - [[URL] [`mailto://John.Doe@example.com`] ] - [[Scheme] [`mailto`] ] - [[Has authority] [Yes] ] - [[Authority] [`John.Doe@example.com`] ] - [[Path] [] ] -] - -In complete authority components, we can also extract the `userinfo` and `port` subcomponents. - -[teletype] -``` - userinfo host port - /------\ /-------------\ /-\ - https://john.doe@www.example.com:123/forum/questions/ - \___/ \__________________________/\_______________/ - scheme authority path -``` - -[table - [[Component] [Value] ] - [[URL] [`https://john.doe@www.example.com:123/forum/questions/`] ] - [[Scheme] [`https`] ] - [[Has authority] [Yes] ] - [[Authority] [`john.doe@www.example.com:123`] ] - [[Host] [`www.example.com`] ] - [[Userinfo] [`john.doe`] ] - [[Port] [`123`] ] - [[Path] [`/forum/questions/`] ] -] - -[heading Member Functions] - -The functions for inspecting all or part of the authority in a -__url_view__ are as follows: - -[table Userinfo Observers [ - [Function] - [Description] -][ - [[link url.ref.boost__urls__url_view.has_password `has_password`]] - [Return true if an password is present.] -][ - [[link url.ref.boost__urls__url_view.has_userinfo `has_userinfo`]] - [Return true if a userinfo is present.] -][ - [[link url.ref.boost__urls__url_view.encoded_password `encoded_password`]] - [Return the password as a percent-encoded string.] -][ - [[link url.ref.boost__urls__url_view.encoded_user `encoded_user`]] - [Return the user as a percent-encoded string.] -][ - [[link url.ref.boost__urls__url_view.encoded_userinfo `encoded_userinfo`]] - [Return the userinfo as a percent-encoded string.] -][ - [[link url.ref.boost__urls__url_view.password `password`]] - [Return the password as a string with percent-decoding applied.] -][ - [[link url.ref.boost__urls__url_view.user `user`]] - [Return the user as a string with percent-decoding applied.] -][ - [[link url.ref.boost__urls__url_view.userinfo `userinfo`]] - [Return the userinfo as a string with percent-decoding applied.] -]] - - -[table Host Observers [ - [Function] - [Description] -][ - [[link url.ref.boost__urls__url_view.encoded_host `encoded_host`]] - [Return the host as a percent-encoded string.] -][ - [[link url.ref.boost__urls__url_view.encoded_host_and_port `encoded_host_and_port`]] - [Return the host and port as a percent-encoded string.] -][ - [[link url.ref.boost__urls__url_view.has_port `has_port`]] - [Return true if an port is present.] -][ - [[link url.ref.boost__urls__url_view.host `host`]] - [Return the type of host specified, if any.] -][ - [[link url.ref.boost__urls__url_view.ipv4_address `ipv4_address`]] - [Return the IPv4 address of the host, if applicable.] -][ - [[link url.ref.boost__urls__url_view.ipv6_address `ipv6_address`]] - [Return the IPv6 address of the host, if applicable.] -][ - [[link url.ref.boost__urls__url_view.ipvfuture `ipvfuture`]] - [Return the IPvFuture address of the host, if applicable.] -][ - [[link url.ref.boost__urls__url_view.port `port`]] - [Return the port string of the host, if applicable.] -][ - [[link url.ref.boost__urls__url_view.port_number `port_number`]] - [Return the port number of the host, if applicable.] -]] - -[table Authority Observers [ - [Function] - [Description] -][ - [[link url.ref.boost__urls__url_view.has_authority `has_authority`]] - [Return true if an authority is present.] -][ - [[link url.ref.boost__urls__url_view.encoded_authority `encoded_authority`]] - [Return the authority as a percent-encoded string.] -]] - -The functions for modifying all or part of the authority in a -__url__ are as follows: - -[table Userinfo Modifiers [ - [Function] - [Description] -][ - [[link url.ref.boost__urls__url.set_userinfo `set_userinfo`]] - [Set the userinfo.] -][ - [[link url.ref.boost__urls__url.set_encoded_userinfo `set_encoded_userinfo`]] - [Set the encoded userinfo.] -][ - [[link url.ref.boost__urls__url.remove_userinfo `remove_userinfo`]] - [Remove the userinfo.] -][ - [[link url.ref.boost__urls__url.set_user `set_user`]] - [Set the user.] -][ - [[link url.ref.boost__urls__url.set_encoded_user `set_encoded_user`]] - [Set the encoded user.] -][ - [[link url.ref.boost__urls__url.set_password `set_password`]] - [Set the password.] -][ - [[link url.ref.boost__urls__url.set_encoded_password `set_encoded_password`]] - [Set the encoded password.] -][ - [[link url.ref.boost__urls__url.remove_password `remove_password`]] - [Remove the password.] -]] - - -[table Host Modifiers [ - [Function] - [Description] -][ - [[link url.ref.boost__urls__url.set_host `set_host`]] - [Set the host.] -][ - [[link url.ref.boost__urls__url.set_encoded_host `set_encoded_host`]] - [Set the encoded host.] -][ - [[link url.ref.boost__urls__url.set_port `set_port`]] - [Set the port.] -][ - [[link url.ref.boost__urls__url.remove_port `remove_port`]] - [Remove the port.] -]] - -[table Authority Modifiers [ - [Function] - [Description] -][ - [[link url.ref.boost__urls__url.set_encoded_authority `set_encoded_authority`]] - [Set the encoded authority.] -][ - [[link url.ref.boost__urls__url.remove_authority `remove_authority`]] - [Remove the authority.] -][ - [[link url.ref.boost__urls__url.normalize_authority `normalize_authority`]] - [Normalize the authority.] -]] - -[endsect] diff --git a/doc/qbk/7.1.Rule.qbk b/doc/qbk/5.3.Rule.qbk similarity index 100% rename from doc/qbk/7.1.Rule.qbk rename to doc/qbk/5.3.Rule.qbk diff --git a/doc/qbk/5.3.path.qbk b/doc/qbk/5.3.path.qbk deleted file mode 100644 index 9238bae9..00000000 --- a/doc/qbk/5.3.path.qbk +++ /dev/null @@ -1,396 +0,0 @@ -[/ - Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) - - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - - Official repository: https://github.com/CPPAlliance/url -] - -[/-----------------------------------------------------------------------------] - -[section Path] - -[heading Notation] - -The path contains data, usually organized hierarchically, which is combined -with the [link section.query query] to identify a resource within the scope of -the scheme and authority. - -Most schemes interpret the path as a sequence of slash delimited ['segments]. -These segments can map to file system paths, which is useful for file servers, -but do not always need to imply this relationship. - -In addition to interacting with the path as a single string, the library -provides container adaptors modeling ranges of individual path segments. - -The URL below contains a path `/path/to/file.txt` with the three segments -`path`, `to`, and `file.txt`: - -[teletype] -``` - http://www.example.com/path/to/file.txt -``` - -Depending on the type of URL, there are various syntactic rules for how the -path may be formulated in a URL. The BNF for these formulations is defined: - -[table Path BNF [[ -[teletype] -``` - path = path-abempty ; begins with "/" or is empty - / path-absolute ; begins with "/" but not "//" - / path-noscheme ; begins with a non-colon segment - / path-rootless ; begins with a segment - / path-empty ; zero characters - - path-abempty = *( "/" segment ) - path-absolute = "/" [ segment-nz *( "/" segment ) ] - path-noscheme = segment-nz-nc *( "/" segment ) - path-rootless = segment-nz *( "/" segment ) - path-empty = 0 -``` -]]] - -[heading Observers] - -The functions [link url.ref.boost__urls__url_view.path `path`] and -[link url.ref.boost__urls__url_view.encoded_path `encoded_path`] can -be used to obtain the path from a __url_view__: - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_path_1] -][ -[teletype] -``` - https://www.boost.org/doc/libs/ - path: /doc/libs/ - encoded_path: /doc/libs/ - segments: /doc/libs/ - encoded segments: /doc/libs/ -``` -]]] - -These functions do not throw. There is no function analogous to `has_path` because -all URLs have valid paths, even when the path is empty. - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_path_3] -][ -[teletype] -``` - https://www.boost.org - path: -``` -]]] - -Notice that there the decoded counterpart for -[link url.ref.boost__urls__url_view.encoded_path `encoded_path`] -should be used with care when the path segments represents a -hierarchy as any decoded character `/` could form an ambiguous -path segment. In these use cases, segment views are the most -appropriate to access individual decoded path segments. - -[heading Segments View] - -These segment view containers are lightweight references to the underlying path - string. Ownership of the string is not transferred; the caller is responsible for -ensuring that the lifetime of the string extends until the container is -destroyed. - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_path_2] -][ -[teletype] -``` - 2 segments - segment: doc - segment: libs -``` -]]] - -In contexts where a path can appear by itself, such as HTTP requests, -segment views may not be constructed directly from strings. Instead, -we can use the analogous function -[link url.ref.boost__urls__parse_path `parse_path`] to obtain a -__segments_view__ or a __segments_encoded_view__. - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_path_9] -][ -[teletype] -``` - path: /doc/libs - 2 segments - segment: doc - segment: libs -``` -]]] - -[heading Path Semantics] - -A path can be absolute or relative. An absolute path begins with `/`: - -[table - [[URL] [Path Type] ] - [[[c++][snippet_modifying_path_1]] [Relative path `""` with 0 segments] ] - [[[c++][snippet_modifying_path_2]] [Absolute path `"/"` with 0 segments]] -] - -The complete path segments "." and ".." are intended only for use -within relative references (__rfc3986__ sec. 4.1) and are removed as part of -the reference resolution process (__rfc3986__ sec. 5.2). Normalizing a URI -resolves these dot-segments (__rfc3986__ sec. 5.2.4). - -[table - [[URL] [Normalized URL] [Path] ] - [[[c++][snippet_modifying_path_3]] [`"https://www.boost.org/b"`] [Absolute path `"/b"` with segments `{"b"}`]] -] - -These rules imply a path with the prefix `":"` or `"/"` could be in conflict with -the scheme and authority components of the URL, since they end with these -characters. For instance, attempting to create a path with the prefix `//`, -i.e. a path whose first segment is empty, could be interpreted as an empty -authority: - -[table - [[URL] [Authority] [Path] ] - [[[c++][snippet_modifying_path_4]] [(no authority)] [Relative path `"path/to/file.txt"` with segments `{"path", "to", "file.txt"}`] ] - [[[c++][snippet_modifying_path_5]] [(no authority)] [Absolute path `"/path/to/file.txt"` with segments `{"path", "to", "file.txt"}`]] - [[[c++][snippet_modifying_path_6]] [`"path"`] [Absolute path `"/to/file.txt"` with segments `{"to", "file.txt"}`] ] -] - -Likewise, attempting to create a relative path whose first segment contains a `":"` -could be interpreted as another scheme and a path: - -[table - [[URL] [Scheme] [Path] ] - [[[c++][snippet_modifying_path_7]] [(no scheme)] [Relative path `"path-to/file.txt"` with segments `{"path-to", "file.txt"}`]] - [[[c++][snippet_modifying_path_8]] [`"path"`] [Relative path `"to/file.txt"` with segments `{"to", "file.txt"}`] ] -] - -Modifying functions will properly adjust paths with malleable null prefixes -so that paths maintain their semantics without conflicting with the scheme -or authority components: - -[table - [[Code] [URL] [Path] ] - [[[c++][snippet_modifying_path_9]] [`"https:/.//path/to/file.txt"`] [Absolute path `"/.//path/to/file.txt"` with segments `{"", "path", "to", "file.txt"}`]] - [[[c++][snippet_modifying_path_10]] [`"./path:to/file.txt"`] [Relative path `"./path:to/file.txt"` with segments `{"path:to", "file.txt"}`] ] -] - -Given relative or absolute path, note that all algorithms preserve the path -semantics in lossless round-trip conversions between the URL path and their -segment container representations. Modifying functions will also adjust path -suffixes if a delimiter to the existing path segments would be missing: - -[table - [[Code] [URL] [Path] ] - [[[c++][snippet_modifying_path_11]] [`"path/to/file.txt"`] [Relative path `"path/to/file.txt"` with segments `{"path", "to", "file.txt"}`]] -] - -[heading Use Cases] - -The path comes after the URL authority, including the initial slash `/`: - -[teletype] -[table - [[Component] [Value] ] - [[URL] [`https://www.boost.org/doc/libs/`] ] - [[Path] [`/doc/libs/`] ] -] - -In this example, the path has three segments: - -[table - [[Component] [Value] ] - [[URL] [`https://www.boost.org/doc/libs/`] ] - [[Segment 1] [`doc`] ] - [[Segment 2] [`libs`] ] - [[Segment 3] [(empty segment)] ] -] - -Note that the final slash in `/doc/libs/` implies an extra -empty segment that would not exist in the path `/doc/libs`: - -[table - [[Component] [Value] ] - [[URL] [`https://www.boost.org/doc/libs`] ] - [[Segment 1] [`doc`] ] - [[Segment 2] [`libs`] ] -] - -A URL always contains a path, even if it is empty: - -[table - [[Component] [Value] ] - [[URL] [`https://www.boost.org`] ] - [[Path] [] ] -] - -Empty segments are also possible, resulting in consecutive slashes. - -[table - [[Component] [Value] ] - [[URL] [`https://www.boost.org//doc///libs`] ] - [[Path] [`//doc///libs`] ] - [[Segment 1] [(empty)] ] - [[Segment 2] [`doc`] ] - [[Segment 3] [(empty)] ] - [[Segment 4] [(empty)] ] - [[Segment 5] [`libs`] ] -] - -If the authority is present, the path needs to be empty or start with a -slash `/`. - -[table - [[Component] [Value] ] - [[URL] [`https://www.boost.org`] ] - [[Host] [`www.boost.org`] ] - [[Path] [] ] - [[Segments] [0] ] -] - -[table - [[Component] [Value] ] - [[URL] [`https://www.boost.org/`] ] - [[Host] [`www.boost.org`] ] - [[Path] [\/] ] - [[Segments] [0] ] -] - -[table - [[Component] [Value] ] - [[URL] [`https://www.boost.org//`] ] - [[Host] [`www.boost.org`] ] - [[Path] [\//] ] - [[Segments] [2] ] -] - -A path might begin with two slashes to indicate its first segment is empty. - -[table - [[Component] [Value] ] - [[URL] [`https://www.boost.org//doc/libs/`] ] - [[Authority] [`www.boost.org`] ] - [[Path] [`//doc/libs/`] ] - [[Segment 1] [(empty)] ] - [[Segment 2] [`doc`] ] - [[Segment 3] [`libs`] ] - [[Segment 4] [(empty)] ] -] - -However, beginning the path with double slashes is not possible when the -authority is absent, as the first segment path would be interpreted as the -authority. - -[table - [[Component] [Value] ] - [[URL] [`https://doc/libs/`] ] - [[Authority] [`doc`] ] - [[Path] [`/libs/`] ] - [[Segment 1] [`libs`] ] - [[Segment 2] [(empty)] ] -] - -For this reason, paths beginning with two slashes are typically avoided -altogether. - -Of the reserved character set for URLs, `":"` and `"@"` may appear unencoded within -paths. - -[table - [[Component] [Value] ] - [[URL] [`https://www.boost.org/doc@folder/libs:boost`] ] - [[Authority] [`www.boost.org`] ] - [[Path] [`/doc@folder/libs:boost`] ] - [[Segment 1] [`doc@folder`] ] - [[Segment 2] [`libs:boost`] ] -] - -[heading Member Functions] - -The functions for interacting with the path in a __url_view__ are as follows: - -[table Path Observers [ - [Function] - [Description] -][ - [[link url.ref.boost__urls__url_view.path `path`]] - [Return the path as a percent-decoded string] -][ - [[link url.ref.boost__urls__url_view.encoded_path `encoded_path`]] - [Return the path as a percent-encoded string] -][ - [[link url.ref.boost__urls__url_view.encoded_segments `encoded_segments`]] - [Return the path segments as a read-only container of percent-encoded strings.] -]] - -A URL path is usually interpreted as segments. The library -provides two read-only containers for interacting with the segments -in a URL's path: - -[table Segment Observers [ - [Type] - [Description] -][ - [[link url.ref.boost__urls__segments_view `segments_view`]] - [A read-only forward range of path segments returned as strings with percent-decoding applied.] -][ - [[link url.ref.boost__urls__segments_encoded_view `segments_encoded_view`]] - [A read-only forward range of path segments returned as percent-encoded strings.] -]] - -Segment views can be directly created by the parsing functions below. This provides -the guarantee that all constructed views contain valid path segments: - -[table Path Parsing Functions [ - [Function] - [Grammar] -][ - [[link url.ref.boost__urls__parse_path `parse_path`]] - [['any path]] -]] - -The functions for modifying paths in a __url__ are as follows: - -[table Path Modifiers [ - [Function] - [Description] -][ - [[link url.ref.boost__urls__url.set_path `set_path`]] - [Set the path] -][ - [[link url.ref.boost__urls__url.set_encoded_path `set_encoded_path`]] - [Set the encoded path] -][ - [[link url.ref.boost__urls__url.set_path_absolute `set_path_absolute`]] - [Set whether the path is absolute] -][ - [[link url.ref.boost__urls__url.normalize_path `normalize_path`]] - [Normalize path] -]] - -A URL path is usually interpreted as segments. A __url__ -provides two modifiable containers for interacting with the segments -in a URL's path: - -[table Segment View Types [ - [Type] - [Description] -][ - [[link url.ref.boost__urls__segments `segments`]] - [A forward range of path segments returned as strings with percent-decoding applied.] -][ - [[link url.ref.boost__urls__segments_encoded `segments_encoded`]] - [A forward range of path segments returned as percent-encoded strings.] -]] - -[/-----------------------------------------------------------------------------] - -[endsect] diff --git a/doc/qbk/5.4.query.qbk b/doc/qbk/5.4.query.qbk deleted file mode 100644 index aa67f50a..00000000 --- a/doc/qbk/5.4.query.qbk +++ /dev/null @@ -1,312 +0,0 @@ -[/ - Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) - - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - - Official repository: https://github.com/CPPAlliance/url -] - -[/-----------------------------------------------------------------------------] - -[#section.query] -[section:query Query] - -[heading Notation] - -The query component of a URL augments the information in the path to identify -a resource within the scope of the URL's scheme and authority. Unlike the URL -path, the query string contains non-hierarchical data. - -Although there is no mandatory syntax for interpreting queries, its strings -are often interpreted as key-value parameters delimited by the '&' or ';' -character. In addition to interacting with the query as a single string, -the library provides container adaptors modeling ranges of individual query -parameters. - -The URL below contains the query "[teletype]`?id=409&name=Joe&individual`" -with the three parameters "[teletype]`id=409`", "[teletype]`name=Joe`", and -"[teletype]`individual`": - -[teletype] -``` - https://www.example.com/get-customer.php?id=409&name=Joe&individual -``` - -A query is indicated by a leading question mark ('?') character as seen in -the BNF below: - -[table Query BNF [[ -``` - query = *( pchar / "/" / "?" ) - - absolute-URI = scheme ":" hier-part [ "?" query ] - - relative-ref = relative-part [ "?" query ] [ "#" fragment ] - - URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] - - URI-reference = URI / relative-ref -``` -]]] - -This table shows the BNF for a query string interpreted as parameters: - -[table Query Params BNF [[ -``` - query-params = query-param *( "&" query-param ) - - query-param = key [ "=" value ] - - key = *qpchar - value = *( qpchar / "=" ) - - qpchar = unreserved - / pct-encoded - / "!" / "$" / "'" / "(" / ")" - / "*" / "+" / "," / ";" - / ":" / "@" / "/" / "?" -``` -]]] - -[heading Observers] - -The function [link url.ref.boost__urls__url_view.query `query`] and -[link url.ref.boost__urls__url_view.encoded_query `encoded_query`] can -be used to obtain the query string from a __url_view__: - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_query_4] -][ -[teletype] -``` - https://www.example.com/get-customer.php?name=joe - encoded query: name=joe -``` -]]] - -These functions do not throw. If the URL has no query, -[link url.ref.boost__urls__url_view.query `query`] and -[link url.ref.boost__urls__url_view.encoded_query `encoded_query`] -return an empty string. The function -[link url.ref.boost__urls__url_view.has_query `has_query`] -can be used to determine whether this empty string means there is -no query or an empty query string in the URL. - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_query_5] -][ -[teletype] -``` - https://www.example.com/get-customer.php - has query: 0 - encoded query: -``` -]]] - -When using the query string as parameters, note that decoded -query strings might include ambiguous `&` and `=` characters. - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_query_7] -][ -[teletype] -``` - https://www.example.com/get-customer.php?name=John%26Doe - has query: 1 - encoded query: name=John%26Doe - query: name=John&Doe -``` -]]] - -In this example, the decoded query seems to imply there are -two query parameters while there is only one parameter whose -value includes a `&` character. In this case, the decoded -parameters can be accessed safely through a parameter view. - -The reason the decoded variant of query string is still allowed -is because query strings are not required to be interpreted as -query parameters, in which case the `&` character is not ambiguous. - -[heading Parameter View] - -Parameter views are lightweight references to the underlying path string. -Ownership of the string is not transferred; the caller is responsible for -ensuring that the lifetime of the string extends until the container is -destroyed. - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_query_1] -][ -[teletype] -``` - https://www.example.com/get-customer.php?id=409&name=Joe&individual - has query: 1 - query: id=409&name=Joe&individual - 3 parameters - parameter: - parameter: - parameter: individual -``` -]]] - -Each parameter is represented as a structure with fields to refer to the -key and value. An extra field `has_value` is used to indicate whether -the value is absent. - -[heading Use Cases] - -The most common formulation for the query in a URL is to define a set of -key and value pairs of percent-encoded strings, using the ampersand ('&') -character to delimit each pair after the first. In contexts where a query -is interpreted as key/value pairs, it is called the ['query parameters], -['query params], or just [*params]. - -In addition to accessor functions which treat the query as a single string, -the library provides container adaptors modeling ranges of query parameters. -The following URL contains three query parameters: - -[table - [[Component] [Value] ] - [[URL] [`https://www.example.com/get-customer.php?id=409&name=Joe&individual`] ] - [[Has Query] [Yes] ] - [[Query] [`id=409&name=Joe&individual`] ] - [[Parameter 1] [Key `id`, Value `409`] ] - [[Parameter 2] [Key `name`, Value `Joe`] ] - [[Parameter 3] [Key `individual`, No value] ] -] - -Note that a parameter value might be either empty or absent. The -presence of a value is indicated by the presence of an equals ('=') -sign appearing after the key. This means the value may be absent, -empty, or contain characters. - -The key of a query parameter might also be empty. This means that -a query parameter may be completely empty. In this case the -parameter is said to have a zero-length or empty key, and -no value. - -The URL below demonstrate all the ways that keys and values may -appear in query parameters: - -[table - [[Component] [Value] ] - [[URL] [`https://www.example.com/get-customer.php?key-1=value-1&key-2=&key-3&&=value-5`] ] - [[Has Query] [Yes] ] - [[Query] [`key-1=value-1&key-2=&key-3&&=value-5`] ] - [[Parameter 1] [Key `key-1`, Value `value-1`] ] - [[Parameter 2] [Key `key-2`, Value (empty)] ] - [[Parameter 3] [Key `key-3`, No value] ] - [[Parameter 4] [Key (empty), No value] ] - [[Parameter 5] [Key (empty), Value `value-5`] ] -] - -The URL reserved characters `:`, `@`, `?`, and `/` may appear -unencoded with URL queries, as they are not ambiguous with -other URL components. - -[table - [[Component] [Value] ] - [[URL] [`https://www.example.com/get-customer.php?email=joe@email.com&code=a:2@/!`] ] - [[Has Query] [Yes] ] - [[Query] [`email=joe@email.com&code=a:2@/!`] ] - [[Parameter 1] [Key `email`, Value `joe@email.com`] ] - [[Parameter 2] [Key `code`, Value `a:2@/!`] ] -] - -[heading Member Functions] - -The functions for interacting with the query in a __url_view__ are as follows: - -[table Query Observers [ - [Function] - [Description] -][ - [[link url.ref.boost__urls__url_view.has_query `has_query`]] - [Return true if a query is present] -][ - [[link url.ref.boost__urls__url_view.query `query`]] - [Return the query as a string with percent-decoding applied.] -][ - [[link url.ref.boost__urls__url_view.encoded_query `encoded_query`]] - [Return the percent-encoded query.] -]] - -A URL query is usually interpreted as parameters. A __url_view__ -provides two observers and read-only containers for interacting -with the parameters in a URL's query: - -[table Query Params Observers [ - [Function] - [Description] -][ - [[link url.ref.boost__urls__url_view.encoded_params `encoded_params`]] - [Return the query parameters as a read-only container of percent-encoded strings.] -][ - [[link url.ref.boost__urls__url_view.params `params`]] - [Return the query parameters as a read-only container of strings with percent-decoding applied.] -]] - -[table Params View Types [ - [Type] - [Description] -][ - [[link url.ref.boost__urls__params_view `params_view`]] - [A read-only forward range of query parameters returned as strings with percent-decoding applied.] -][ - [[link url.ref.boost__urls__params_encoded_view `params_encoded_view`]] - [A read-only forward range of query parameters returned as percent-encoded strings.] -]] - -The functions for modifying the query in a __url__ are as follows: - -[table Query Modifiers [ - [Function] - [Description] -][ - [[link url.ref.boost__urls__url.set_query `set_query`]] - [Set query] -][ - [[link url.ref.boost__urls__url.set_encoded_query `set_encoded_query`]] - [Set encoded query] -][ - [[link url.ref.boost__urls__url.remove_query `remove_query`]] - [Remove query] -][ - [[link url.ref.boost__urls__url.normalize_query `normalize_query`]] - [Normalize query] -]] - -A URL query is usually interpreted as parameters. A __url__ -provides two modifiable containers for interacting with the parameters -in a URL's query: - -[table Query Params Modifiers [ - [Function] - [Description] -][ - [[link url.ref.boost__urls__url.params `params`]] - [Return the query parameters as a modifiable container of strings with percent-decoding applied.] -][ - [[link url.ref.boost__urls__url.encoded_params `encoded_params`]] - [Return the query parameters as a modifiable container of percent-encoded strings.] -]] - -[table Params View Types [ - [Type] - [Description] -][ - [[link url.ref.boost__urls__params `params`]] - [A modifiable forward range of query parameters returned as strings with percent-decoding applied.] -][ - [[link url.ref.boost__urls__params_encoded `params_encoded`]] - [A modifiable forward range of query parameters returned as percent-encoded strings.] -]] - -[/-----------------------------------------------------------------------------] - -[endsect] diff --git a/doc/qbk/5.5.fragment.qbk b/doc/qbk/5.5.fragment.qbk deleted file mode 100644 index ac64c835..00000000 --- a/doc/qbk/5.5.fragment.qbk +++ /dev/null @@ -1,194 +0,0 @@ -[/ - Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) - - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - - Official repository: https://github.com/CPPAlliance/url -] - -[/-----------------------------------------------------------------------------] - -[section Fragment] - -[heading Notation] - -The fragment identifier in a URL provides further refinement of the -specification of the resource, including additional identifying information. -It provides directions to a secondary resource related to such main resource, -such as the section in an article or a time-point in a video. - -As usual, its semantics vary depending on the scheme, authority, path, -and media type of the resource. In HTML, fragments are used as internal -page references. This usage is called a "named anchor," referring to a -section within a web page. - -The URL below contains the fragment "section2": - -[teletype] -``` - https://www.example.com/index.html#section2 - \____/\_______________/\_________/\_______/ - scheme authority path fragment -``` - -A fragment appearing in a URL is always preceded by the number sign ('#'). -This makes a URL with a fragment of zero length distinguishable from a URL -with no fragment. - -The fragment grammar is defined as follows: - -[table Fragment BNF [[ -[teletype] -``` - fragment = *( pchar / "/" / "?" ) - - relative-ref = relative-part [ "?" query ] [ "#" fragment ] - - URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] - - URI-reference = URI / relative-ref -``` -]]] - -[heading Observers] - -Analogous to other components, the functions -[link url.ref.boost__urls__url_view.encoded_fragment `fragment`] and -[link url.ref.boost__urls__url_view.encoded_fragment `encoded_fragment`] -can be used to obtain the fragment from a __url_view__: - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_fragment_1] -][ -[teletype] -``` - https://www.example.com/index.html#section%202 - has fragment: 1 - fragment: section 2 - encoded fragment: section%202 -``` -]]] - -These functions do not throw. If the URL has no fragment, they -return an empty string. The function -[link url.ref.boost__urls__url_view.has_fragment `has_fragment`] -can be used to determine whether this empty string means there is -no fragment or an empty fragment string in the URL. - -[table [[Code][Output]] [[ -[c++] -[snippet_parsing_fragment_2_a] -][ -[teletype] -``` - https://www.example.com/index.html# - has fragment: 1 - fragment: -``` -]] -[[ -[c++] -[snippet_parsing_fragment_2_b] -][ -[teletype] -``` - https://www.example.com/index.html - has fragment: 0 - fragment: -``` -]][[ -[c++] -[snippet_parsing_fragment_3] -][ -[teletype] -``` - https://www.example.com/index.html - has fragment: 1 - fragment: code :a@b?c/d -``` -]]] - -[heading Use Cases] - -URL fragments are usually interpreted as a single string. - -[table - [[Component] [Value] ] - [[URL] [`https://www.example.com/index.html#section%202`] ] - [[Has Fragment] [Yes] ] - [[Encoded Fragment] [`section%202`] ] - [[Fragment] [`section 2`] ] -] - -The URL fragment might also be empty or absent. - -[table - [[Component] [Value] ] - [[URL] [`https://www.example.com/index.html#`] ] - [[Has Fragment] [Yes] ] - [[Encoded Fragment] [(empty)] ] - [[Fragment] [(empty)] ] -] - -[table - [[Component] [Value] ] - [[URL] [`https://www.example.com/index.html`] ] - [[Has Fragment] [No] ] - [[Encoded Fragment] [(No fragment)] ] - [[Fragment] [(No fragment)] ] -] - -The URL reserved characters `:`, `@`, `?`, and `/` may appear -unencoded with URL fragments, as they are not ambiguous with -other URL components. - -[table - [[Component] [Value] ] - [[URL] [`https://www.example.com/index.html#code%20:a@b?c/d`] ] - [[Has Fragment] [Yes] ] - [[Encoded Fragment] [`code%20:a@b?c/d`] ] - [[Fragment] [`code :a@b?c/d`] ] -] - -[heading Member Functions] - -The functions for inspecting the fragment in a __url_view__ are as follows: - -[table Fragment Observers[ - [Function] - [Description] -][ - [[link url.ref.boost__urls__url_view.has_fragment `has_fragment`]] - [Return true if a fragment is present] -][ - [[link url.ref.boost__urls__url_view.fragment `fragment`]] - [Return the fragment as a string with percent-decoding applied.] -][ - [[link url.ref.boost__urls__url_view.encoded_fragment `encoded_fragment`]] - [Return the fragment as a percent-encoded string.] -]] - -The functions for modifying the fragment in a __url__ are as follows: - -[table Fragment Observers[ - [Function] - [Description] -][ - [[link url.ref.boost__urls__url.set_fragment `set_fragment`]] - [Set fragment] -][ - [[link url.ref.boost__urls__url.set_encoded_fragment `set_encoded_fragment`]] - [Set encoded fragment] -][ - [[link url.ref.boost__urls__url.remove_fragment `remove_fragment`]] - [Remove fragment] -][ - [[link url.ref.boost__urls__url.normalize_fragment `normalize_fragment`]] - [Normalize fragment] -]] - -[/-----------------------------------------------------------------------------] - -[endsect] diff --git a/doc/qbk/8.0.examples.qbk b/doc/qbk/6.0.examples.qbk similarity index 100% rename from doc/qbk/8.0.examples.qbk rename to doc/qbk/6.0.examples.qbk diff --git a/doc/qbk/2.1.HelpCard.qbk b/doc/qbk/7.1.HelpCard.qbk similarity index 91% rename from doc/qbk/2.1.HelpCard.qbk rename to doc/qbk/7.1.HelpCard.qbk index f89c5148..eebf2d9c 100644 --- a/doc/qbk/2.1.HelpCard.qbk +++ b/doc/qbk/7.1.HelpCard.qbk @@ -7,7 +7,7 @@ Official repository: https://github.com/CPPAlliance/url ] -[section Help Card] +[section:helpcard Help Card] [$url/images/HelpCard.svg] diff --git a/example/magnet/magnet.cpp b/example/magnet/magnet.cpp index 26b5b7ed..aa07d91d 100644 --- a/example/magnet/magnet.cpp +++ b/example/magnet/magnet.cpp @@ -24,12 +24,12 @@ namespace urls = boost::urls; -/// Functor to identify a magnet "exact topic" +/// Callable to identify a magnet "exact topic" /** - This functor evaluates if a query parameter + This callable evaluates if a query parameter represents a magnet "exact topic". - This functor is used as a filter for + This callable is used as a filter for the topics_view. */ struct is_exact_topic { @@ -37,16 +37,16 @@ struct is_exact_topic { operator()(urls::query_param_view p); }; -/// Functor to identify a magnet url parameter +/// Callable to identify a magnet url parameter /** - This functor evaluates if a query parameter + This callable evaluates if a query parameter has a given key and a url as its value. These urls are percent-encoded twice, which means we need to decode it once before attempting to parse it. - This functor is used as a filter for + This callable is used as a filter for the keys_view. */ template @@ -65,12 +65,12 @@ public: operator()(urls::query_param_view p); }; -/// Functor to convert param values to urls +/// Callable to convert param values to urls /** - This functor converts the value of a + This callable converts the value of a query parameter into a urls::url_view. - This functor is used as a transform + This callable is used as a transform function for the topics_view. */ struct to_url { @@ -78,12 +78,12 @@ struct to_url { operator()(urls::query_param_view p); }; -/// Functor to convert param values to urls::pct_encoded_view +/// Callable to convert param values to urls::pct_encoded_view /** - This functor converts the value of a + This callable converts the value of a query parameter into a urls::pct_encoded_view. - This functor is used as a transform + This callable is used as a transform function for the keys_view. */ struct to_decoded_value { @@ -94,16 +94,16 @@ struct to_decoded_value { } }; -/// Functor to convert param values to info_hashes +/// Callable to convert param values to info_hashes /** - This functor converts the value of a + This callable converts the value of a query parameter into a urls::string_view with its infohash. The infohash hash is a parameter of an exact topic field in the magnet link. - This functor is used as a transform + This callable is used as a transform function for the info_hashes_view. */ struct to_infohash { @@ -111,16 +111,16 @@ struct to_infohash { operator()(urls::query_param_view p); }; -/// Functor to convert param values to protocols +/// Callable to convert param values to protocols /** - This functor converts the value of a + This callable converts the value of a query parameter into a urls::string_view with its protocol. The protocol is a parameter of an exact topic field in the magnet link. - This functor is used as a transform + This callable is used as a transform function for the protocols_view. */ struct to_protocol { diff --git a/include/boost/url/url.hpp b/include/boost/url/url.hpp index 93f61c07..1a34cef0 100644 --- a/include/boost/url/url.hpp +++ b/include/boost/url/url.hpp @@ -79,7 +79,7 @@ public: invalidated. */ BOOST_URL_DECL - ~url(); + virtual ~url(); /** Constructor diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index b1562341..0041eebf 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -104,9 +104,12 @@ target_link_libraries(boost_url_tests PRIVATE Boost::unordered) add_test(NAME boost_url_tests COMMAND boost_url_tests) add_dependencies(boost_url_all_tests boost_url_tests) -if (TARGET boost_filesystem AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - get_target_property(FS_IS_IMPORTED boost_filesystem IMPORTED) - if (FS_IS_IMPORTED) - target_compile_options(boost_filesystem PUBLIC -Wno-error=restrict) +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(boost_url_tests PUBLIC -Wno-error=unused-but-set-variable) + if (TARGET boost_filesystem AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + get_target_property(FS_IS_IMPORTED boost_filesystem IMPORTED) + if (FS_IS_IMPORTED) + target_compile_options(boost_filesystem PUBLIC -Wno-error=restrict) + endif() endif() endif() \ No newline at end of file diff --git a/test/unit/snippets.cpp b/test/unit/snippets.cpp index af5e5f43..78ce06a8 100644 --- a/test/unit/snippets.cpp +++ b/test/unit/snippets.cpp @@ -55,42 +55,47 @@ using_url_views() //] } - url_view u = parse_uri( s ).value(); - - //[snippet_accessing_1 - std::cout << - "url : " << u << "\n" - "scheme : " << u.scheme() << "\n" - "authority : " << u.authority() << "\n" - "userinfo : " << u.userinfo() << "\n" - "user : " << u.user() << "\n" - "password : " << u.password() << "\n" - "host : " << u.host() << "\n" - "port : " << u.port() << "\n" - "path : " << u.path() << "\n" - "query : " << u.query() << "\n" - "fragment : " << u.fragment() << "\n"; - //] + { + //[snippet_accessing_1 + string_view s = "https://user:pass@example.com:443/path/to/my%2dfile.txt?id=42&name=John%20Doe+Jingleheimer%2DSchmidt#page%20anchor"; + url_view u( s ); + assert(u.scheme() == "https"); + assert(u.authority().string() == "user:pass@example.com:443"); + assert(u.userinfo() == "user:pass"); + assert(u.user() == "user"); + assert(u.password() == "pass"); + assert(u.host() == "example.com"); + assert(u.port() == "443"); + assert(u.path() == "/path/to/my-file.txt"); + assert(u.query() == "id=42&name=John Doe Jingleheimer-Schmidt"); + assert(u.fragment() == "page anchor"); + //] + } + url_view u( s ); //[snippet_accessing_1b for (auto seg: u.segments()) std::cout << seg << "\n"; std::cout << "\n"; + for (auto param: u.params()) std::cout << param.key << ": " << param.value << "\n"; + std::cout << "\n"; //] { //[snippet_accessing_2a url_view u1 = parse_uri( "http://www.example.com" ).value(); - std::cout << "fragment 1 : " << u1.fragment() << "\n\n"; + assert(u1.fragment().empty()); + assert(!u1.has_fragment()); //] } { //[snippet_accessing_2b url_view u2 = parse_uri( "http://www.example.com/#" ).value(); - std::cout << "fragment 2 : " << u2.fragment() << "\n\n"; + assert(u2.fragment().empty()); + assert(u2.has_fragment()); //] } @@ -274,18 +279,56 @@ using_urls() void parsing_urls() { - //[snippet_parsing_url_1 - result< url_view > r = parse_uri( "https://www.example.com/path/to/file.txt" ); - if( r.has_value() ) // parsing was successful { - url_view u = r.value(); // extract the url_view - std::cout << u << "\n"; // format the URL to cout + //[snippet_parsing_url_1 + result< url_view > r = parse_uri( "https://www.example.com/path/to/file.txt" ); + //] + boost::ignore_unused(r); } - else + { - std::cout << r.error().message(); // parsing failure; print error + //[snippet_parsing_url_1b + url_view u( "https://www.example.com/path/to/file.txt" ); + //] + + //[snippet_parsing_url_1bb + std::cout << u; + //] + } + + { + //[snippet_parsing_url_1bc + result< url > rv = parse_uri_reference( "https://www.example.com/path/to/file.txt" ); + + static_assert( std::is_convertible< result< url_view >, result< url > >::value, "" ); + //] + boost::ignore_unused(rv); + } + + { + //[snippet_parsing_url_1bd + result< static_url<1024> > rv = parse_uri_reference( "https://www.example.com/path/to/file.txt" ); + + static_assert( std::is_convertible< result< static_url<1024> >, result< url > >::value, "" ); + //] + boost::ignore_unused(rv); + } + + { + //[snippet_parsing_url_1c + result< url_view > r0 = parse_relative_ref( "/path/to/file.txt" ); + assert( r0.has_value() ); + //] + //[snippet_parsing_url_1d + result< url_view > r1 = parse_uri( "https://www.example.com" ); + assert( r1.has_value() ); + url dest; + error_code ec; + resolve(r1.value(), r0.value(), dest, ec); + assert(dest.string() == "https://www.example.com/path/to/file.txt"); + //] + boost::ignore_unused(dest); } - //] //[snippet_parsing_url_2 // This will hold our copy @@ -323,42 +366,85 @@ parsing_urls() std::cout << v << "\n"; // and it's mutable - v.set_encoded_fragment("anchor"); + v.set_fragment("anchor"); + // path/to/file.txt#anchor std::cout << v << "\n"; //] } } +void +parsing_components() +{ + { + //[snippet_components_1 + string_view s = "https://user:pass@example.com:443/path/to/my%2dfile.txt?id=42&name=John%20Doe+Jingleheimer%2DSchmidt#page%20anchor"; + url_view u( s ); + assert(u.scheme() == "https"); + assert(u.authority().string() == "user:pass@example.com:443"); + assert(u.path() == "/path/to/my-file.txt"); + assert(u.query() == "id=42&name=John Doe Jingleheimer-Schmidt"); + assert(u.fragment() == "page anchor"); + //] + } + { + //[snippet_components_2a + url_view u( "https://www.ietf.org/rfc/rfc2396.txt" ); + assert(u.scheme() == "https"); + assert(u.host() == "www.ietf.org"); + assert(u.path() == "/rfc/rfc2396.txt"); + //] + } + { + //[snippet_components_2b + url_view u( "ftp://ftp.is.co.za/rfc/rfc1808.txt" ); + assert(u.scheme() == "ftp"); + assert(u.host() == "ftp.is.co.za"); + assert(u.path() == "/rfc/rfc1808.txt"); + //] + } + { + //[snippet_components_2c + url_view u( "mailto:John.Doe@example.com" ); + assert(u.scheme() == "mailto"); + assert(u.path() == "John.Doe@example.com"); + //] + } + { + //[snippet_components_2d + url_view u( "urn:isbn:096139210x" ); + assert(u.scheme() == "urn"); + assert(u.path() == "isbn:096139210x"); + //] + } + { + //[snippet_components_2e + url_view u( "magnet:?xt=urn:btih:d2474e86c95b19b8bcfdb92bc12c9d44667cfa36" ); + assert(u.scheme() == "magnet"); + assert(u.path() == ""); + assert(u.query() == "xt=urn:btih:d2474e86c95b19b8bcfdb92bc12c9d44667cfa36"); + //] + } +} + void parsing_scheme() { { //[snippet_parsing_scheme_1 - string_view s = "mailto:name@email.com"; - url_view u = parse_uri( s ).value(); - std::cout << u.scheme() << "\n"; - //] - } - { - string_view s = "mailto:name@email.com"; - //[snippet_parsing_scheme_2 - url_view u = parse_uri( s ).value(); - if (u.has_scheme()) - { - std::cout << u.scheme() << "\n"; - } + url_view u("mailto:name@email.com" ); + assert( u.has_scheme() ); + assert( u.scheme() == "mailto" ); //] + boost::ignore_unused(u); } { //[snippet_parsing_scheme_3 - string_view s = "file://host/path/to/file"; - url_view u = parse_uri( s ).value(); - if (u.scheme_id() == scheme::file) - { - // handle file - } + url_view u("file://host/path/to/file" ); + assert( u.scheme_id() == scheme::file ); //] + boost::ignore_unused(u); } } @@ -367,19 +453,15 @@ parsing_authority() { { //[snippet_parsing_authority_1 - string_view s = "https:///path/to_resource"; - url_view u = parse_uri( s ).value(); - std::cout << u << "\n" - "scheme: " << u.scheme() << "\n" - "has authority: " << u.has_authority() << "\n" - "authority: " << u.authority() << "\n" - "path: " << u.encoded_path() << "\n"; + url_view u( "https:///path/to_resource" ); + assert( u.authority().string() == ""); + assert( u.has_authority() ); + assert( u.path() == "/path/to_resource" ); //] } { //[snippet_parsing_authority_2 - string_view s = "https://www.boost.org"; - url_view u = parse_uri( s ).value(); + url_view u("https://www.boost.org" ); std::cout << "scheme: " << u.scheme() << "\n" "has authority: " << u.has_authority() << "\n" "authority: " << u.authority() << "\n" @@ -387,20 +469,18 @@ parsing_authority() //] } { - //[snippet_parsing_authority_3 - string_view s = "https://www.boost.org/users/download/"; - url_view u = parse_uri( s ).value(); - std::cout << u << "\n" - "scheme: " << u.scheme() << "\n" - "has authority: " << u.has_authority() << "\n" - "authority: " << u.authority() << "\n" - "path: " << u.path() << "\n"; + //[snippet_parsing_authority_3a + url_view u( "https://www.boost.org/users/download/" ); + assert(u.has_authority()); + authority_view a = u.authority(); + //] + //[snippet_parsing_authority_3b + assert(a.host() == "www.boost.org"); //] } { //[snippet_parsing_authority_4 - string_view s = "https://www.boost.org/"; - url_view u = parse_uri( s ).value(); + url_view u( "https://www.boost.org/" ); std::cout << "scheme: " << u.scheme() << "\n" "has authority: " << u.has_authority() << "\n" "authority: " << u.authority() << "\n" @@ -409,8 +489,7 @@ parsing_authority() } { //[snippet_parsing_authority_5 - string_view s = "mailto:John.Doe@example.com"; - url_view u = parse_uri( s ).value(); + url_view u( "mailto:John.Doe@example.com" ); std::cout << "scheme: " << u.scheme() << "\n" "has authority: " << u.has_authority() << "\n" "authority: " << u.authority() << "\n" @@ -419,8 +498,7 @@ parsing_authority() } { //[snippet_parsing_authority_6 - string_view s = "mailto://John.Doe@example.com"; - url_view u = parse_uri( s ).value(); + url_view u( "mailto://John.Doe@example.com" ); std::cout << u << "\n" "scheme: " << u.scheme() << "\n" "has authority: " << u.has_authority() << "\n" @@ -430,8 +508,7 @@ parsing_authority() } { //[snippet_parsing_authority_7 - string_view s = "https://john.doe@www.example.com:123/forum/questions/"; - url_view u = parse_uri( s ).value(); + url_view u( "https://john.doe@www.example.com:123/forum/questions/" ); std::cout << "scheme: " << u.scheme() << "\n" "has authority: " << u.has_authority() << "\n" "authority: " << u.authority() << "\n" @@ -443,96 +520,131 @@ parsing_authority() } { //[snippet_parsing_authority_8 - string_view s = "https://john.doe@www.example.com:123/forum/questions/"; - url_view u = parse_uri( s ).value(); - std::cout << u << "\n" - "host: " << u.host() << "\n" - "host and port: " << u.encoded_host_and_port() << "\n" - "port: " << u.port() << "\n" - "port number: " << u.port_number() << "\n"; + url_view u( "https://john.doe@www.example.com:123/forum/questions/" ); + assert(u.host() == "www.example.com"); + assert(u.port() == "123"); //] } { //[snippet_parsing_authority_9 - string_view s = "https://john.doe@192.168.2.1:123/forum/questions/"; - url_view u = parse_uri( s ).value(); - std::cout << u << "\n" - "host: " << u.host() << "\n" - "host and port: " << u.encoded_host_and_port() << "\n" - "port: " << u.port() << "\n" - "port number: " << u.port_number() << "\n"; + url_view u( "https://john.doe@192.168.2.1:123/forum/questions/" ); + assert(u.host() == "192.168.2.1"); + assert(u.port() == "123"); //] } { + //[snippet_parsing_authority_9b + url_view u( "https://www.example.com" ); + assert(u.host() == "www.example.com"); + assert(u.host() == u.encoded_host()); + //] + } + { + struct resolve_f { + pct_encoded_view + operator()(pct_encoded_view h) + { + return h; + } + } resolve; + struct write_request_f { + void operator()(pct_encoded_view) {} + void operator()(ipv4_address) {} + void operator()(ipv6_address) {} + } write_request; + //[snippet_parsing_authority_10 - string_view s = "https://www.boost.org/users/download/"; - url_view u = parse_uri( s ).value(); + url_view u( "https://www.boost.org/users/download/" ); switch (u.host_type()) { case host_type::name: - // resolve name - case host_type::ipv4: - case host_type::ipv6: - case host_type::ipvfuture: - // connect to ip + write_request(resolve(u.host())); break; - case host_type::none: - // handle empty host URL + case host_type::ipv4: + write_request(u.ipv4_address()); + break; + case host_type::ipv6: + write_request(u.ipv6_address()); + break; + default: break; } //] } { - //[snippet_parsing_authority_11 - string_view s = "https://john.doe:123456@www.somehost.com/forum/questions/"; - url_view u = parse_uri( s ).value(); - std::cout << u << "\n\n" - "has_userinfo: " << u.has_userinfo() << "\n" - "userinfo: " << u.userinfo() << "\n" - "user: " << u.user() << "\n\n" - "has_password: " << u.has_password() << "\n" - "password: " << u.password() << "\n"; + //[snippet_parsing_authority_10a + url_view u( "https:///path/to_resource" ); + assert( u.has_authority() ); + assert( u.authority().string().empty() ); + assert( u.path() == "/path/to_resource" ); + //] + } + { + //[snippet_parsing_authority_10b + url_view u( "https://www.boost.org" ); + assert( u.host() == "www.boost.org" ); + assert( u.path().empty() ); + //] + } + { + //[snippet_parsing_authority_10c + url_view u( "https://www.boost.org/users/download/" ); + assert( u.host() == "www.boost.org" ); + assert( u.path() == "/users/download/" ); + //] + } + { + //[snippet_parsing_authority_10d + url_view u( "https://www.boost.org/" ); + assert( u.host() == "www.boost.org" ); + assert( u.path() == "/" ); + //] + } + { + //[snippet_parsing_authority_10e + url_view u( "mailto:John.Doe@example.com" ); + assert( !u.has_authority() ); + assert( u.path() == "John.Doe@example.com" ); + //] + } + { + //[snippet_parsing_authority_10f + url_view u( "mailto://John.Doe@example.com" ); + assert( u.authority().string() == "John.Doe@example.com" ); + assert( u.path().empty() ); + //] + } + { + //[snippet_parsing_authority_11a + url_view u( "https://john.doe@www.example.com:123/forum/questions/" ); + assert(u.userinfo() == "john.doe"); + assert(u.port() == "123"); + //] + } + { + //[snippet_parsing_authority_11b + url_view u( "https://john.doe:123456@www.somehost.com/forum/questions/" ); + assert(u.userinfo() == "john:doe"); + assert(u.user() == "john"); + assert(u.password() == "doe"); //] } { - std::cout << "snippet_parsing_authority_12\n"; //[snippet_parsing_authority_12 - string_view s = "www.example.com:80"; - authority_view a = parse_authority( s ).value(); - std::cout << a << "\n\n" - // host and port - "host_and_port: " << a.encoded_host_and_port() << "\n" - "host: " << a.host() << "\n" - "port: " << a.port() << "\n" - "port number: " << a.port_number() << "\n\n" - // userinfo - "has_userinfo: " << a.has_userinfo() << "\n" - "userinfo: " << a.userinfo() << "\n" - // user - "user: " << a.user() << "\n\n" - // password - "has_password: " << a.has_password() << "\n" - "password: " << a.password() << "\n"; + authority_view a = parse_authority( "www.example.com:80" ).value(); + assert(!a.has_userinfo()); + assert(a.host() == "www.example.com"); + assert(a.port() == "80"); //] } { //[snippet_parsing_authority_13 - string_view s = "user:pass@www.example.com:443"; - authority_view a = parse_authority( s ).value(); - std::cout << a << "\n\n" - // host and port - "host_and_port: " << a.encoded_host_and_port() << "\n" - "host: " << a.host() << "\n" - "port: " << a.port() << "\n" - "port number: " << a.port_number() << "\n\n" - // userinfo - "has_userinfo: " << a.has_userinfo() << "\n" - "userinfo: " << a.userinfo() << "\n\n" - // user - "user: " << a.user() << "\n\n" - // password - "has_password: " << a.has_password() << "\n" - "password: " << a.password() << "\n"; + authority_view a = parse_authority( "user:pass@www.example.com:443" ).value(); + assert(a.userinfo() == "user:pass"); + assert(a.user() == "user"); + assert(a.password() == "pass"); + assert(a.host() == "www.example.com"); + assert(a.port() == "443"); //] } } @@ -540,60 +652,61 @@ parsing_authority() void parsing_path() { + { + //[snippet_parsing_path_0 + url_view u("http://www.example.com/path/to/file.txt"); + assert(u.path() == "/path/to/file.txt"); + segments_view segs = u.segments(); + auto it = segs.begin(); + assert( *it++ == "path" ); + assert( *it++ == "to" ); + assert( *it == "file.txt" ); + //] + boost::ignore_unused(it); + } { //[snippet_parsing_path_1 - string_view s = "https://www.boost.org/doc/libs/"; - url_view u = parse_uri(s).value(); - std::cout << u << "\n" - << "path: " << u.path() << "\n" - << "path: " << u.encoded_path() << "\n" - << "segments: " << u.segments() << "\n" - << "encoded_segments: " << u.encoded_segments() << "\n"; + url_view u("https://www.boost.org/doc/the%20libs/"); + assert(u.path() == "/doc/the libs/"); + assert(u.encoded_path() == "/doc/the%20libs/"); //] //[snippet_parsing_path_1_b std::cout << u.encoded_segments().size() << " segments\n"; for (auto seg: u.encoded_segments()) - { std::cout << "segment: " << seg << "\n"; - } //] } - { //[snippet_parsing_path_2 - string_view s = "https://www.boost.org/doc/libs"; - url_view u = parse_uri(s).value(); + url_view u("https://www.boost.org/doc/libs"); std::cout << u.segments().size() << " segments\n"; for (auto seg: u.segments()) - { std::cout << "segment: " << seg << "\n"; - } //] } - { //[snippet_parsing_path_3 - string_view s = "https://www.boost.org"; - url_view u = parse_uri(s).value(); - std::cout << u << "\n" - << "path: " << u.encoded_path() << "\n"; + url_view u("https://www.boost.org"); + assert(u.path().empty()); + assert(u.encoded_path().empty()); + //] + } + { + //[snippet_parsing_path_3a + assert( url_view("urn:isbn:096139210x").path() == "isbn:096139210x" ); //] } - { //[snippet_parsing_path_4 - string_view s = "https://www.boost.org//doc///libs"; - url_view u = parse_uri(s).value(); + url_view u("https://www.boost.org//doc///libs"); std::cout << u << "\n" "path: " << u.encoded_path() << "\n" "encoded segments: " << u.encoded_segments() << "\n" "segments: " << u.segments() << "\n"; std::cout << u.encoded_segments().size() << " segments\n"; for (auto seg: u.encoded_segments()) - { std::cout << "segment: " << seg << "\n"; - } //] } @@ -601,7 +714,7 @@ parsing_path() { //[snippet_parsing_path_5_a string_view s = "https://www.boost.org"; - url_view u = parse_uri(s).value(); + url_view u(s); std::cout << u << "\n" << "path: " << u.encoded_host() << "\n" << "path: " << u.encoded_path() << "\n" @@ -611,7 +724,7 @@ parsing_path() { //[snippet_parsing_path_5_b string_view s = "https://www.boost.org/"; - url_view u = parse_uri(s).value(); + url_view u(s); std::cout << u << "\n" << "host: " << u.encoded_host() << "\n" << "path: " << u.encoded_path() << "\n" @@ -621,7 +734,7 @@ parsing_path() { //[snippet_parsing_path_5_c string_view s = "https://www.boost.org//"; - url_view u = parse_uri(s).value(); + url_view u(s); std::cout << u << "\n" << "host: " << u.encoded_host() << "\n" << "path: " << u.encoded_path() << "\n" @@ -632,60 +745,121 @@ parsing_path() { //[snippet_parsing_path_6 - string_view s = "https://www.boost.org//doc/libs/"; - url_view u = parse_uri(s).value(); + url_view u("https://www.boost.org//doc/libs/"); std::cout << u << "\n" "authority: " << u.encoded_authority() << "\n" "path: " << u.encoded_path() << "\n"; std::cout << u.encoded_segments().size() << " segments\n"; for (auto seg: u.encoded_segments()) - { std::cout << "segment: " << seg << "\n"; - } //] } { //[snippet_parsing_path_7 - string_view s = "https://doc/libs/"; - url_view u = parse_uri(s).value(); + url_view u("https://doc/libs/"); std::cout << u << "\n" "authority: " << u.encoded_authority() << "\n" "path: " << u.encoded_path() << "\n"; std::cout << u.encoded_segments().size() << " segments\n"; for (auto seg: u.encoded_segments()) - { std::cout << "segment: " << seg << "\n"; - } //] } { //[snippet_parsing_path_8 - string_view s = "https://www.boost.org/doc@folder/libs:boost"; - url_view u = parse_uri(s).value(); + url_view u("https://www.boost.org/doc@folder/libs:boost"); std::cout << u << "\n" "authority: " << u.encoded_authority() << "\n" "path: " << u.encoded_path() << "\n"; std::cout << u.encoded_segments().size() << " segments\n"; for (auto seg: u.encoded_segments()) - { std::cout << "segment: " << seg << "\n"; - } //] } { //[snippet_parsing_path_9 - string_view s = "/doc/libs"; - segments_view p = parse_path(s).value(); - std::cout << "path: " << p << "\n"; - std::cout << p.size() << " segments\n"; - for (auto seg: p) - { - std::cout << "segment: " << seg << "\n"; - } + segments_view segs = parse_path("/doc/libs").value(); + assert( segs.size() == 2 ); //] + boost::ignore_unused(segs); + } + + { + //[snippet_parsing_path_use_case_1 + url_view u("https://www.boost.org/doc/libs/"); + assert(u.host() == "www.boost.org"); + assert(u.path() == "/doc/libs/"); + //] + boost::ignore_unused(u); + } + { + //[snippet_parsing_path_use_case_2 + assert( parse_uri("https://www.boost.org").has_value() ); + assert( parse_uri("https://www.boost.org/").has_value() ); + assert( parse_uri("https://www.boost.org//").has_value() ); + //] + } + { + //[snippet_parsing_path_use_case_3 + assert( url_view("https://www.boost.org").path().empty() ); + //] + } + { + //[snippet_parsing_path_use_case_4 + url_view u("https://www.boost.org/doc@folder/libs:boost"); + assert(u.path() == "/doc@folder/libs:boost"); + //] + boost::ignore_unused(u); + } + { + //[snippet_parsing_path_use_case_5 + url_view u("https://www.boost.org/doc/libs/"); + segments_view segs = u.segments(); + auto it = segs.begin(); + assert(*it++ == "doc"); + assert(*it++ == "libs"); + assert(*it == ""); + //] + boost::ignore_unused(it); + } + { + //[snippet_parsing_path_use_case_6 + url_view u("https://www.boost.org//doc///libs"); + segments_view segs = u.segments(); + auto it = segs.begin(); + assert(*it++ == ""); + assert(*it++ == "doc"); + assert(*it++ == ""); + assert(*it++ == ""); + assert(*it == "libs"); + //] + boost::ignore_unused(it); + } + { + //[snippet_parsing_path_use_case_7 + url_view u("https://www.boost.org//doc/libs/"); + segments_view segs = u.segments(); + auto it = segs.begin(); + assert(*it++ == ""); + assert(*it++ == "doc"); + assert(*it++ == "libs"); + assert(*it == ""); + //] + boost::ignore_unused(it); + } + { + //[snippet_parsing_path_use_case_8 + url_view u("https://doc/libs/"); + assert(u.host() == "doc"); + segments_view segs = u.segments(); + auto it = segs.begin(); + assert(*it++ == "libs"); + assert(*it == ""); + //] + boost::ignore_unused(it); } } @@ -693,31 +867,33 @@ void parsing_query() { { - //[snippet_parsing_query_1 - string_view s = "https://www.example.com/get-customer.php?id=409&name=Joe&individual"; - url_view u = parse_uri(s).value(); - std::cout << u << "\n" - "has query: " << u.has_query() << "\n" - "query: " << u.query() << "\n"; - std::cout << u.params().size() << " parameters\n"; - - for (auto p: u.params()) - { - if (p.has_value) - { - std::cout << - "parameter: <" << p.key << - ", " << p.value << ">\n"; - } else { - std::cout << "parameter: " << p.key << "\n"; - } - } + //[snippet_parsing_query_0 + url_view u("https://www.example.com/get-customer.php?id=409&name=Joe&individual"); + assert( u.query() == "id=409&name=Joe&individual" ); //] } + { + //[snippet_parsing_query_1 + url_view u("https://www.example.com/get-customer.php?id=409&name=Joe&individual"); + params_view ps = u.params(); + assert(ps.size() == 3); + //] + //[snippet_parsing_query_1a + auto it = ps.begin(); + assert((*it).key == "id"); + assert((*it).value == "409"); + ++it; + assert((*it).key == "name"); + assert((*it).value == "Joe"); + ++it; + assert((*it).key == "individual"); + assert(!(*it).has_value); + //] + boost::ignore_unused(it); + } { //[snippet_parsing_query_2 - string_view s = "https://www.example.com/get-customer.php?key-1=value-1&key-2=&key-3&&=value-2"; - url_view u = parse_uri(s).value(); + url_view u("https://www.example.com/get-customer.php?key-1=value-1&key-2=&key-3&&=value-2"); std::cout << u << "\n" "has query: " << u.has_query() << "\n" "encoded query: " << u.encoded_query() << "\n" @@ -738,8 +914,7 @@ parsing_query() } { //[snippet_parsing_query_3 - string_view s = "https://www.example.com/get-customer.php?email=joe@email.com&code=a:2@/!"; - url_view u = parse_uri(s).value(); + url_view u("https://www.example.com/get-customer.php?email=joe@email.com&code=a:2@/!"); std::cout << u << "\n" "has query: " << u.has_query() << "\n" "encoded query: " << u.encoded_query() << "\n" @@ -760,25 +935,20 @@ parsing_query() } { //[snippet_parsing_query_4 - string_view s = "https://www.example.com/get-customer.php?name=joe"; - url_view u = parse_uri(s).value(); + url_view u("https://www.example.com/get-customer.php?name=joe"); std::cout << u << "\n" "query: " << u.query() << "\n"; //] } { //[snippet_parsing_query_5 - string_view s = "https://www.example.com/get-customer.php"; - url_view u = parse_uri(s).value(); - std::cout << u << "\n" - "has query: " << u.has_query() << "\n" - "query: " << u.query() << "\n"; + assert(url_view("https://www.example.com/get-customer.php?").has_query()); + assert(!url_view("https://www.example.com/get-customer.php").has_query()); //] } { //[snippet_parsing_query_6 - string_view s = "https://www.example.com/get-customer.php?name=John%20Doe"; - url_view u = parse_uri(s).value(); + url_view u("https://www.example.com/get-customer.php?name=John%20Doe"); std::cout << u << "\n" "has query: " << u.has_query() << "\n" "encoded query: " << u.encoded_query() << "\n" @@ -787,12 +957,42 @@ parsing_query() } { //[snippet_parsing_query_7 - string_view s = "https://www.example.com/get-customer.php?name=John%26Doe"; - url_view u = parse_uri(s).value(); - std::cout << u << "\n" - "has query: " << u.has_query() << "\n" - "encoded query: " << u.encoded_query() << "\n" - "query: " << u.query() << "\n"; + url_view u("https://www.example.com/get-customer.php?name=John%26Doe"); + assert(u.query() == "name=John&Doe"); + //] + } + { + //[snippet_parsing_query_8 + url_view u("https://www.example.com/get-customer.php?key-1=value-1&key-2=&key-3&&=value-4"); + params_view ps = u.params(); + assert(ps.size() == 5); + //] + //[snippet_parsing_query_8a + auto it = ps.begin(); + assert((*it).key == "key-1"); + assert((*it).value == "value-1"); + //] + //[snippet_parsing_query_8b + ++it; + assert((*it).key == "key-2"); + assert((*it).value == ""); + assert((*it).has_value); + //] + //[snippet_parsing_query_8c + ++it; + assert((*it).key == "key-3"); + assert(!(*it).has_value); + //] + //[snippet_parsing_query_8d + ++it; + assert((*it).key == ""); + assert((*it).value == "value-4"); + //] + } + { + //[snippet_parsing_query_9 + url_view u("https://www.example.com/get-customer.php?email=joe@email.com&code=a:2@/!"); + assert(u.has_query()); //] } } @@ -802,8 +1002,7 @@ parsing_fragment() { { //[snippet_parsing_fragment_1 - string_view s = "https://www.example.com/index.html#section%202"; - url_view u = parse_uri(s).value(); + url_view u("https://www.example.com/index.html#section%202"); std::cout << u << "\n" "has fragment: " << u.has_fragment() << "\n" "fragment: " << u.fragment() << "\n" @@ -812,8 +1011,7 @@ parsing_fragment() } { //[snippet_parsing_fragment_2_a - string_view s = "https://www.example.com/index.html#"; - url_view u = parse_uri(s).value(); + url_view u("https://www.example.com/index.html#"); std::cout << u << "\n" "has fragment: " << u.has_fragment() << "\n" "fragment: " << u.fragment() << "\n"; @@ -821,8 +1019,7 @@ parsing_fragment() } { //[snippet_parsing_fragment_2_b - string_view s = "https://www.example.com/index.html"; - url_view u = parse_uri(s).value(); + url_view u("https://www.example.com/index.html"); std::cout << u << "\n" "has fragment: " << u.has_fragment() << "\n" "fragment: " << u.fragment() << "\n"; @@ -830,13 +1027,30 @@ parsing_fragment() } { //[snippet_parsing_fragment_3 - string_view s = "https://www.example.com/index.html#code%20:a@b?c/d"; - url_view u = parse_uri(s).value(); + url_view u("https://www.example.com/index.html#code%20:a@b?c/d"); std::cout << u << "\n" "has fragment: " << u.has_fragment() << "\n" "fragment: " << u.fragment() << "\n"; //] } + { + //[snippet_parsing_fragment_4 + url_view u("https://www.example.com/index.html#section2"); + assert(u.fragment() == "section2"); + //] + } + { + //[snippet_parsing_fragment_5 + assert(url_view("https://www.example.com/index.html#").has_fragment()); + assert(!url_view("https://www.example.com/index.html").has_fragment()); + //] + } + { + //[snippet_parsing_fragment_6 + url_view u("https://www.example.com/index.html#code%20:a@b?c/d"); + assert(u.fragment() == "code :a@b?c/d"); + //] + } } void @@ -844,34 +1058,29 @@ using_modifying() { { //[snippet_modifying_1 - string_view s = "https://www.example.com"; - url_view u = parse_uri(s).value(); + url_view u("https://www.example.com"); url v(u); //] //[snippet_modifying_2 - std::cout << v << "\n" - "scheme: " << v.scheme() << "\n" - "has authority: " << v.has_authority() << "\n" - "authority: " << v.encoded_authority() << "\n" - "path: " << v.encoded_path() << "\n"; + assert(v.scheme() == "https"); + assert(v.has_authority()); + assert(v.encoded_authority() == "www.example.com"); + assert(v.encoded_path() == ""); //] //[snippet_modifying_3 v.set_host("my website.com"); v.set_path("my file.txt"); v.set_query("id=42&name=John Doe"); - std::cout << v << "\n"; + assert(v.string() == "https://my%20website.com/my%20file.txt?id=42&name=John%20Doe"); //] //[snippet_modifying_4 v.set_scheme("http"); - std::cout << v << "\n"; - //] - - //[snippet_modifying_5 - v.set_host("www.my example.com"); - std::cout << v << "\n"; + assert(v.string() == "http://my%20website.com/my%20file.txt?id=42&name=John%20Doe"); + v.set_encoded_host("www.my%20example.com"); + assert(v.string() == "http://my%20example.com/my%20file.txt?id=42&name=John%20Doe"); //] @@ -1028,34 +1237,40 @@ modifying_path() { { //[snippet_modifying_path_1 - url_view u = parse_uri("https://www.boost.org").value(); + url_view u("https://www.boost.org"); + //] BOOST_TEST_NOT(u.is_path_absolute()); BOOST_TEST_EQ(u.encoded_segments().size(), 0u); } - { //[snippet_modifying_path_2 - url_view u = parse_uri("https://www.boost.org/").value(); + url_view u("https://www.boost.org/"); //] BOOST_TEST(u.is_path_absolute()); BOOST_TEST_EQ(u.encoded_segments().size(), 0u); } + { + //[snippet_modifying_path_1_2 + assert( !url_view("https://www.boost.org").is_path_absolute() ); + assert( url_view("https://www.boost.org/").is_path_absolute() ); + //] + } { //[snippet_modifying_path_3 - url u = parse_uri("https://www.boost.org/./a/../b").value(); + url u("https://www.boost.org/./a/../b"); u.normalize(); + assert(u.string() == "https://www.boost.org/b"); //] BOOST_TEST(u.is_path_absolute()); BOOST_TEST_EQ(u.string(), "https://www.boost.org/b"); BOOST_TEST_EQ(u.encoded_segments().size(), 1u); } - { //[snippet_modifying_path_4 // scheme and a relative path - url_view u = parse_uri("https:path/to/file.txt").value(); + url_view u("https:path/to/file.txt"); //] BOOST_TEST_EQ(u.scheme(), "https"); BOOST_TEST_NOT(u.has_authority()); @@ -1066,7 +1281,7 @@ modifying_path() { //[snippet_modifying_path_5 // scheme and an absolute path - url_view u = parse_uri("https:/path/to/file.txt").value(); + url_view u("https:/path/to/file.txt"); //] BOOST_TEST_EQ(u.scheme(), "https"); BOOST_TEST_NOT(u.has_authority()); @@ -1077,7 +1292,7 @@ modifying_path() { //[snippet_modifying_path_6 // "//path" will be considered the authority component - url_view u = parse_uri("https://path/to/file.txt").value(); + url_view u("https://path/to/file.txt"); //] BOOST_TEST_EQ(u.scheme(), "https"); BOOST_TEST(u.has_authority()); @@ -1085,6 +1300,29 @@ modifying_path() BOOST_TEST_EQ(u.encoded_segments().size(), 2u); } + { + //[snippet_modifying_path_4_5_6 + // scheme and a relative path + url_view u1("https:path/to/file.txt"); + assert(!u1.has_authority()); + assert(!u1.is_path_absolute()); + assert(u1.path() == "path/to/file.txt"); + + // scheme and an absolute path + url_view u2("https:/path/to/file.txt"); + assert(!u2.has_authority()); + assert(u2.is_path_absolute()); + assert(u2.path() == "/path/to/file.txt"); + + // "//path" will be considered the authority component + url_view u3("https://path/to/file.txt"); + assert(u3.has_authority()); + assert(u3.authority().string() == "path"); + assert(u3.is_path_absolute()); + assert(u3.path() == "/to/file.txt"); + //] + } + { //[snippet_modifying_path_7 // only a relative path @@ -1108,6 +1346,21 @@ modifying_path() BOOST_TEST_EQ(u.encoded_segments().size(), 2u); } + { + //[snippet_modifying_path_7_8 + // only a relative path + url_view u1 = parse_uri_reference("path-to/file.txt").value(); + assert(!u1.has_scheme()); + assert(u1.path() == "path-to/file.txt"); + + // "path:" will be considered the scheme component + // instead of a substring of the first segment + url_view u2 = parse_uri_reference("path:to/file.txt").value(); + assert(u2.has_scheme()); + assert(u2.path() == "to/file.txt"); + //] + } + { //[snippet_modifying_path_9 // "path" should not become the authority component @@ -1131,13 +1384,20 @@ modifying_path() BOOST_TEST_NOT(u.is_path_absolute()); BOOST_TEST_EQ(u.encoded_segments().size(), 2u); } - + { + //[snippet_modifying_path_9_10 + url u1("https:path/to/file.txt" ); + u1.set_encoded_path("//path/to/file.txt"); + assert(u1.string() == "https:/.//path/to/file.txt"); + //] + } { //[snippet_modifying_path_11 // should not insert as "pathto/file.txt" url u = parse_uri_reference("to/file.txt").value(); segments segs = u.segments(); segs.insert(segs.begin(), "path"); + assert(u.string() == "path/to/file.txt"); //] BOOST_TEST_NOT(u.has_scheme()); BOOST_TEST_NOT(u.has_authority()); @@ -1161,7 +1421,8 @@ public: { ignore_unused(&using_url_views); ignore_unused(&using_urls); - ignore_unused(&parsing_urls); + parsing_urls(); + parsing_components(); ignore_unused(&parsing_scheme); ignore_unused(&parsing_authority); ignore_unused(&parsing_path);