forked from rofafor/vdr-plugin-iptv
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathuri_parser.h
217 lines (178 loc) · 6.14 KB
/
uri_parser.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#pragma once
#ifndef SIMPLE_URI_PARSER_LIBRARY_H
#define SIMPLE_URI_PARSER_LIBRARY_H
#include <string>
#include <unordered_map>
#include <algorithm>
#ifndef simple_uri_CPLUSPLUS
# if defined(_MSVC_LANG ) && !defined(__clang__)
# define simple_uri_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
# else
# define simple_uri_CPLUSPLUS __cplusplus
# endif
#endif
#define simple_uri_CPP17_OR_GREATER ( simple_uri_CPLUSPLUS >= 201703L )
namespace uri {
#if simple_uri_CPP17_OR_GREATER
using string_view_type = std::string_view;
using string_arg_type = std::string_view;
constexpr auto npos = std::string_view::npos;
#else
using string_view_type = std::string;
using string_arg_type = const std::string&;
constexpr auto npos = std::string::npos;
#endif
using query_type = std::unordered_map<std::string, std::string>;
enum class Error {
None,
InvalidScheme,
InvalidPort,
};
struct Authority {
std::string authority;
std::string userinfo;
std::string host;
long port = 0;
};
struct Uri {
Error error;
std::string scheme;
Authority authority = {};
std::string path;
query_type query = {};
std::string query_string;
std::string fragment;
explicit Uri(Error error) : error(error) {}
Uri(std::string scheme, Authority authority, std::string path, query_type query, std::string query_string, std::string fragment)
: error(Error::None)
, scheme(std::move(scheme))
, authority(std::move(authority))
, path(std::move(path))
, query(std::move(query))
, query_string(std::move(query_string))
, fragment(std::move(fragment))
{}
};
}
namespace {
bool valid_scheme(uri::string_arg_type scheme) {
if (scheme.empty()) {
return false;
}
auto pos = std::find_if_not(scheme.begin(), scheme.end(), [&](char c){
return std::isalnum(c) || c == '+' || c == '.' || c == '-';
});
return pos == scheme.end();
}
std::tuple<std::string, uri::Error, uri::string_view_type> parse_scheme(uri::string_arg_type uri) {
auto pos = uri.find(':');
if (pos == uri::npos) {
return { "", uri::Error::InvalidScheme, uri };
}
auto scheme = uri.substr(0, pos);
if (!::valid_scheme(scheme)) {
return { "", uri::Error::InvalidScheme, uri };
}
std::string scheme_string{ scheme };
std::transform(scheme_string.begin(), scheme_string.end(), scheme_string.begin(),
[](unsigned char c){ return std::tolower(c); });
return { scheme_string, uri::Error::None, uri.substr(pos + 1) };
}
std::tuple<uri::Authority, uri::Error, uri::string_view_type> parse_authority(uri::string_arg_type uri) {
uri::Authority authority;
bool has_authority = uri.length() >= 2 && uri[0] == '/' && uri[1] == '/';
if (!has_authority) {
return { authority, uri::Error::None, uri };
}
auto pos = uri.substr(2).find('/');
auto auth_string = uri.substr(2, pos);
auto rem = uri.substr(pos + 2);
authority.authority = auth_string;
pos = auth_string.find('@');
if (pos != uri::npos) {
authority.userinfo = std::string(auth_string.substr(0, pos));
auth_string = auth_string.substr(pos + 1);
}
char* end_ptr = nullptr;
if (!auth_string.empty() && auth_string[0] != '[') {
pos = auth_string.find(':');
if (pos != uri::npos) {
authority.port = std::strtol(&auth_string[pos + 1], &end_ptr, 10);
if (end_ptr != &*auth_string.end()) {
return { authority, uri::Error::InvalidPort, auth_string };
}
}
}
authority.host = auth_string.substr(0, pos);
return { authority, uri::Error::None, rem };
}
std::tuple<std::string, uri::Error, uri::string_view_type> parse_path(uri::string_arg_type uri) {
auto pos = uri.find_first_of("#?");
if (pos == uri::npos) {
auto path = std::string(uri);
return { path, uri::Error::None, "" };
} else {
auto path = std::string(uri.substr(0, pos));
return { path, uri::Error::None, uri.substr(pos + 1) };
}
}
std::tuple<uri::query_type, std::string, uri::Error, uri::string_view_type> parse_query(uri::string_arg_type uri) {
auto hash_pos = uri.find('#');
auto query_substring = uri.substr(0, hash_pos);
auto query_string = std::string(query_substring);
uri::query_type query;
while (!query_substring.empty()) {
auto delim_pos = query_substring.find_first_of("&;?", 0);
auto arg = query_substring.substr(0, delim_pos);
auto equals_pos = arg.find('=');
if (equals_pos == uri::npos) {
query[std::string(arg)] = "";
} else {
query[std::string(arg.substr(0, equals_pos))] = arg.substr(equals_pos + 1);
}
if (delim_pos == uri::npos) {
query_substring = "";
} else {
query_substring = query_substring.substr(delim_pos + 1);
}
}
return {query, query_string, uri::Error::None, uri.substr(hash_pos + 1) };
}
std::tuple<std::string, uri::Error, uri::string_view_type> parse_fragment(uri::string_arg_type uri) {
return { std::string(uri), uri::Error::None, uri };
}
} // anon namespace
namespace uri {
inline Uri parse_uri(uri::string_arg_type uri_in) {
uri::Error error;
string_view_type uri;
std::string scheme;
std::tie(scheme, error, uri) = ::parse_scheme(uri_in);
if (error != Error::None) {
return Uri(error);
}
Authority authority;
std::tie(authority, error, uri) = ::parse_authority(uri);
if (error != Error::None) {
return Uri(error);
}
std::string path;
std::tie(path, error, uri) = ::parse_path(uri);
if (error != Error::None) {
return Uri(error);
}
query_type query;
std::string query_string;
std::tie(query, query_string, error, uri) = ::parse_query(uri);
if (error != Error::None) {
return Uri(error);
}
std::string fragment;
std::tie(fragment, error, uri) = ::parse_fragment(uri);
if (error != Error::None) {
return Uri(error);
}
return Uri(scheme, authority, path, query, query_string, fragment);
}
} // namespace uri
#endif // SIMPLE_URI_PARSER_LIBRARY_H