header_utils
Loading...
Searching...
No Matches
uri.h
1
4
5#pragma once
6
7#include <vector>
8#include <stdexcept>
9#include <system_error>
10
11#include "expected.h"
12
13#include "enum_flags.h"
14#include "string_ops.h"
15#include "parsing.h"
16
17namespace ghassanpl
18{
22
23 // https://github.com/austinsc/Poco/blob/master/Foundation/include/Poco/URI.h
24 // https://docs.pocoproject.org/current/Poco.URI.html
25 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3420.html
26
28 using uri = std::string;
29 using uri_view = std::string_view;
30
31 enum class uri_error_code
32 {
33 no_error,
34
35 unknown_uri_scheme,
36
37 scheme_malformed,
38 scheme_invalid,
39 scheme_empty,
40
41 authority_malformed,
42 authority_empty,
43 authority_not_allowed_in_scheme,
44 authority_invalid_for_scheme,
45 authority_required_in_scheme,
46
47 user_info_malformed,
48 user_info_not_allowed_in_scheme,
49 user_info_invalid_for_scheme,
50 user_info_required_in_scheme,
51
52 host_malformed,
53 host_not_allowed_in_scheme,
54 host_invalid_for_scheme,
55 host_required_in_scheme,
56
57 port_malformed,
58 port_not_allowed_in_scheme,
59 port_invalid_for_scheme,
60 port_required_in_scheme,
61
62 path_malformed,
63 path_element_malformed,
64
65 query_malformed,
66 query_not_allowed_in_scheme,
67 query_invalid_for_scheme,
68 query_required_in_scheme,
69
70 fragment_malformed,
71 fragment_not_allowed_in_scheme,
72 fragment_invalid_for_scheme,
73 fragment_required_in_scheme,
74
75 invalid_percent_encoding,
76
77 no_scheme_specific_elements,
78 scheme_specific_element_malformed,
79 };
80
83 {
84 decomposed_uri() noexcept = default;
85 decomposed_uri(decomposed_uri const&) noexcept = default;
86 decomposed_uri(decomposed_uri&&) noexcept = default;
87 decomposed_uri& operator=(decomposed_uri const&) noexcept = default;
88 decomposed_uri& operator=(decomposed_uri&&) noexcept = default;
89
90 std::string scheme{};
91 std::string authority{};
92 std::string user_info{};
93 std::string host{};
94 std::string port{};
95 std::string path{};
96 std::vector<std::string> path_elements;
98 std::vector<std::string> normalized_path() const noexcept;
99 std::string query{};
100 std::vector<std::pair<std::string, std::string>> query_elements;
101 std::string fragment{};
102
103 bool canonical_form = false;
104
105 bool empty() const noexcept { return scheme.empty(); }
106
107 bool operator==(decomposed_uri const& other) const noexcept;
108 };
109
110 template <typename T>
111 using uri_expected = expected<T, uri_error_code>;
112 using uri_error = expected<void, uri_error_code>;
113
115 uri_expected<uri> make_uri_safe_for_display(uri_view uri);
116
119 {
120 split_query_elements,
121 split_path_elements,
123 //convert_plus_to_space,
124 lowercase_when_appropriate,
125 normalize_path,
126 };
127
132
133 enum class uri_compose_flags
134 {
135 path_leading_slash,
136 path_trailing_slash,
137 lowercase_when_appropriate,
138 normalize_path,
139 use_known_scheme,
140 //convert_space_to_plus
141 };
142
144
145 uri_expected<uri> normalize_uri(uri_view uri);
146
147 struct known_uri_scheme
148 {
149 virtual ~known_uri_scheme() noexcept = default;
150
151 virtual uri_error validate(uri_view uri) const noexcept
152 {
154 if (!decomposed) return decomposed.transform([](auto) {});
155
156 if (decomposed.value().scheme != scheme()) return unexpected(uri_error_code::scheme_invalid);
157
158 return validate_authority(decomposed.value().authority)
159 .and_then([&] { return validate_path(decomposed.value().path); })
160 .and_then([&] { return validate_query(decomposed.value().query); })
161 .and_then([&] { return validate_fragment(decomposed.value().fragment); });
162 }
163
164 virtual std::string_view scheme() const noexcept = 0;
165
166 virtual uri_error validate_authority(std::string_view fragment) const noexcept
167 {
168 return validate_user_info(fragment)
169 .and_then([&] { return validate_host(fragment); })
170 .and_then([&] { return validate_port(fragment); });
171 }
172 virtual uri_error validate_user_info(std::string_view element) const noexcept { return {}; }
173 virtual uri_error validate_host(std::string_view element) const noexcept { return {}; }
174 virtual uri_error validate_port(std::string_view element) const noexcept { return {}; }
175 virtual uri_error validate_path(std::string_view element) const noexcept { return {}; }
176 virtual uri_error validate_query(std::string_view element) const noexcept { return {}; }
177 virtual uri_error validate_fragment(std::string_view element) const noexcept { return {}; }
178
179 virtual std::string_view default_authority() const noexcept { return {}; }
180 virtual std::string_view default_user_info() const noexcept { return {}; }
181 virtual std::string_view default_host() const noexcept { return {}; }
182 virtual std::string_view default_port() const noexcept { return {}; }
183 virtual std::string_view default_path() const noexcept { return {}; }
184 virtual std::string_view default_query() const noexcept { return {}; }
185 virtual std::string_view default_fragment() const noexcept { return {}; }
186
187 virtual enum_flags<uri_decompose_flags> default_decompose_flags() const noexcept { return enum_flags<uri_decompose_flags>::all(); }
188 virtual enum_flags<uri_compose_flags> default_compose_flags() const noexcept { return enum_flags<uri_compose_flags>::all(); }
189
190 virtual std::vector<std::pair<std::string, std::string>> split_query_elements(std::string_view query) const noexcept;
191
192 virtual std::string normalize_authority(std::string_view element) const noexcept { return std::string{ element }; }
193 virtual std::string normalize_user_info(std::string_view element) const noexcept { return std::string{ element }; }
194 virtual std::string normalize_host(std::string_view element) const noexcept { return std::string{ element }; }
195 virtual std::string normalize_port(std::string_view element) const noexcept { return std::string{ element }; }
196 virtual std::string normalize_path(std::string_view element) const noexcept { return std::string{ element }; }
197 virtual std::string normalize_query(std::string_view element) const noexcept { return std::string{ element }; }
198 virtual std::string normalize_fragment(std::string_view element) const noexcept { return std::string{ element }; }
199
210 virtual uri_error_code iterate_scheme_elements(uri_view uri, std::function<bool(std::string_view element_name, std::string_view element_value)> callback) const noexcept
211 {
212 return uri_error_code::no_scheme_specific_elements;
213 }
214
215 uri_expected<uri> normalize_uri(uri_view uri);
216
217 virtual bool equivalent(uri_view u1, uri_view u2) const noexcept { return decompose_uri(u1) == decompose_uri(u2); }
218 };
219
220 known_uri_scheme const* query_uri_scheme(std::string_view scheme);
221
222 namespace known_schemes
223 {
224 struct file_scheme : public known_uri_scheme
225 {
227 virtual std::string_view scheme() const noexcept override { return "file"; }
228
229 static bool is_local(decomposed_uri const& uri)
230 {
231 return uri.host.empty() || uri.host == "localhost";
232 }
233 };
234 inline const file_scheme file;
235 }
236
237 struct uri_builder
238 {
239 explicit uri_builder(uri& uri);
240 uri_builder(uri& uri, known_uri_scheme const& scheme);
241 uri_builder(const uri_builder&) = delete;
242 uri_builder& operator= (const uri_builder&) = delete;
243 ~uri_builder();
244
245 template <class Source>
246 uri_builder& scheme(const Source& scheme);
247
248 template <class Source>
249 uri_builder& authority(const Source& authority);
250
251 template <class Source>
252 uri_builder& authority(const Source& user_info, const Source& host, const Source& port);
253
254 template <class Source>
255 uri_builder& user_info(const Source& user_info);
256
257 template <class Source>
258 uri_builder& host(const Source& host);
259
260 template <class Source>
261 uri_builder& port(const Source& port);
262
263 template <class Source>
264 uri_builder& path(const Source& path);
265
266 template <class Source>
267 uri_builder& query(const Source& query);
268
269 template <class Source>
270 uri_builder& fragment(const Source& fragment);
271
272 };
273
275
278}
279
280
281namespace std
282{
283 template <>
284 struct is_error_code_enum<ghassanpl::uri_error_code> : true_type {};
285}
286
287#include "uri_impl.h"
constexpr auto bit_count
Equal to the number of bits in the type.
Definition bits.h:33
uri_expected< uri > make_uri_safe_for_display(uri_view uri)
Removes data that should not be displayed to an untrusted user (user-info after the first ':',...
std::string uri
URIs are stored in a UTF-8 encoding where both non-ASCII code unit bytes as well as URI-reserved char...
Definition uri.h:28
uri_decompose_flags
Flags that modify how a URI string is decomposed into ghassanpl::decomposed_uri.
Definition uri.h:119
uri_expected< decomposed_uri > decompose_uri(uri_view uri, enum_flags< uri_decompose_flags > flags=enum_flags< uri_decompose_flags >::all())
This function will decompose URI into its composite elements, which includes percent-decoding all the...
Definition uri_impl.h:243
@ use_well_known_port_numbers
if a port is not specified in the uri, the result will guess the port based on the scheme
Primary namespace for everything in this library.
Definition align+rec2.h:10
Holds the constituents of a URI.
Definition uri.h:83
std::vector< std::string > normalized_path() const noexcept
Returns the path normalized by applying any "." or ".." elements.
Definition uri_impl.h:310
EF_NODISCARD static EF_CONSTEXPR self_type all() EF_NOEXCEPT
Returns a value with all bits set (including the ones not in the enum, if any)
Definition enum_flags.h:54