File Coverage

src/panda/uri/URI.h
Criterion Covered Total %
statement 1 1 100.0
branch n/a
condition n/a
subroutine n/a
pod n/a
total 1 1 100.0


line stmt bran cond sub pod time code
1             #pragma once
2             #include
3             #include
4             #include
5             #include
6             #include
7             #include
8             #include
9             #include
10             #include
11             #include
12             #include
13             #include
14              
15             namespace panda { namespace uri {
16              
17             struct URIError : std::logic_error {
18             explicit URIError (const std::string& what_arg) : logic_error(what_arg) {}
19             };
20              
21             struct WrongScheme : URIError {
22             explicit WrongScheme (const std::string& what_arg) : URIError(what_arg) {}
23             };
24              
25             struct URI : Refcnt {
26             struct Flags {
27             static constexpr const int allow_suffix_reference = 1; // https://tools.ietf.org/html/rfc3986#section-4.5 uri may omit leading "SCHEME://"
28             static constexpr const int query_param_semicolon = 2; // query params are delimited by ';' instead of '&'
29             static constexpr const int allow_extended_chars = 4; // non-RFC input: allow some unencoded chars in query string
30             };
31              
32             template struct Strict;
33             struct http; struct https; struct ftp; struct socks; struct ws; struct wss; struct ssh; struct telnet; struct sftp;
34              
35             using uricreator = URI*(*)(const URI& uri);
36              
37             struct SchemeInfo {
38             int index;
39             string scheme;
40             uricreator creator;
41             uint16_t default_port;
42             bool secure;
43             const std::type_info* type_info;
44             };
45              
46             static void register_scheme (const string& scheme, uint16_t default_port, bool secure = false);
47             static void register_scheme (const string& scheme, const std::type_info*, uricreator, uint16_t default_port, bool secure = false);
48              
49             static URI* create (const string& source, int flags = 0) {
50             URI temp(source, flags);
51             if (temp.scheme_info) return temp.scheme_info->creator(temp);
52             else return new URI(temp);
53             }
54              
55             static URI* create (const URI& source) {
56             if (source.scheme_info) return source.scheme_info->creator(source);
57             else return new URI(source);
58             }
59              
60             URI () : scheme_info(NULL), _port(0), _qrev(1), _flags(0) {}
61             URI (const string& s, int flags = 0) : scheme_info(NULL), _port(0), _qrev(1), _flags(flags) { parse(s); }
62             URI (const string& s, const Query& q, int flags = 0) : URI(s, flags) { add_query(q); }
63             URI (const URI& s) { assign(s); }
64              
65             URI& operator= (const URI& source) { if (this != &source) assign(source); return *this; }
66             URI& operator= (const string& source) { assign(source); return *this; }
67              
68             const string& scheme () const { return _scheme; }
69             const string& user_info () const { return _user_info; }
70             const string& host () const { return _host; }
71             const string& path () const { return _path; }
72             const string& fragment () const { return _fragment; }
73             uint16_t explicit_port () const { return _port; }
74             uint16_t default_port () const { return scheme_info ? scheme_info->default_port : 0; }
75             uint16_t port () const { return _port ? _port : default_port(); }
76             bool secure () const { return scheme_info ? scheme_info->secure : false; }
77              
78             virtual void assign (const URI& source) {
79             _scheme = source._scheme;
80             scheme_info = source.scheme_info;
81             _user_info = source._user_info;
82             _host = source._host;
83             _path = source._path;
84             _qstr = source._qstr;
85             _query = source._query;
86             _query.rev = source._query.rev;
87             _qrev = source._qrev;
88             _fragment = source._fragment;
89             _port = source._port;
90             _flags = source._flags;
91             }
92              
93             void assign (const string& s, int flags = 0) {
94             clear();
95             _flags = flags;
96             parse(s);
97             }
98              
99             const string& query_string () const {
100             sync_query_string();
101             return _qstr;
102             }
103              
104             const string raw_query () const {
105             sync_query_string();
106             return decode_uri_component(_qstr);
107             }
108              
109             Query& query () {
110             sync_query();
111             return _query;
112             }
113              
114             const Query& query () const {
115             sync_query();
116             return _query;
117             }
118              
119             virtual void scheme (const string& scheme) {
120             _scheme = scheme;
121             sync_scheme_info();
122             }
123              
124             void user_info (const string& user_info) { _user_info = user_info; }
125             void host (const string& host) { _host = host; }
126             void fragment (const string& fragment) { _fragment = fragment; }
127             void port (uint16_t port) { _port = port; }
128              
129             void path (const string& path) {
130             if (path && path.front() != '/') {
131             _path = '/';
132             _path += path;
133             }
134             else _path = path;
135             }
136              
137             void query_string (const string& qstr) {
138             _qstr = qstr;
139             ok_qstr();
140             }
141              
142             void raw_query (const string& rq) {
143             _qstr.clear();
144             encode_uri_component(rq, _qstr, URIComponent::query);
145             ok_qstr();
146             }
147              
148             void query (const string& qstr) { query_string(qstr); }
149             void query (const Query& query) {
150             _query = query;
151             ok_query();
152             }
153              
154             void add_query (const string& addstr) {
155             if (!addstr) return;
156             sync_query_string();
157             ok_qstr();
158             if (_qstr) {
159             _qstr.reserve(_qstr.length() + addstr.length() + 1);
160             _qstr += '&';
161             _qstr += addstr;
162             }
163             else _qstr = addstr;
164             }
165              
166             void add_query (const Query& addquery);
167              
168             const string& param (const string_view& key) const {
169             sync_query();
170             const auto& cq = _query;
171             auto it = cq.find(key);
172             return it == cq.cend() ? _empty : it->second;
173             }
174              
175             void param (const string& key, const string& val) {
176             sync_query();
177             auto it = _query.find(key);
178             if (it != _query.cend()) it->second.assign(val);
179             else _query.emplace(key, val);
180             }
181              
182             string explicit_location () const {
183             if (!_port) return _host;
184             return location();
185             }
186              
187             string location () const {
188             string ret(_host.length() + 6); // port is 5 chars max
189             if (_host) ret += _host;
190             ret += ':';
191             char* buf = ret.buf(); // has exactly 5 bytes left
192             auto len = ret.length();
193             auto ptr_start = buf + len;
194             auto res = to_chars(ptr_start, buf + ret.capacity(), port());
195             assert(!res.ec); // because buf is always enough
196             ret.length(len + (res.ptr - ptr_start));
197             return ret;
198             }
199              
200             void location (const string& newloc) {
201             if (!newloc) {
202             _host.clear();
203             _port = 0;
204             return;
205             }
206              
207             size_t delim = newloc.rfind(':');
208             if (delim == string::npos) _host.assign(newloc);
209             else {
210             size_t ipv6end = newloc.rfind(']');
211             if (ipv6end != string::npos && ipv6end > delim) _host.assign(newloc);
212             else {
213             _host.assign(newloc, 0, delim);
214             _port = 0;
215             from_chars(newloc.data() + delim + 1, newloc.data() + newloc.length(), _port);
216             }
217             }
218             }
219              
220             const std::vector path_segments () const;
221              
222             template
223             void path_segments (It begin, It end) {
224             _path.clear();
225             for (auto it = begin; it != end; ++it) {
226             if (!it->length()) continue;
227             _path += '/';
228             _encode_uri_component_append(*it, _path, URIComponent::path_segment);
229             }
230             }
231              
232             string to_string (bool relative = false) const;
233             string relative () const { return to_string(true); }
234              
235             bool equals (const URI& uri) const {
236             if (_path != uri._path || _host != uri._host || _user_info != uri._user_info || _fragment != uri._fragment || _scheme != uri._scheme) return false;
237             if (_port != uri._port && port() != uri.port()) return false;
238             sync_query_string();
239             uri.sync_query_string();
240             return _qstr == uri._qstr;
241             }
242              
243             void swap (URI& uri);
244              
245             string user () const;
246             void user (const string& user);
247              
248             string password () const;
249             void password (const string& password);
250              
251             virtual ~URI () {}
252              
253             protected:
254             SchemeInfo* scheme_info;
255              
256             virtual void parse (const string&);
257              
258             static SchemeInfo* get_scheme_info (const std::type_info*);
259              
260             private:
261             string _scheme;
262             string _user_info;
263             string _host;
264             string _path;
265             string _fragment;
266             uint16_t _port;
267             mutable string _qstr;
268             mutable Query _query;
269             mutable uint32_t _qrev; // last query rev we've synced query string with (0 if query itself isn't synced with string)
270             int _flags;
271              
272             static const string _empty;
273              
274             void ok_qstr () const { _qrev = 0; }
275 3           void ok_query () const { _qrev = _query.rev - 1; }
276             void ok_qboth () const { _qrev = _query.rev; }
277             bool has_ok_qstr () const { return !_qrev || _qrev == _query.rev; }
278             bool has_ok_query () const { return _qrev != 0; }
279              
280             void clear () {
281             _port = 0;
282             _scheme.clear();
283             scheme_info = NULL;
284             _user_info.clear();
285             _host.clear();
286             _path.clear();
287             _qstr.clear();
288             _query.clear();
289             _fragment.clear();
290             ok_qboth();
291             _flags = 0;
292             }
293              
294             void guess_suffix_reference ();
295              
296             void compile_query () const;
297             void parse_query () const;
298              
299             void sync_query_string () const { if (!has_ok_qstr()) compile_query(); }
300             void sync_query () const { if (!has_ok_query()) parse_query(); }
301              
302             void sync_scheme_info ();
303              
304             static inline void _encode_uri_component_append (const string_view& src, string& dest, const char* unsafe) {
305             char* buf = dest.reserve(dest.length() + src.length()*3) + dest.length();
306             size_t final_size = encode_uri_component(src, buf, unsafe);
307             dest.length(dest.length() + final_size);
308             }
309              
310             bool _parse (const string&, bool&);
311             bool _parse_ext (const string&, bool&);
312             };
313              
314             using URISP = iptr;
315              
316             std::ostream& operator<< (std::ostream& os, const URI& uri);
317              
318             inline bool operator== (const URI& lhs, const URI& rhs) { return lhs.equals(rhs); }
319             inline bool operator!= (const URI& lhs, const URI& rhs) { return !lhs.equals(rhs); }
320             inline void swap (URI& l, URI& r) { l.swap(r); }
321              
322             }}