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