File Coverage

src/panda/uri/URI.cc
Criterion Covered Total %
statement 174 223 78.0
branch 176 298 59.0
condition n/a
subroutine n/a
pod n/a
total 350 521 67.1


line stmt bran cond sub pod time code
1             #include
2             #include
3             #include
4             #include
5              
6             namespace panda { namespace uri {
7              
8 13           static std::unordered_map scheme_map;
9 13           static std::map scheme_ti_map;
10 13           static std::vector schemas;
11              
12             static URI::SchemeInfo* http_si;
13             static URI::SchemeInfo* https_si;
14              
15 13           const string URI::_empty;
16              
17 0           void URI::register_scheme (const string& scheme, uint16_t default_port, bool secure) {
18 0 0         register_scheme(scheme, &typeid(URI), [](const URI& u)->URI*{ return new URI(u); }, default_port, secure);
    0          
19 0           }
20              
21 117           void URI::register_scheme (const string& scheme, const std::type_info* ti, uricreator creator, uint16_t default_port, bool secure) {
22 117 50         if (scheme_map.find(scheme) != scheme_map.end())
    50          
23 0 0         throw std::invalid_argument("URI::register_scheme: scheme '" + scheme + "' has been already registered");
    0          
    0          
    0          
24 117           auto& inf = scheme_map[scheme];
25 117           inf.index = schemas.size();
26 117           inf.scheme = scheme;
27 117           inf.creator = creator;
28 117           inf.default_port = default_port;
29 117           inf.secure = secure;
30 117           inf.type_info = ti;
31 117           scheme_ti_map[ti] = &inf;
32 117 50         schemas.push_back(&inf);
33 117           }
34              
35              
36 13           static int init () {
37 72 50         URI::register_scheme("http", &typeid(URI::http), [](const URI& u)->URI*{ return new URI::http(u); }, 80 );
    50          
38 42 50         URI::register_scheme("https", &typeid(URI::https), [](const URI& u)->URI*{ return new URI::https(u); }, 443, true);
    50          
39 42 50         URI::register_scheme("ws", &typeid(URI::ws), [](const URI& u)->URI*{ return new URI::ws(u); }, 80 );
    50          
40 42 50         URI::register_scheme("wss", &typeid(URI::wss), [](const URI& u)->URI*{ return new URI::wss(u); }, 443, true);
    50          
41 42 50         URI::register_scheme("ftp", &typeid(URI::ftp), [](const URI& u)->URI*{ return new URI::ftp(u); }, 21 );
    50          
42 42 50         URI::register_scheme("socks5", &typeid(URI::socks), [](const URI& u)->URI*{ return new URI::socks(u); }, 1080 );
    50          
43 42 50         URI::register_scheme("ssh", &typeid(URI::ssh), [](const URI& u)->URI*{ return new URI::ssh(u); }, 22, true);
    50          
44 42 50         URI::register_scheme("telnet", &typeid(URI::telnet), [](const URI& u)->URI*{ return new URI::telnet(u); }, 23 );
    50          
45 42 50         URI::register_scheme("sftp", &typeid(URI::sftp), [](const URI& u)->URI*{ return new URI::sftp(u); }, 22, true);
    50          
46              
47 13 50         http_si = &scheme_map.find("http")->second;
48 13 50         https_si = &scheme_map.find("https")->second;
49              
50 13           return 0;
51             }
52 13           static const int __init = init();
53              
54 9           void URI::guess_suffix_reference () {
55             // try to find out if it was an url with leading authority ('ya.ru', 'ya.ru:80/a/b/c', 'user@mysite.com/a/b/c')
56             // in either case host is always empty and there are 2 cases
57             // 1) if no scheme -> host is first path part, port is absent
58             // 2) if scheme is present and first path part is a valid port -> scheme is host, first path part is port.
59             // otherwise leave parsed url unchanged as it has no leading authority
60 9 100         if (!_scheme.length()) {
61 3           size_t delim = _path.find('/');
62 3 100         if (delim == string::npos) {
63 1           _host = _path;
64 1           _path.clear();
65             } else {
66 2           _host.assign(_path, 0, delim);
67 2           _path.erase(0, delim);
68             }
69 3           return;
70             }
71              
72 6           bool ok = false;
73 6           size_t plen = _path.length();
74 6           const char* p = _path.data();
75 6           size_t i = 0;
76 18 100         for (; i < plen; ++i) {
77 16           char c = p[i];
78 16 100         if (c >= '0' && c <= '9') {
    100          
79 12           _port = _port * 10 + c - '0';
80 12           ok = true;
81 12           continue;
82             }
83 4 100         if (c != '/') { _port = 0; return; }
84 2           break;
85             }
86              
87 4 100         if (!ok) return;
88 2           _host = _scheme;
89 2           _scheme.clear();
90 2           _path.erase(0, i);
91             }
92              
93 336           string URI::to_string (bool relative) const {
94 336           sync_query_string();
95 336           size_t approx_len = _path.length() + _fragment.length() + _qstr.length() + 3;
96 336 100         if (!relative) approx_len += (_scheme.length()+3) + (_user_info.length()*3 + 1) + (_host.length()*3 + 6);
97 336           string str(approx_len);
98              
99 336 100         if (!relative) {
100 334 100         if (_scheme.length()) {
101 257 50         str += _scheme;
102 257 100         if (_host.length()) str += "://";
    50          
103 257 50         else str += ':';
104             }
105 77 100         else if (_host.length()) str += "//";
    50          
106              
107 334 100         if (_host.length()) {
108 244 100         if (_user_info.length()) {
109 50 50         _encode_uri_component_append(_user_info, str, URIComponent::user_info);
110 50 50         str += '@';
111             }
112              
113 244           const auto& chost = _host;
114 244 100         if (chost.front() == '[' && chost.back() == ']') str += _host;
    50          
    100          
    50          
115 209 50         else _encode_uri_component_append(_host, str, URIComponent::host);
116              
117 244 100         if (_port) {
118 65 50         str += ':';
119 334 50         str += string::from_number(_port);
    50          
120             }
121             }
122             }
123              
124 336 100         if (_path.length()) str += _path;
    50          
125 151 100         else if (relative) str += '/'; // relative path MUST NOT be empty
    50          
126              
127 336 100         if (_qstr.length()) {
128 102 50         str += '?';
129 102 50         str += _qstr; // as is, because already encoded either by raw_query setter or by compile_query
130             }
131              
132 336 100         if (_fragment.length()) {
133 98 50         str += '#';
134 98 50         str += _fragment;
135             }
136              
137 336           return str;
138             }
139              
140 8           void URI::parse_query () const {
141 8           enum { PARSE_MODE_KEY, PARSE_MODE_VAL, PARSE_MODE_WRITE } mode = PARSE_MODE_KEY;
142 8           int key_start = 0;
143 8           int key_end = 0;
144 8           int val_start = 0;
145 8           bool has_pct = false;
146 8 100         const char delim = _flags & Flags::query_param_semicolon ? ';' : '&';
147 8           const char* str = _qstr.data();
148 8           int len = _qstr.length();
149 8           _query.clear();
150              
151 100 100         if (len) for (int i = 0; i <= len; ++i) {
    100          
152 92 100         char c = (i == len) ? delim : str[i];
153 92 100         if (c == '=' && mode == PARSE_MODE_KEY) {
    50          
154 16           key_end = i;
155 16           mode = PARSE_MODE_VAL;
156 16           val_start = i+1;
157             }
158 76 100         else if (c == '%') {
159 3           has_pct = true;
160             }
161 73 100         else if (c == delim) {
162 17 100         if (mode == PARSE_MODE_KEY) {
163 1           key_end = i;
164 1           val_start = i;
165             }
166              
167 17 100         if (has_pct) {
168 6           string key, value;
169 3           size_t klen = key_end - key_start;
170 3 50         if (klen > 0) decode_uri_component(string_view(str+key_start, klen), key);
    50          
171              
172 3           size_t vlen = i - val_start;
173 3 50         if (vlen > 0) decode_uri_component(string_view(str+val_start, vlen), value);
    50          
174              
175 3           has_pct = false;
176 3 50         _query.emplace(key, value);
177             } else {
178 14 50         _query.emplace(_qstr.substr(key_start, key_end - key_start), _qstr.substr(val_start, i - val_start));
    50          
179             }
180              
181 17           mode = PARSE_MODE_KEY;
182 17           key_start = i+1;
183             }
184             }
185              
186 8           ok_qboth();
187 8           }
188              
189 6           void URI::compile_query () const {
190 6           _qstr.clear();
191 6 50         const char delim = _flags & Flags::query_param_semicolon ? ';' : '&';
192 6           auto begin = _query.cbegin();
193 6           auto end = _query.cend();
194              
195 6           size_t bufsize = 0;
196 22 100         for (auto it = begin; it != end; ++it) bufsize += (it->first.length() + it->second.length())*3 + 2;
197 6 50         if (bufsize) --bufsize;
198              
199 6 50         _qstr.reserve(bufsize);
200              
201 6 50         char* bufp = _qstr.buf();
202 6           char* ptr = bufp;
203 22 100         for (auto it = begin; it != end; ++it) {
204 16 100         if (it != begin) *ptr++ = delim;
205 16 50         ptr += encode_uri_component(it->first, ptr);
206 16           *ptr++ = '=';
207 16 50         ptr += encode_uri_component(it->second, ptr);
208             }
209 6           _qstr.length(ptr-bufp);
210              
211 6           ok_qboth();
212 6           }
213              
214 0           void URI::add_query (const Query& addquery) {
215 0 0         sync_query();
216 0           auto end = addquery.cend();
217 0 0         for (auto it = addquery.cbegin(); it != end; ++it) _query.emplace(it->first, it->second);
    0          
218 0           ok_query();
219 0           }
220              
221 5           const std::vector URI::path_segments () const {
222 5           size_t plen = _path.length();
223 5 100         if (!plen) return std::vector();
224 9           std::vector ret;
225 4 50         ret.reserve(7);
226 4           const char* p = _path.data();
227 4           size_t start = 0;
228 34 100         for (size_t i = 0; i < plen; ++i) {
229 30 100         if (p[i] != '/') continue;
230 8 100         if (i == start) { start++; continue; }
231 4 50         ret.push_back(decode_uri_component(string_view(p+start, i-start)));
    50          
232 4           start = i+1;
233             }
234 4 100         if (p[plen-1] != '/') ret.push_back(string(p+start, plen-start));
    50          
    50          
235 4           return ret;
236             }
237              
238 0           void URI::swap (URI& uri) {
239 0           std::swap(_scheme, uri._scheme);
240 0           std::swap(scheme_info, uri.scheme_info);
241 0           std::swap(_user_info, uri._user_info);
242 0           std::swap(_host, uri._host);
243 0           std::swap(_port, uri._port);
244 0           std::swap(_path, uri._path);
245 0           std::swap(_qstr, uri._qstr);
246 0           std::swap(_query, uri._query);
247 0           std::swap(_qrev, uri._qrev);
248 0           std::swap(_fragment, uri._fragment);
249 0           std::swap(_flags, uri._flags);
250 0           }
251              
252 153           void URI::sync_scheme_info () {
253 153 100         if (!_scheme) {
254 25           scheme_info = NULL;
255 153           return;
256             }
257              
258 128           auto len = _scheme.length();
259 128 100         if (len >= 4 && (_scheme[0]|0x20) == 'h' && (_scheme[1]|0x20) == 't' && (_scheme[2]|0x20) == 't' && (_scheme[3]|0x20) == 'p') {
    50          
    100          
    50          
    50          
    50          
    50          
    50          
    50          
    100          
    100          
    100          
    100          
    100          
    0          
    0          
    0          
    0          
260 86 100         if (len == 4) {
261 58           scheme_info = http_si;
262 58 50         _scheme = "http";
263             }
264 28 50         else if (len == 5 && (_scheme[4]|0x20) == 's') {
    50          
    50          
    50          
    50          
    0          
265 28           scheme_info = https_si;
266 28 50         _scheme = "https";
267 28           return;
268             }
269 58           return;
270             }
271              
272             // lowercase the scheme
273 42 50         char* p = _scheme.buf();
274 42           char* end = p + _scheme.length();
275 202 100         for (;p != end; ++p) *p = tolower(*p);
276              
277 42 50         auto it = scheme_map.find(_scheme);
278 42 100         if (it == scheme_map.cend()) scheme_info = NULL;
279 42           else scheme_info = &it->second;
280             }
281              
282 0           URI::SchemeInfo* URI::get_scheme_info (const std::type_info* ti) {
283 0 0         auto it = scheme_ti_map.find(ti);
284 0 0         return it == scheme_ti_map.end() ? nullptr : it->second;
285             }
286              
287 0           string URI::user () const {
288 0           size_t delim = _user_info.find(':');
289 0 0         if (delim == string::npos) return _user_info;
290 0           return _user_info.substr(0, delim);
291             }
292              
293 0           void URI::user (const string& user) {
294 0           size_t delim = _user_info.find(':');
295 0 0         if (delim == string::npos) _user_info = user;
296 0           else _user_info.replace(0, delim, user);
297 0           }
298              
299 0           string URI::password () const {
300 0           size_t delim = _user_info.find(':');
301 0 0         if (delim == string::npos) return string();
302 0           return _user_info.substr(delim+1);
303             }
304              
305 0           void URI::password (const string& password) {
306 0           size_t delim = _user_info.find(':');
307 0 0         if (delim == string::npos) {
308 0           _user_info += ':';
309 0           _user_info += password;
310             }
311 0           else _user_info.replace(delim+1, string::npos, password);
312 0           }
313              
314 0           std::ostream& operator<< (std::ostream& os, const URI& uri) {
315 0 0         string tmp = uri.to_string();
316 0 0         return os.write(tmp.data(), tmp.length());
317             }
318              
319 52 50         }}
    50