Note
Notes highlighted in yellow are comments on the working draft and are not intended for the actual TS.
The scope of this Technical Specification will include a single std::experimental::uri type, specifications about how the are intended to be processed and extended, including some additional helper types and functions. It will include a std::experimental::uri_builder type to build a URI from its components. Finally, it will include types and functions for percent encoding, URI references, reference resolution and URI normalization and comparison.
The generic syntax of a URI is defined in IETF RFC 3986. section 3.
All URIs are of the form:
scheme ":" "hierarchical part" [ "?" query ] [ "#" fragment ]
The scheme is used to identify the specification needed to parse the rest of the URI. A generic syntax parser can parse any URI into its main parts. The scheme can then be used to identify whether further scheme-specific parsing can be performed.
The hierarchical part refers to the part of the URI that holds identification information that is hierarchical in nature. This may contain an authority (always prefixed with a double slash (“//”)) and/or a path. The path part is required, thought it may be empty. The authority part holds an optional user info part, ending with an at sign (“@”); a host identifier and an optional port number, preceded by a colon (”:”). The host may be an IP address or domain name. The normative reference for IPv6 addresses is IETF RFC 3986.
The query is an optional part following a question mark (”?”) that contains information that is not hierarchical.
Finally, the fragment is an optional part, prefixed by a hash symbol (“#”) that is used to identify secondary sources.
IETF RFC 3987 specifies a new protocol element, the Internationalized Resource Identifier (IRI). The IRI complements a URI, and extends it to allow unicode characters. The syntax of an IRI is specified in IETF RFC 3987, section 2.
IETF RFC 6874 specifies scoped IDs in IPv6 addresses. The syntax is specified in IETF RFC 6874, section 2.
The rules for URI normalization are specified in IETF RFC 3986, section 6 and IETF RFC 3987, section 5.
The rule for transforming references is given in IETF RFC 3986, section 5.2.2.
The rule for removing dot segments is given in IETF RFC 3986, section 5.2.4.
The rule for recomposing a URI from its parts is given in IETF RFC 3986, section 5.3.
A Uniform Resource Identifier is a sequence of characters from a limited set with a specific syntax used to identify a name or resource. URIs can be classified as URLs or URNs. The URI syntax is defined in IETF RFC 3986.
A Uniform Resource Locator (URL) is a type of URI, complementary to a URN used to locate a resource over a network.
A Uniform Resource Name (URN) is a type of URI, complementary to a URL used to unambiguously identify resources.
An Internationalized Resource Identifier (IRI) is a complement to the URI that allows characters from the Universal Character Set (Unicode/ISO 10646). The IRI syntax is defined in IETF RFC 3987.
A generic URI is decomposed into four principal parts: the scheme, the hierarchical part, an optional query and optional fragment. The hierarchical part can be further decomposed into four parts: the user info, host, port and path.
A scheme name is the top level of the URI naming structure. It indicates the specifications, syntax and semantics of the rest of the URI structure. It is always followed by a colon (”:”).
A query is a part, indicated by a question mark (”?”) and terminated by a hash (“#”), that contains non-hierarchical information. It is commonly structured as a sequence of key-value parameter values separated by equals (“=”), which are separated by a semi-colon (”;”) or ampersand (“&”).
A fragment is indicated by a hash (“#”) and allows indirect identification of a secondary resource. For example, a fragment may refer to a section header in an HTML document with an id attribute of the same name.
The hierarchical part of a URI contains hierarchical information. If it starts with a double forward slash (“//”), it is followed by an authority and a path. The authority can be further broken down into a user-information part, a hostname and a port. The authority is followed by an optional path. If the hierarchical part does not begin with a double forward slash (“//”), then it must contain a path.
The hierarchical part contains an authority. The authority contains an optional user info followed by at (“@”), a host and an optional port, preceded by a colon (”:”).
The user info is an optional part of the URI authority, terminated by at (“@”) and is followed by a host. It is used in the telnet scheme:
telnet://<user>:<password>@<host>:<port>/
The hostname contains a domain name or IP address.
A domain name is human-readable string used to identify a host. Domain names are registered in the Domain Name System (DNS).
The IP address can either be an IPv4 (e.g. 127.0.0.1) or an IPv6 address (e.g. ::1. In a URI, an IPv6 address is enclosed in square braces (“[]”).
The optional port is always preceded by a colon (”:”). If the port is not present, even if a colon is present, then the port is considered to have the value of the default port of the scheme.
The path is a part of the hierarchical data and is a sequence of segments, each separated by a forward slash (“/”). It is terminated by a question mark (”?”), followed by a query, a hash (“#”) followed by a fragment or by the end of the URI.
Dot segments are elements in a path containing either a dot (”.”) or a double dot (”..”), separated by a forward slash (“/”). Dot segments can be removed from a path as part of its normalization without changing the URI semantics.
An absolute URI always specifies the scheme. URIs that don’t provide the scheme are called relative references.
An opaque URI is an absolute URI that does not provide a double slash (“//”) after the scheme-delimiting colon (”:”). Opaque URIs have no authority and the part immediately following the colon (”:”) is the path. Some examples of opaque URIs are:
mailto:[email protected]
news:comp.lang.c++
URIs that provide a double slash (“//”) following the scheme-delimiting colon (”:”) are known as hierarchical URIs. Some examples are:
http://www.example.com/
ftp://[email protected]/
URI normalization is the process by which a URI is transformed in order to determine of two URIs are equivalent. There are different levels to comparison, which trade-off the number of false negatives and complexity. The normalization and comparison procedures are defined in IETF RFC 3986, section 6.
The comparison ladder describes how URIs can be compared using normalization in different ways, trading off the complexity of the method and the number of false negatives. The comparison ladder is defined in IETF RFC 3986, section 6.2 and IETF RFC 3987, section 5.3.
Relative references are URIs that do not provide a scheme. Relative references are only usable when a base URI is known, against which the relative reference can be resolved. The relative reference is defined in IETF RFC 3986, section 4.2 and IETF RFC 3987, section 6.5.
Relative references can be resolved against a base URI, producing an absolute URI. Only the scheme is required to be present in the base URI. Reference resolution is defined in IETF RFC 3986, section 5.
Pre-parsing and normalization of the URI is performed before transforming the reference.
The transform reference for resolving URIs is given in IETF RFC 3986, section 5.2.2.
Percent encoding is the mechanism used to encode reserved characters in a URI. See IETF RFC 3986, section 2.1.
All characters in a URI scheme and host must be lower-case. All hexidecimal digits within a percent-encoded triplet must be upper-case. See IETF RFC 3986, section 6.2.2.1 and IETF RFC 3987, section 5.3.2.1.
URIs should be normalized by decoding any percent-encoded octet that corresponds to a an unreserved character. See IETF RFC 3986, section 6.2.2.2 and IETF RFC 3987, section 5.3.2.3.
Path segments [uri.definition.dot-segments] should be removed from URIs that are not relative references. See IETF RFC 3986, section 6.2.2.3 and IETF RFC 3987, section 5.3.2.4.
In Unicode, different sequences of characters could be defined as equivalent depending on how they are encoded. See IETF RFC 3987, section 5.3.2.2.
A zone index is used to identify to which scope a non-global address belongs in an IPv6 address. It is specified in IETF RFC 6874.
Template parameters named InputIterator shall meet the C++ Standard’s library input iterator requirements ([input.iterators]) and shall have a value type that is one of the encoded character types.
The uri class must be able to parse according to the rules described in IETF RFC 3986, Section 3.
The uri class must be able to correctly parse IPv6 addresses, described in IETF RFC 3986.
The uri class must be able to parse internationalized uri class according to IETF RFC 3987, section 2.
The uri class must be able to parse zone IDs in IPv6 addresses according to IETF RFC 6874, section 2.
#include <string> // std::basic_string
#include <system_error> // std::error_code
#include <iosfwd> // std::basic_istream, std::basic_ostream
#include <iterator> // std::iterator_traits
#include <memory> // std::allocator
#include <optional> // std::optional
#include <string_view> // std::basic_string_view
#include <functional> // std::hash
#include <filesystem> // std::filesystem::path
namespace std {
namespace experimental {
// class declarations
class uri;
class uri_builder;
class uri_error;
enum class uri_error_code {
// uri syntax errors
invalid_syntax = 1,
// uri reference and resolution errors
base_uri_is_empty,
base_uri_is_not_absolute,
base_uri_is_opaque,
base_uri_does_not_match,
// builder errors
invalid_uri,
invalid_scheme,
invalid_user_info,
invalid_host,
invalid_port,
invalid_path,
invalid_query,
invalid_fragment,
// decoding errors
not_enough_input,
non_hex_input,
conversion_failed,
};
enum class uri_normalization_level {
string_comparison,
syntax_based,
};
// factory functions
template <class Source>
uri make_uri(const Source& source, std::error_code& ec);
template <class InputIterator>
uri make_uri(InputIterator first, InputIterator last, std::error_code& ec);
template <class Source, class Allocator>
uri make_uri(const Source& source, const Allocator& a, std::error_code& ec);
template <class InputIterator, class Allocator>
uri make_uri(InputIterator first, InputIterator last, const Allocator& a, std::error_code& ec);
// interoperability with std::filesystem::path
uri to_uri(const std::filesystem::path& p);
std::filesystem::path to_filesystem_path(const uri& u);
std::filesystem::path to_filesystem_path(const uri& u, std::error_code& ec);
// equality and comparison operators
bool operator== (const uri& lhs, const uri& rhs) noexcept;
bool operator!= (const uri& lhs, const uri& rhs) noexcept;
bool operator< (const uri& lhs, const uri& rhs) noexcept;
bool operator> (const uri& lhs, const uri& rhs) noexcept;
bool operator<= (const uri& lhs, const uri& rhs) noexcept;
bool operator>= (const uri& lhs, const uri& rhs) noexcept;
// stream operators
template <typename CharT, class CharTraits = std::char_traits<CharT>>
std::basic_ostream<CharT, CharTraits>&
operator<< (std::basic_ostream<CharT, CharTraits>& os, const uri& u);
template <typename CharT, class CharTraits = std::char_traits<CharT>>
std::basic_istream<CharT, CharTraits>&
operator>> (std::basic_istream<CharT, CharTraits>& is, uri& u);
// swap functions
void swap(uri& lhs, uri& rhs) noexcept;
} // namespace experimental
template <>
struct hash<experimental::uri> {
size_t operator () (const experimental::uri &u) const;
};
template <>
struct is_error_code_enum<experimental::uri_error> : public true_type { };
} // namespace std
The <experimental/uri> header contains a declaration for a uri class, a uri_builder class and an exception class, uri_error in the std::experimental namespace.
// factory functions
template <class Source>
uri make_uri(const Source& source, std::error_code& ec);
template <class InputIterator>
uri make_uri(InputIterator first, InputIterator last, std::error_code& ec);
template <class Source, class Allocator>
uri make_uri(const Source& source, const Allocator& a, std::error_code& ec);
template <class InputIterator, class Allocator>
uri make_uri(InputIterator first, InputIterator last, const Allocator& a,
std::error_code& ec);
uri to_uri(const std::filesystem::path& p);
std::filesystem::path p("/usr/bin/ls");
std::experimental::uri u(std::experimental::to_uri(p));
assert("file:///usr/bin/ls" == u.string());
filesystem::path to_filesystem_path(const uri& u);
try {
std::experimental::uri u("file:///usr/bin/ls");
std::filesystem::path p(std::experimental::to_filesystem_path(u));
assert("/usr/bin/ls" == p.string());
}
catch (std::experimental::uri_error& e) {
// handle error
}
filesystem::path to_filesystem_path(const uri& u, std::error_code& ec);
std::experimental::uri u("file:///usr/bin/ls");
std::error_code ec;
std::filesystem::path p(std::experimental::to_filesystem_path(u, ec));
if (!ec) {
assert("/usr/bin/ls" == p.string());
}
else {
// handle error
}
bool operator== (const uri& lhs, const uri& rhs) noexcept;
bool operator!= (const uri& lhs, const uri& rhs) noexcept;
lhs.compare(rhs, uri_normalization_level::syntax_based) == 0 and !(lhs == rhs).
bool operator< (const uri& lhs, const uri& rhs) noexcept;
bool operator> (const uri& lhs, const uri& rhs) noexcept;
bool operator<= (const uri& lhs, const uri& rhs) noexcept;
bool operator>= (const uri& lhs, const uri& rhs) noexcept;
lhs.compare(rhs, uri_normalization_level::syntax_based) < 0, (rhs < lhs), !(rhs < lhs) and !(lhs < rhs).
template <typename CharT, class CharTraits = std::char_traits<CharT>>
std::basic_ostream<CharT, CharTraits>&
operator<< (std::basic_ostream<CharT, CharTraits>& os, const uri& u);
template <typename CharT, class CharTraits = std::char_traits<CharT>>
std::basic_istream<CharT, CharTraits>&
operator>> (std::basic_istream<CharT, CharTraits>& is, uri& u);
void swap(uri& lhs, uri& rhs) noexcept;
template <>
struct hash<experimental::uri> {
size_t operator () (const experimental::uri &u) const;
};
template <>
struct is_error_code_enum<experimental::uri_error_code> : public true_type { };
Some URI functions provide two overloads, one that throws an exception to report errors, and a second that sets an std::error_code.
Member functions of uri not having an error of type std::error_code& report errors as follow, unless otherwise specified:
Functions that have an error of type std::error_code& report errors as follows:
namespace std {
namespace experimental {
class uri {
public:
// typedefs
typedef *unspecified* string_type;
typedef *unspecified* iterator;
typedef *unspecified* const_iterator;
typedef std::iterator_traits<iterator>::value_type value_type;
typedef basic_string_view<value_type> string_view;
// constructors and destructor
uri();
template <class Source, class Allocator = std::allocator<value_type>>
explicit uri(const Source& source, const Allocator& a = Allocator());
template <typename InputIterator, class Allocator = std::allocator<value_type>>
uri(InputIterator begin, InputIterator end, const Allocator& a = Allocator());
uri(const uri& other);
uri(uri&& other) noexcept;
~uri() noexcept;
// assignment
uri& operator= (const uri& other);
uri& operator= (uri&& other) noexcept;
// modifiers
void swap(uri& other) noexcept;
// iterators
const_iterator begin() const;
const_iterator end() const;
const_iterator cbegin() const;
const_iterator cend() const;
// accessors
std::optional<string_view> scheme() const;
std::optional<string_view> user_info() const;
std::optional<string_view> host() const;
std::optional<string_view> port() const;
template <typename IntT>
std::optional<IntT> port(typename std::is_integral<IntT>::type* = 0) const;
std::optional<string_view> path() const;
std::optional<string_view> authority() const;
std::optional<string_view> query() const;
std::optional<string_view> fragment() const;
// string accessors
template <typename CharT,
class CharTraits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>>
std::basic_string<CharT, CharTraits, Allocator>
to_string(const Allocator& a = Allocator()) const;
std::string string() const;
std::wstring wstring() const;
std::string u8string() const;
std::u16string u16string() const;
std::u32string u32string() const;
// query
bool empty() const noexcept;
bool is_absolute() const;
bool is_opaque() const;
// transformers
uri normalize(uri_normalization_level level) const;
template <class Allocator>
uri normalize(uri_normalization_level level, const Allocator& alloc) const;
uri normalize(uri_normalization_level level, std::error_code& ec) const;
template <class Allocator>
uri normalize(uri_normalization_level level, const Allocator& alloc,
std::error_code& ec) const;
uri make_relative(const uri& u) const;
template <class Allocator>
uri make_relative(const uri& u, const Allocator& a) const;
uri make_relative(const uri& u, std::error_code& ec) const;
template <class Allocator>
uri make_relative(const uri& u, const Allocator& a, std::error_code& ec) const;
uri resolve(const uri& u) const;
template <class Allocator>
uri resolve(const uri& u, const Allocator& a) const;
uri resolve(const uri& u, std::error_code& ec) const;
template <class Allocator>
uri resolve(const uri& u, const Allocator& a,std::error_code& ec) const;
// comparison
int compare(const uri& other, uri_normalization_level level) const noexcept;
// percent encoding and decoding
template <typename InputIterator, typename OutputIterator>
static OutputIterator encode_user_info(InputIterator begin, InputIterator end,
OutputIterator out);
template <typename InputIterator, typename OutputIterator>
static OutputIterator encode_host(InputIterator begin, InputIterator end,
OutputIterator out);
template <typename InputIterator, typename OutputIterator>
static OutputIterator encode_port(InputIterator begin, InputIterator end,
OutputIterator out);
template <typename InputIterator, typename OutputIterator>
static OutputIterator encode_path(InputIterator begin, InputIterator end,
OutputIterator out);
template <typename InputIterator, typename OutputIterator>
static OutputIterator encode_query(InputIterator begin, InputIterator end,
OutputIterator out);
template <typename InputIterator, typename OutputIterator>
static OutputIterator encode_fragment(InputIterator begin, InputIterator end,
OutputIterator out);
template <typename InputIterator, typename OutputIterator>
static OutputIterator decode(InputIterator begin, InputIterator end,
OutputIterator out);
};
} // namespace experimental
} // namespace std
string_type is unspecified and is not required to be a contiguous memory block. As a consequence, iterator and const_iterator are also unspecified. Should an implementor decide to use a contiguous string (e.g. std::string), iterator and const_iterator can be string_type::const_iterator. Each URI part is required to be a contiguous memory block.
Function template parameters named Source shall be one of:
Arguments of type Source shall not be null pointers.
Note
This is similar wording to the filesystem path requirements in N3963 (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3693.html#path-Requirements).
A std::experimental::uri object is always normalized upon construction.
Case normalization must be performed according to IETF RFC 3986, section 6.2.2.1 and IETF RFC 3987, section 5.3.2.1.
Percent encoding normalization must be performed according to IETF RFC 3986, section 6.2.2.2 and IETF RFC 3987, section 5.3.2.3.
Removing dot segments (”.”, ”..”) from a path must conform to IETF RFC 3986, section 5.2.4.
URI References returned by std::experimental::uri::make_relative must be transformed by using the algorithm in IETF RFC 3986, section 5.2.2.
typedef *unspecified* string_type;
typedef *unspecified* iterator;
typedef *unspecified* const_iterator;
typedef std::iterator_traits<iterator>::value_type value_type;
typedef basic_string_view<value_type> string_view;
The string_type, iterator and const_iterator types are left unspecified.
uri();
uri(const uri& other);
uri(uri&& other) noexcept;
template <class Source, class Allocator = std::allocator<value_type>>
uri(const Source& source, const Allocator& a = Allocator());
template <typename InputIterator, class Allocator = std::allocator<value_type>>
uri(InputIterator begin, InputIterator end, const Allocator& a = Allocator());
uri& operator= (const uri& other);
uri& operator= (uri&& other) noexcept;
void swap(uri& other) noexcept;
const_iterator begin() const;
const_iterator end() const;
const_iterator cbegin() const;
const_iterator cend() const;
std::optional<string_view> scheme() const;
std::optional<string_view> user_info() const;
std::optional<string_view> host() const;
std::optional<string_view> port() const;
template <typename IntT>
std::optional<IntT> port(typename std::is_integral<IntT>::type* = 0) const;
std::optional<string_view> path() const;
std::optional<string_view> authority() const;
std::optional<string_view> query() const;
std::optional<string_view> fragment() const;
template <typename CharT,
class CharTraits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>>
std::basic_string<CharT, CharTraits, Allocator>
to_string(const Allocator& a = Allocator()) const;
std::string string() const;
std::wstring wstring() const;
std::string u8string() const;
std::u16string u16string() const;
std::u32string u32string() const;
bool empty() const noexcept;
bool is_absolute() const;
bool is_opaque() const;
This proposal specifies three transformer functions:, normalize, make_relative and resolve.
uri normalize(uri_normalization_level level) const;
template <class Allocator>
uri normalize(uri_normalization_level level, const Allocator& alloc) const;
uri normalize(uri_normalization_level level, std::error_code& ec) const;
template <class Allocator>
uri normalize(uri_normalization_level level, const Allocator& alloc,
std::error_code& ec) const;
uri make_relative(const uri& u) const;
try {
std::experimental::uri u("http://www.example.org/path/");
std::experimental::uri v("http://www.example.org/path/to/file.html");
std::experimental::uri w = u.make_relative(v);
assert(w.string() == "to/file.html");
}
catch (std::experimental::uri_error& e) {
// handle error
}
template <class Allocator>
uri make_relative(const uri& u, const Allocator& a) const;
std::allocator<std::experimental::uri::value_type> a;
try {
std::experimental::uri u("http://www.example.org/path/", a);
std::experimental::uri v("http://www.example.org/path/to/file.html", a);
std::experimental::uri w = v.make_relative(u, a);
assert(w.string() == "to/file.html");
}
catch (std::experimental::uri_error& e) {
// handle error
}
uri make_relative(const uri& base, std::error_code& ec) const;
std::experimental::uri u("http://www.example.org/path/");
std::experimental::uri v("http://www.example.org/path/to/file.html");
std::error_code ec;
std::experimental::uri w = v.make_relative(u, ec);
if (!ec) {
assert(w.string() == "to/file.html");
}
else {
// handle error
}
template <class Allocator>
uri make_relative(const uri& base, const Allocator &a,
std::error_code& ec) const;
std::allocator<std::experimental::uri::value_type> a;
std::experimental::uri u("http://www.example.org/path/", a);
std::experimental::uri v("http://www.example.org/path/to/file.html", a);
std::error_code ec;
std::experimental::uri w = v.make_relative(u, a, ec);
if (!ec) {
assert(w.string() == "to/file.html");
}
else {
// handle error ec
}
uri resolve(const uri& u) const;
template <class Allocator>
uri resolve(const uri& u, const Allocator& a) const;
uri resolve(const uri& u, std::error_code& ec) const;
template <class Allocator>
uri resolve(const uri& u, const Allocator& a, std::error_code& ec) const;
int compare(const uri& other, uri_normalization_level level) const;
template <typename InputIterator, typename OutputIterator>
static OutputIterator encode_user_info(InputIterator begin, InputIterator end,
OutputIterator out);
template <typename InputIterator, typename OutputIterator>
static OutputIterator encode_host(InputIterator begin, InputIterator end,
OutputIterator out);
template <typename InputIterator, typename OutputIterator>
static OutputIterator encode_port(InputIterator begin, InputIterator end,
OutputIterator out);
template <typename InputIterator, typename OutputIterator>
static OutputIterator encode_path(InputIterator begin, InputIterator end,
OutputIterator out);
template <typename InputIterator, typename OutputIterator>
static OutputIterator encode_query(InputIterator begin, InputIterator end,
OutputIterator out);
template <typename InputIterator, typename OutputIterator>
static OutputIterator encode_fragment(InputIterator begin, InputIterator end,
OutputIterator out);
template <typename InputIterator, typename OutputIterator>
static OutputIterator decode(InputIterator begin, InputIterator end,
OutputIterator out);
namespace std {
namespace experimental {
class uri_builder {
public:
// constructors and destructor
uri_builder();
explicit uri_builder(const uri& base);
template <class Source>
explicit uri_builder(const Source& base);
template <class InputIterator>
uri_builder(InputIterator begin, InputIterator end);
uri_builder(const uri_builder& other);
uri_builder(uri_builder&& other) noexcept;
~uri_builder() noexcept;
// assignment
uri_builder& operator = uri_builder(const uri_builder&);
uri_builder& operator = uri_builder(uri_builder&&) noexcept;
// modifiers
void swap(uri_builder& other) noexcept;
// setters
template <class Source>
uri_builder& scheme(const Source& scheme);
template <class InputIterator>
uri_builder& scheme(InputIterator begin, InputIterator end);
template <class Source>
uri_builder& user_info(const Source& user_info);
template <class InputIterator>
uri_builder& user_info(InputIterator begin, InputIterator end);
template <class Source>
uri_builder& host(const Source& host);
template <class InputIterator>
uri_builder& host(InputIterator begin, InputIterator end);
template <class Source>
uri_builder& port(const Source& port);
template <class InputIterator>
uri_builder& port(InputIterator begin, InputIterator end);
template <class Source>
uri_builder& authority(const Source& authority);
template <class InputIterator>
uri_builder& authority(InputIterator begin, InputIterator end);
template <class UserInfoSource, class HostSource, PortSource>
uri_builder& authority(const UserInfoSource& user_info,
const HostSource& host, const PortSource& port);
template <class Source>
uri_builder& path(const Source& path);
template <class InputIterator>
uri_builder& path(InputIterator begin, InputIterator end);
template <class Source>
uri_builder& append_path(const Source& path);
template <class InputIterator>
uri_builder& append_path(InputIterator begin, InputIterator end);
template <class Source>
uri_builder& query(const Source& query);
template <class InputIterator>
uri_builder& query(InputIterator begin, InputIterator end);
template <class KeySource, class ParamSource>
uri_builder& append_query(const KeySource& key, const ParamSource& param);
template <class Source>
uri_builder& fragment(const Source& fragment);
template <class InputIterator>
uri_builder& fragment(InputIterator begin, InputIterator end);
// builder
std::experimental::uri uri() const;
};
} // namespace experimental
} // namespace std
Function template parameters named Source shall be one of:
Arguments of type Source shall not be null pointers.
The URI must be built according to component recomposition rules in IETF RFC 3986, section 5.3.
uri_builder();
uri_builder(const uri& base);
template <class Source>
uri_builder(const Source& base);
template <class InputIterator>
uri_builder(InputIterator begin, InputIterator end);
uri_builder(const uri_builder& other);
uri_builder(uri_builder&& other) noexcept;
uri_builder& operator= (const uri_builder& other);
uri_builder& operator= (uri_builder&& other) noexcept;
void swap(uri_builder& other) noexcept;
template <class Source>
uri_builder& scheme(const Source& scheme);
template <class InputIterator>
uri_builder& scheme(InputIterator begin, InputIterator end);
template <class Source>
uri_builder& user_info(const Source& user_info);
template <class InputIterator>
uri_builder& user_info(InputIterator begin, InputIterator end);
template <class Source>
uri_builder& host(const Source& host);
template <class InputIterator>
uri_builder& host(InputIterator begin, InputIterator end);
template <class Source>
uri_builder& port(const Source& port);
template <class InputIterator>
uri_builder& port(InputIterator begin, InputIterator end);
template <class Source>
uri_builder& authority(const Source& authority);
template <class InputIterator>
uri_builder& authority(InputIterator begin, InputIterator end);
template <class UserInfoSource, class HostSource, class PortSource>
uri_builder& authority(const UserInfoSource& user_info,
const HostSource& host, const PortSource& port);
template <class Source>
uri_builder& path(const Source& path);
template <class InputIterator>
uri_builder& path(InputIterator begin, InputIterator end);
template <class Source>
uri_builder& append_path(const Source& path);
template <class InputIterator>
uri_builder& append_path(InputIterator begin, InputIterator end);
template <class Source>
uri_builder& query(const Source& query);
template <class InputIterator>
uri_builder& query(InputIterator begin, InputIterator end);
template <class KeySource, class ParamSource>
uri_builder& append_query(const KeySource& key, const ParamSource& param);
template <class Source>
uri_builder& fragment(const Source& fragment);
template <class InputIterator>
uri_builder& fragment(InputIterator begin, InputIterator end);
std::experimental::uri uri() const;
namespace std {
namespace experimental {
class uri_error : public std::system_error {
public:
uri_error(const string& what_arg, error_code ec);
virtual ~uri_error() noexcept;
virtual const char *what() const noexcept;
};
} // namespace experimental
} // namespace std
uri_error(const string& what_arg, error_code ec);
const char *what() const noexcept;
enum class uri_error_code {
// uri syntax errors
invalid_syntax = 1,
// uri relative reference and resolution errors
base_uri_is_empty,
base_uri_is_not_absolute,
base_uri_is_opaque,
base_uri_does_not_match,
// builder errors
invalid_uri,
invalid_scheme,
invalid_user_info,
invalid_host,
invalid_port,
invalid_path,
invalid_query,
invalid_fragment,
// decoding errors
not_enough_input,
non_hex_input,
conversion_failed,
};
This error is set when the parser is unable to parse the given URI string.
This error is set when the base URI passed to make_relative or resolve is empty.
This error is set when the base URI passed to make_relative or resolve is not absolute (it is itself a relative reference).
This error is set when the base URI passed to make_relative or resolve is opaque.
This error is set when the base URI passed to make_relative does not match the prefix of the URI.
This error is set by the uri_builder when the builder is unable to construct a valid URI.
This error is set by the uri_builder if the scheme provided is invalid.
This error is set by the uri_builder if the user info provided is invalid.
This error is set by the uri_builder if the host provided is invalid.
This error is set by the uri_builder if the port provided is invalid.
This error is set by the uri_builder if the path provided is invalid.
This error is set by the uri_builder if the query provided is invalid.
This error is set by the uri_builder if the fragment provided is invalid.
This error is set when not enough input was given to the decoder to be able to decode the percent encoded string, e.g. %2.
This error is set when non-hex input is given to the decoder, e.g. %GG.
This error is set when the decoder was unable to convert the percent encoded string, e.g. %80.
Note
Issues
1. Normalization Invariant
At the Chicago meeting, it was strongly suggested that URIs are always normalized. However, this does not play well when working with filesystem URIs, especially if they refer to symbolic links and therefore it makes sense in these circumstances to keep the URI unnormalized. Therefore, the changes made after the Chicago meeting have been reverted, and URIs can be left unnormalized. A normalize member function exists to provide the conversion explicitly.
2. Scheme-Specific Normalization
There needs to be an extension point in order to allow scheme- and protocol- specific normalization.
3. empty() vs. is_absolute() vs. is_opaque()
In the minutes to the Chicago meeting, there was a suggestion that the is_ prefix is being applied inconsistently. The current way is consistent with at least the filesystem proposal, but clarification should be made with the LEWG.
4. Factory Functions
The make_uri factory functions are free functions, but the LEWG needs to clarify if they can remain this way or if they should be static members of uri.
5. Source Template Parameters
The Source template parameters seem overly generic, this will be taken up with the LEWG.
Note
C++ Network Library users and mailing list
Kyle Kloepper and Niklas Gustafsson for providing valuable feedback and encouragement, and for presenting different versions of this proposal at committee meetings.
Beman Dawes and his Filesystem proposal from which I was influenced strongly in the class design.
Thiago Macieira of Qt for important feedback on the draft proposal.
David Thaler for suggesting corrections to errors in referencing IETF standards.
Wikipedia, for being there.