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
|
|
|
|
|
|
|
}} |