/src/botan/build/include/public/botan/uri.h
Line | Count | Source |
1 | | /* |
2 | | * (C) 2026 Jack Lloyd |
3 | | * |
4 | | * Botan is released under the Simplified BSD License (see license.txt) |
5 | | */ |
6 | | |
7 | | #ifndef BOTAN_URI_H_ |
8 | | #define BOTAN_URI_H_ |
9 | | |
10 | | #include <botan/dns_name.h> |
11 | | #include <botan/ipv4_address.h> |
12 | | #include <botan/ipv6_address.h> |
13 | | #include <botan/types.h> |
14 | | #include <functional> |
15 | | #include <optional> |
16 | | #include <string> |
17 | | #include <string_view> |
18 | | #include <variant> |
19 | | |
20 | | namespace Botan { |
21 | | |
22 | | /** |
23 | | * URI (RFC 3986 subset) |
24 | | */ |
25 | | class BOTAN_PUBLIC_API(3, 13) URI final { |
26 | | public: |
27 | | /** |
28 | | * The optional authority component of a URI: a validated DNS name, IPv4 |
29 | | * literal, or IPv6 literal, with an optional port. |
30 | | */ |
31 | | class BOTAN_PUBLIC_API(3, 13) Authority final { |
32 | | public: |
33 | | /** |
34 | | * A validated DNS name, or a literal IPv4 or IPv6 address. |
35 | | */ |
36 | | using Host = std::variant<DNSName, IPv4Address, IPv6Address>; |
37 | | |
38 | | /** |
39 | | * Tag for the alternative held by `Host`. |
40 | | */ |
41 | | enum class HostKind : uint8_t { |
42 | | DNS = 0, |
43 | | IPv4 = 1, |
44 | | IPv6 = 2, |
45 | | }; |
46 | | |
47 | | /** |
48 | | * Parse a bare authority "host[:port]" or "[ipv6][:port]". |
49 | | * Returns nullopt for any parse failure. |
50 | | */ |
51 | | static std::optional<Authority> from_string(std::string_view raw); |
52 | | |
53 | | /** |
54 | | * Parsed host: a DNS name, an IPv4 literal, or an IPv6 literal. |
55 | | */ |
56 | 0 | const Host& host() const { return m_host; } |
57 | | |
58 | | /** |
59 | | * Which alternative of `host()` is held. |
60 | | */ |
61 | | HostKind host_kind() const; |
62 | | |
63 | | /** |
64 | | * The host as a string: DNS names and dotted-IPv4 literals are |
65 | | * returned verbatim; IPv6 literals are returned without surrounding |
66 | | * brackets. Lowercased for DNS / IPv4; the IPv6 form is whatever |
67 | | * `IPv6Address::to_string` produces. |
68 | | */ |
69 | | std::string host_to_string() const; |
70 | | |
71 | | /** |
72 | | * Port if present; nullopt otherwise. |
73 | | */ |
74 | 0 | std::optional<uint16_t> port() const { return m_port; } |
75 | | |
76 | | /** |
77 | | * The original input that was parsed |
78 | | */ |
79 | 0 | const std::string& original_input() const { return m_raw; } |
80 | | |
81 | | /** |
82 | | * The userinfo component, preserved verbatim (no case normalization |
83 | | * or pct-decoding) and compared verbatim for identity. nullopt if no |
84 | | * "@" was present; present-but-empty (e.g. "https://@example.com/") |
85 | | * is distinguished from absent. |
86 | | */ |
87 | 0 | const std::optional<std::string>& userinfo() const { return m_userinfo; } |
88 | | |
89 | | /** |
90 | | * Order two authorities |
91 | | * @param other the authority to compare against |
92 | | * @return the ordering of this authority relative to other |
93 | | */ |
94 | | std::strong_ordering operator<=>(const Authority& other) const; |
95 | | |
96 | | /** |
97 | | * Compare two authorities |
98 | | * @param other the authority to compare against |
99 | | * @return true if the two authorities are equal |
100 | | */ |
101 | | bool operator==(const Authority& other) const; |
102 | | |
103 | | private: |
104 | | Authority(std::string raw, std::optional<std::string> userinfo, Host host, std::optional<uint16_t> port) : |
105 | 6.03k | m_raw(std::move(raw)), m_userinfo(std::move(userinfo)), m_host(std::move(host)), m_port(port) {} |
106 | | |
107 | | std::string m_raw; |
108 | | std::optional<std::string> m_userinfo; |
109 | | Host m_host; |
110 | | std::optional<uint16_t> m_port; |
111 | | }; |
112 | | |
113 | | /// A validated DNS name, or a literal IPv4 or IPv6 address |
114 | | using Host = Authority::Host; |
115 | | |
116 | | /// Tag for the alternative held by `Host` |
117 | | using HostKind = Authority::HostKind; |
118 | | |
119 | | /** |
120 | | * Parse a URI, return nullopt on failure |
121 | | */ |
122 | | static std::optional<URI> from_string(std::string_view raw); |
123 | | |
124 | | /** |
125 | | * Return the scheme, lowercase normalized |
126 | | */ |
127 | 0 | const std::string& scheme() const { return m_scheme; } |
128 | | |
129 | | /** |
130 | | * Return the parsed URI authority, if this URI has one. |
131 | | */ |
132 | 0 | const std::optional<Authority>& authority() const { return m_authority; } |
133 | | |
134 | | /** |
135 | | * Return the raw authority component if this URI included one, including |
136 | | * the empty string for URIs such as "ldap:///CN=...". |
137 | | */ |
138 | | std::optional<std::string_view> raw_authority() const; |
139 | | |
140 | | /** |
141 | | * Return the parsed host, if this URI has an authority. |
142 | | * TODO(C++26) This can return std::optional<const Host&> |
143 | | */ |
144 | 0 | std::optional<std::reference_wrapper<const Host>> host() const { |
145 | 0 | return m_authority.has_value() ? std::optional<std::reference_wrapper<const Host>>(m_authority->host()) |
146 | 0 | : std::nullopt; |
147 | 0 | } |
148 | | |
149 | | /** |
150 | | * The path component, preserved verbatim. Begins with "/" when present; |
151 | | * empty if the parsed URI had no path (e.g. "http://example.com" or |
152 | | * "http://example.com?q"). |
153 | | */ |
154 | 0 | const std::string& path() const { return m_path; } |
155 | | |
156 | | /** |
157 | | * The query component, without the leading "?". Nullopt if no "?" was |
158 | | * present; present-but-empty distinguishes "http://h/p?" from |
159 | | * "http://h/p". |
160 | | */ |
161 | 0 | const std::optional<std::string>& query() const { return m_query; } |
162 | | |
163 | | /** |
164 | | * The fragment component, without the leading "#". Nullopt if no "#" |
165 | | * was present; present-but-empty distinguishes "http://h/p#" from |
166 | | * "http://h/p". |
167 | | */ |
168 | 0 | const std::optional<std::string>& fragment() const { return m_fragment; } |
169 | | |
170 | | /** |
171 | | * The original input that was parsed. |
172 | | */ |
173 | 0 | const std::string& original_input() const { return m_raw; } |
174 | | |
175 | | /** |
176 | | * Order two URIs |
177 | | * @param other the URI to compare against |
178 | | * @return the ordering of this URI relative to other |
179 | | */ |
180 | | std::strong_ordering operator<=>(const URI& other) const; |
181 | | |
182 | | /** |
183 | | * Compare two URIs |
184 | | * @param other the URI to compare against |
185 | | * @return true if the two URIs are equal |
186 | | */ |
187 | | bool operator==(const URI& other) const; |
188 | | |
189 | | /** |
190 | | * Return a list of URIs (possibly empty) which match the specified scheme |
191 | | * and which contain a non-empty authority |
192 | | */ |
193 | | static std::vector<URI> filter_scheme(std::string_view scheme, std::span<const URI> uris); |
194 | | |
195 | | private: |
196 | | URI(std::string raw, |
197 | | std::string scheme, |
198 | | std::optional<Authority> authority, |
199 | | std::string path, |
200 | | std::optional<std::string> query, |
201 | | std::optional<std::string> fragment) : |
202 | 8.91k | m_raw(std::move(raw)), |
203 | 8.91k | m_scheme(std::move(scheme)), |
204 | 8.91k | m_authority(std::move(authority)), |
205 | 8.91k | m_path(std::move(path)), |
206 | 8.91k | m_query(std::move(query)), |
207 | 8.91k | m_fragment(std::move(fragment)) {} |
208 | | |
209 | | std::string m_raw; |
210 | | std::string m_scheme; |
211 | | std::optional<Authority> m_authority; |
212 | | std::string m_path; |
213 | | std::optional<std::string> m_query; |
214 | | std::optional<std::string> m_fragment; |
215 | | }; |
216 | | |
217 | | } // namespace Botan |
218 | | |
219 | | #endif |