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
32 struct known_uri_scheme;
33
35 enum class uri_error_code
36 {
37 no_error,
38
39 unknown_uri_scheme,
40
41 scheme_malformed,
42 scheme_invalid,
43 scheme_empty,
44
45 authority_malformed,
46 authority_empty,
47 authority_not_allowed_in_scheme,
48 authority_invalid_for_scheme,
49 authority_required_in_scheme,
50
51 user_info_malformed,
52 user_info_not_allowed_in_scheme,
53 user_info_invalid_for_scheme,
54 user_info_required_in_scheme,
55
56 host_malformed,
57 host_not_allowed_in_scheme,
58 host_invalid_for_scheme,
59 host_required_in_scheme,
60
61 port_malformed,
62 port_not_allowed_in_scheme,
63 port_invalid_for_scheme,
64 port_required_in_scheme,
65
66 path_malformed,
67 path_element_malformed,
68
69 query_malformed,
70 query_not_allowed_in_scheme,
71 query_invalid_for_scheme,
72 query_required_in_scheme,
73
74 fragment_malformed,
75 fragment_not_allowed_in_scheme,
76 fragment_invalid_for_scheme,
77 fragment_required_in_scheme,
78
79 invalid_percent_encoding,
80
81 no_scheme_specific_elements,
82 scheme_specific_element_malformed,
83 };
84
85 template <typename T>
86 using uri_expected = expected<T, uri_error_code>;
87 using uri_error = expected<void, uri_error_code>;
88
91 {
92 split_query_elements,
93 split_path_elements,
95 //convert_plus_to_space,
96 lowercase_when_appropriate,
97 normalize_path,
98 query_known_scheme,
99 validate_known_scheme,
100 };
101
104 {
105 // TODO: Should the optional elements be std::optional<std::string> instead of relying on empty strings?
106 // Or we could have an enum_flags and accessors?
107
108 std::string scheme{};
109 std::string authority{};
110 std::string user_info{};
111 std::string host{};
112 std::string port{};
113 std::string path{};
114 std::vector<std::string> path_elements;
116 std::vector<std::string> normalized_path() const noexcept;
117 std::string query{};
118 std::vector<std::pair<std::string, std::string>> query_elements;
119 std::string fragment{};
120
121 known_uri_scheme const* known_scheme = nullptr;
122
124
125 bool canonical_form = false;
126
127 bool empty() const noexcept { return scheme.empty(); }
128
129 // URL Stuff
130
132 uri_expected<std::string> url_origin() const;
134 uri_expected<std::string> url_site() const;
136 uri_expected<std::pair<std::string, std::string>> url_user_info() const;
138 uri_expected<struct url_host> url_host() const;
140 uri_expected<struct url_blob> url_blob() const;
141
143 bool same_origin(decomposed_uri const& other) const noexcept;
145 bool same_site(decomposed_uri const& other) const noexcept;
146
147 bool operator==(decomposed_uri const& other) const noexcept;
148 };
149
150 uri_expected<std::string_view> extract_scheme(uri_view uri) noexcept;
152 uri_expected<std::string_view> extract_authority(uri_view uri) noexcept;
154 uri_expected<std::string_view> extract_path(uri_view uri) noexcept;
156 template <typename FUNC>
157 uri_expected<void> extract_path_elements(uri_view uri, FUNC&& func) noexcept;
159 uri_expected<std::string_view> extract_query(uri_view uri) noexcept;
161 uri_expected<std::string_view> extract_fragment(uri_view uri) noexcept;
163 template <typename FUNC>
164 uri_expected<void> extract_query_elements(uri_view uri, FUNC&& func) noexcept;
165
167 uri_expected<uri> make_uri_safe_for_display(uri_view uri);
168
170 uri_expected<decomposed_uri> decompose_uri(uri_view uri, enum_flags<uri_decompose_flags> flags = enum_flags<uri_decompose_flags>::all());
171
172 enum class uri_compose_flags
173 {
174 path_leading_slash,
175 path_trailing_slash,
176 lowercase_when_appropriate,
177 normalize_path,
178 use_known_scheme,
179 //convert_space_to_plus
180 };
181
184
186 uri_expected<uri> normalize_uri(uri_view uri);
187
190 {
191 virtual ~known_uri_scheme() noexcept = default;
192
193 virtual uri_error validate(uri_view uri) const noexcept
194 {
195 auto decomposed = decompose_uri(uri);
196 if (!decomposed) return decomposed.transform([](auto) {});
197
198 return validate(decomposed.value());
199 }
200
201 virtual uri_error validate(decomposed_uri const& decomposed) const noexcept
202 {
203 if (decomposed.scheme != scheme()) return unexpected(uri_error_code::scheme_invalid);
204
205 return validate_authority(decomposed.authority)
206 .and_then([&] { return validate_path(decomposed.path); })
207 .and_then([&] { return validate_query(decomposed.query); })
208 .and_then([&] { return validate_fragment(decomposed.fragment); });
209 }
210
211 virtual std::string_view scheme() const noexcept = 0;
212
213 virtual uri_error validate_authority(std::string_view fragment) const noexcept
214 {
215 return validate_user_info(fragment)
216 .and_then([&] { return validate_host(fragment); })
217 .and_then([&] { return validate_port(fragment); });
218 }
219 virtual uri_error validate_user_info(std::string_view element) const noexcept { return {}; }
220 virtual uri_error validate_host(std::string_view element) const noexcept { return {}; }
221 virtual uri_error validate_port(std::string_view element) const noexcept { return {}; }
222 virtual uri_error validate_path(std::string_view element) const noexcept { return {}; }
223 virtual uri_error validate_query(std::string_view element) const noexcept { return {}; }
224 virtual uri_error validate_fragment(std::string_view element) const noexcept { return {}; }
225
226 virtual std::string_view default_authority() const noexcept { return {}; }
227 virtual std::string_view default_user_info() const noexcept { return {}; }
228 virtual std::string_view default_host() const noexcept { return {}; }
229 virtual std::string_view default_port() const noexcept { return {}; }
230 virtual std::string_view default_path() const noexcept { return {}; }
231 virtual std::string_view default_query() const noexcept { return {}; }
232 virtual std::string_view default_fragment() const noexcept { return {}; }
233
234 virtual enum_flags<uri_decompose_flags> default_decompose_flags() const noexcept { return enum_flags<uri_decompose_flags>::all(); }
235 virtual enum_flags<uri_compose_flags> default_compose_flags() const noexcept { return enum_flags<uri_compose_flags>::all(); }
236
237 virtual std::vector<std::pair<std::string, std::string>> split_query_elements(std::string_view query) const noexcept;
238
239 virtual std::string normalize_authority(std::string_view element) const noexcept { return std::string{ element }; }
240 virtual std::string normalize_user_info(std::string_view element) const noexcept { return std::string{ element }; }
241 virtual std::string normalize_host(std::string_view element) const noexcept { return std::string{ element }; }
242 virtual std::string normalize_port(std::string_view element) const noexcept { return std::string{ element }; }
243 virtual std::string normalize_path(std::string_view element) const noexcept { return std::string{ element }; }
244 virtual std::string normalize_query(std::string_view element) const noexcept { return std::string{ element }; }
245 virtual std::string normalize_fragment(std::string_view element) const noexcept { return std::string{ element }; }
246
257 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
258 {
259 return uri_error_code::no_scheme_specific_elements;
260 }
261
262 uri_expected<uri> normalize_uri(uri_view uri);
263
264 virtual bool equivalent(uri_view u1, uri_view u2) const noexcept { return decompose_uri(u1) == decompose_uri(u2); }
265 };
266
268 known_uri_scheme const* query_uri_scheme(std::string_view scheme);
269
272 namespace known_schemes
273 {
276 {
278 virtual std::string_view scheme() const noexcept override { return "file"; }
279
280 static bool is_local(decomposed_uri const& uri)
281 {
282 return uri.host.empty() || uri.host == "localhost";
283 }
284
285 static std::filesystem::path filesystem_path(decomposed_uri const& uri)
286 {
287 if (uri.path_elements.empty())
288 {
290 return {};
291 }
292
293 std::filesystem::path result;
294 for (auto& pe : uri.path_elements)
295 result /= pe;
296 return result;
297 }
298 };
299
300 inline const file_scheme file;
301 }
302
304 struct uri_builder
305 {
306 explicit uri_builder(uri& uri);
307 uri_builder(uri& uri, known_uri_scheme const& scheme);
308 uri_builder(const uri_builder&) = delete;
309 uri_builder& operator= (const uri_builder&) = delete;
310 ~uri_builder();
311
312 template <class Source>
313 uri_builder& scheme(const Source& scheme);
314
315 template <class Source>
316 uri_builder& authority(const Source& authority);
317
318 template <class Source>
319 uri_builder& authority(const Source& user_info, const Source& host, const Source& port);
320
321 template <class Source>
322 uri_builder& user_info(const Source& user_info);
323
324 template <class Source>
325 uri_builder& host(const Source& host);
326
327 template <class Source>
328 uri_builder& port(const Source& port);
329
330 template <class Source>
331 uri_builder& path(const Source& path);
332
333 template <class Source>
334 uri_builder& query(const Source& query);
335
336 template <class Source>
337 uri_builder& fragment(const Source& fragment);
338
339 };
340
342
345}
346
347
348namespace std
349{
350 template <>
351 struct is_error_code_enum<ghassanpl::uri_error_code> : true_type {};
352}
353
354#include "uri_impl.h"
uri_expected< uri > normalize_uri(uri_view uri)
[Unimplemented] Converts a URI to a normalized form
uri_expected< std::string_view > extract_query(uri_view uri) noexcept
[Unimplemented]
uri_expected< uri > compose_uri(decomposed_uri const &decomposed, enum_flags< uri_compose_flags > flags=enum_flags< uri_compose_flags >::all())
[Unimplemented]
uri_expected< void > extract_query_elements(uri_view uri, FUNC &&func) noexcept
[Unimplemented]
uri_error_code
Error codes relating to URI decomposing, composing, and other operations.
Definition uri.h:36
uri_expected< uri > make_uri_safe_for_display(uri_view uri)
[Unimplemented] Removes data that should not be displayed to an untrusted user (user-info after the f...
uri_expected< std::string_view > extract_path(uri_view uri) noexcept
[Unimplemented]
uri_expected< std::string_view > extract_authority(uri_view uri) noexcept
[Unimplemented]
uri_decompose_flags
Flags that modify how a URI string is decomposed into ghassanpl::decomposed_uri.
Definition uri.h:91
uri_expected< void > extract_path_elements(uri_view uri, FUNC &&func) noexcept
[Unimplemented]
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_expected< std::string_view > extract_fragment(uri_view uri) noexcept
[Unimplemented]
known_uri_scheme const * query_uri_scheme(std::string_view scheme)
Looks up the scheme in the internal URI scheme database.
Definition uri_impl.h:395
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:257
@ use_well_known_port_numbers
if a port is not specified in the uri, the result will guess the port based on the scheme
Definition uri.h:94
Primary namespace for everything in this library.
Definition align+rec2.h:10
Contains known URI scheme types and objects.
Holds the constituents of a URI.
Definition uri.h:104
uri_expected< struct url_blob > url_blob() const
[Unimplemented]
uri_expected< std::string > url_origin() const
[Unimplemented]
uri_expected< std::string > url_site() const
[Unimplemented]
uri_expected< struct url_host > url_host() const
[Unimplemented]
bool same_site(decomposed_uri const &other) const noexcept
[Unimplemented]
bool same_origin(decomposed_uri const &other) const noexcept
[Unimplemented]
std::vector< std::string > normalized_path() const noexcept
Returns the path normalized by applying any "." or ".." elements.
Definition uri_impl.h:335
uri_expected< std::pair< std::string, std::string > > url_user_info() const
[Unimplemented]
A (constexpr) value struct that represents a set of bits mapped to an enum (implemented as a bitset).
Definition enum_flags.h:60
static constexpr self_type all() noexcept
Returns a value with all bits set (including the ones not in the enum, if any).
Definition enum_flags.h:103
The "file:" known scheme.
Definition uri.h:276
virtual std::string_view scheme() const noexcept override
https://datatracker.ietf.org/doc/html/rfc8089
Definition uri.h:278
An base class for classes that can validate (and potentially parse more info from) a known URI scheme...
Definition uri.h:190
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
Calls the callback function for each scheme-specific "element" of the uri.
Definition uri.h:257