HTTP v1.13.1
Loading...
Searching...
No Matches
HTTPUtility.cpp
Go to the documentation of this file.
1#include "HTTPUtility.h"
2
3#include <iostream>
4#include <algorithm>
5#include <optional>
6#include <charconv>
7#include <array>
8#include <cassert>
9
10#include "HTTPParseException.h"
11#include "HTTPParser.h"
12
13using namespace std;
14
15static optional<string_view> encodeSymbol(char symbol);
16
17static optional<char> decodeSymbol(string_view symbol);
18
19template<typename T>
21{
22 constexpr void convert(string_view data, T& result)
23 {
24 static_assert(false, "Wrong type");
25 }
26};
27
28template<>
29struct Converter<string>
30{
31 void convert(string_view data, string& result)
32 {
33 result = data;
34 }
35};
36
37template<>
38struct Converter<optional<string>>
39{
40 void convert(string_view data, optional<string>& result)
41 {
42 result = data;
43 }
44};
45
46template<typename... Args>
48{
49private:
50 array<size_t, sizeof...(Args)> offsets;
51 array<char, sizeof...(Args)> nextCharacter;
52
53private:
54 template<size_t Index>
55 constexpr auto& getValue(Args&... args) const
56 {
57 return get<Index>(forward_as_tuple(args...));
58 }
59
60 template<size_t Index = 0>
61 constexpr void parseValue(string_view data, size_t offset, Args&... args) const
62 {
63 auto& value = this->getValue<Index>(args...);
64 Converter<remove_reference_t<decltype(value)>> converter;
65 size_t stringValueIndex = data.find(nextCharacter[Index], offset + offsets[Index]);
66 string_view stringValue(data.begin() + offset + offsets[Index], (stringValueIndex == string_view::npos) ? data.end() : data.begin() + stringValueIndex);
67
68 converter.convert(stringValue, value);
69
70 if constexpr (Index + 1 != sizeof...(Args))
71 {
72 this->parseValue<Index + 1>(data, offset + stringValue.size(), args...);
73 }
74 }
75
76public:
77 constexpr MultipartParser(string_view format)
78 {
79 size_t offset = format.find("{}");
80 size_t index = 0;
81
82 while (offset != string_view::npos)
83 {
84#ifndef __LINUX__
85#pragma warning(push)
86#pragma warning(disable: 28020)
87#endif
88 offsets[index] = offset - 2 * index;
89#ifndef __LINUX__
90#pragma warning(pop)
91#endif
92 format.size() > offset + 2 ?
93 nextCharacter[index] = format[offset + 2] :
94 nextCharacter[index] = '\0';
95
96 offset = format.find("{}", offset + 1);
97
98 index++;
99 }
100 }
101
102 void getValues(string_view data, Args&... args) const
103 {
104 this->parseValue(data, 0, args...);
105 }
106};
107
108namespace web
109{
111 {
112 string version = "1.13.1";
113
114 return version;
115 }
116
117 string encodeUrl(string_view data)
118 {
119 string result;
120
121 for (char symbol : data)
122 {
123 if (optional<string_view> encodedSymbol = encodeSymbol(symbol); encodedSymbol)
124 {
125 result += *encodedSymbol;
126 }
127 else
128 {
129 result += symbol;
130 }
131 }
132
133 return result;
134 }
135
136 string decodeUrl(string_view data)
137 {
138 static constexpr size_t encodedSymbolSize = 3;
139
140 string result;
141
142 for (size_t i = 0; i < data.size(); i++)
143 {
144 if (data[i] == '%')
145 {
146 string_view percentEncodedData(data.data() + i, encodedSymbolSize);
147
148 if (optional<char> encodedSymbol = decodeSymbol(percentEncodedData); encodedSymbol)
149 {
150 result += *encodedSymbol;
151 }
152 else
153 {
154 cerr << "Unknown encoded symbol: " << percentEncodedData << endl;
155
156 result += percentEncodedData;
157 }
158
159 i += encodedSymbolSize - 1;
160 }
161 else
162 {
163 result += data[i];
164 }
165 }
166
167 return result;
168 }
169
170 size_t InsensitiveStringHash::operator () (const string& value) const
171 {
172 string tem;
173
174 tem.reserve(value.size());
175
176 for_each(value.begin(), value.end(), [&tem](char c) { tem += tolower(c); });
177
178 return hash<string>()(tem);
179 }
180
181 bool InsensitiveStringEqual::operator () (const string& left, const string& right) const
182 {
183 return equal
184 (
185 left.begin(), left.end(),
186 right.begin(), right.end(),
187 [](char first, char second) { return tolower(first) == tolower(second); }
188 );
189 }
190
191 Multipart::Multipart(string_view data)
192 {
193 if (data.starts_with(HTTPParser::crlf))
194 {
195 data = string_view(data.begin() + HTTPParser::crlf.size(), data.end());
196 }
197
198 size_t firstStringEnd = data.find(HTTPParser::crlf);
199
200 if (data.find("filename") != string_view::npos)
201 {
202 constexpr MultipartParser<string, optional<string>> parser(R"(Content-Disposition: form-data; name="{}"; filename="{}")");
203
204 parser.getValues(data.substr(0, firstStringEnd), name, fileName);
205 }
206 else
207 {
208 constexpr MultipartParser<string> parser(R"(Content-Disposition: form-data; name="{}")");
209
210 parser.getValues(data.substr(0, firstStringEnd), name);
211 }
212
213 if (data.find("Content-Type:") != string_view::npos)
214 {
215 constexpr MultipartParser<optional<string>> parser("Content-Type: {}");
216
217 parser.getValues
218 (
219 string_view
220 (
221 data.begin() + firstStringEnd + HTTPParser::crlf.size(),
222 data.begin() + data.find(HTTPParser::crlf, firstStringEnd + HTTPParser::crlf.size())
223 ),
224 contentType
225 );
226 }
227
228 this->data = string(data.begin() + data.find(HTTPParser::crlfcrlf) + HTTPParser::crlfcrlf.size(), data.end() - HTTPParser::crlf.size());
229 }
230
231 Multipart::Multipart(string_view name, const optional<std::string>& fileName, const optional<std::string>& contentType, string&& data) :
232 name(name),
233 fileName(fileName),
234 contentType(contentType),
235 data(move(data))
236 {
237
238 }
239
240 const string& Multipart::getName() const
241 {
242 return name;
243 }
244
245 const optional<string>& Multipart::getFileName() const
246 {
247 return fileName;
248 }
249
250 const optional<string>& Multipart::getContentType() const
251 {
252 return contentType;
253 }
254
255 const string& Multipart::getData() const
256 {
257 return data;
258 }
259
260 string __getMessageFromCode(int code)
261 {
262 static const unordered_map<ResponseCodes, string> responseMessage =
263 {
264 { ResponseCodes::Continue, "Continue" },
265 { ResponseCodes::switchingProtocols, "Switching Protocols" },
266 { ResponseCodes::processing, "Processing" },
267 { ResponseCodes::ok, "OK" },
268 { ResponseCodes::created, "Created" },
269 { ResponseCodes::accepted, "Accepted" },
270 { ResponseCodes::nonAuthoritativeInformation, "Non-Authoritative Information" },
271 { ResponseCodes::noContent, "No Content" },
272 { ResponseCodes::resetContent, "Reset Content" },
273 { ResponseCodes::partialContent, "Partial Content" },
274 { ResponseCodes::multiStatus, "Multi-Status" },
275 { ResponseCodes::alreadyReported, "Already Reported" },
276 { ResponseCodes::IMUsed, "IM Used" },
277 { ResponseCodes::multipleChoices, "Multiple Choices" },
278 { ResponseCodes::movedPermanently, "Moved Permanently" },
279 { ResponseCodes::found, "Found" },
280 { ResponseCodes::seeOther, "See Other" },
281 { ResponseCodes::notModified, "Not Modified" },
282 { ResponseCodes::useProxy, "Use Proxy" },
283 { ResponseCodes::temporaryRedirect, "Temporary Redirect" },
284 { ResponseCodes::permanentRedirect, "Permanent Redirect" },
285 { ResponseCodes::badRequest, "Bad Request" },
286 { ResponseCodes::unauthorized, "Unauthorized" },
287 { ResponseCodes::paymentRequired, "Payment Required" },
288 { ResponseCodes::forbidden, "Forbidden" },
289 { ResponseCodes::notFound, "Not Found" },
290 { ResponseCodes::methodNotAllowed, "Method Not Allowed" },
291 { ResponseCodes::notAcceptable, "Not Acceptable" },
292 { ResponseCodes::proxyAuthenticationRequired, "Proxy Authentication Required" },
293 { ResponseCodes::requestTimeout, "Request Timeout" },
294 { ResponseCodes::conflict, "Conflict" },
295 { ResponseCodes::gone, "Gone" },
296 { ResponseCodes::lengthRequired, "Length Required" },
297 { ResponseCodes::preconditionFailed, "Precondition Failed" },
298 { ResponseCodes::payloadTooLarge, "Payload Too Large" },
299 { ResponseCodes::URITooLong, "URI Tool Long" },
300 { ResponseCodes::unsupportedMediaType, "Unsupported Media Type" },
301 { ResponseCodes::rangeNotSatisfiable, "Range Not Satisfiable" },
302 { ResponseCodes::expectationFailed, "Expectation Failed" },
303 { ResponseCodes::iamATeapot, "I am teapot" },
304 { ResponseCodes::authenticationTimeout, "Authentication Timeout" },
305 { ResponseCodes::misdirectedRequest, "Misdirected Request" },
306 { ResponseCodes::unprocessableEntity, "Unprocessable Entity" },
307 { ResponseCodes::locked, "Locked" },
308 { ResponseCodes::failedDependency, "Failed Dependency" },
309 { ResponseCodes::upgradeRequired, "Upgrade Required" },
310 { ResponseCodes::preconditionRequired, "Precondition Required" },
311 { ResponseCodes::tooManyRequests, "Too Many Requests" },
312 { ResponseCodes::requestHeaderFieldsTooLarge, "Request Header Fields Too Large" },
313 { ResponseCodes::retryWith, "Retry With" },
314 { ResponseCodes::unavailableForLegalReasons, "Unavailable For Legal Reasons" },
315 { ResponseCodes::clientClosedRequest, "Client Closed Request" },
316 { ResponseCodes::internalServerError, "Internal Server Error" },
317 { ResponseCodes::notImplemented, "Not Implemented" },
318 { ResponseCodes::badGateway, "Bad Gateway" },
319 { ResponseCodes::serviceUnavailable, "Service Unavailable" },
320 { ResponseCodes::gatewayTimeout, "Gateway Timeout" },
321 { ResponseCodes::HTTPVersionNotSupported, "HTTP Version Not Supported" },
322 { ResponseCodes::variantAlsoNegotiates, "Variant Also Negotiates" },
323 { ResponseCodes::insufficientStorage, "Insufficient Storage" },
324 { ResponseCodes::loopDetected, "Loop Detected" },
325 { ResponseCodes::bandwidthLimitExceeded, "Bandwidth Limit Exceeded" },
326 { ResponseCodes::notExtended, "Not Extended" },
327 { ResponseCodes::networkAuthenticationRequired, "Network Authentication Required" },
328 { ResponseCodes::unknownError, "Unknown Error" },
329 { ResponseCodes::webServerIsDown, "Web Server Is Down" },
330 { ResponseCodes::connectionTimedOut, "Connection Timed Out" },
331 { ResponseCodes::originIsUnreachable, "Origin Is Unreachable" },
332 { ResponseCodes::aTimeoutOccurred, "A Timeout Occurred" },
333 { ResponseCodes::SSLHandshakeFailed, "SSL Handshake Failed" },
334 { ResponseCodes::invalidSSLCertificate, "Invalid SSL Certificate" }
335 };
336
337 if (auto it = responseMessage.find(static_cast<ResponseCodes>(code)); it != responseMessage.end())
338 {
339 return it->second;
340 }
341
342 return "Unknown response code";
343 }
344}
345
346optional<string_view> encodeSymbol(char symbol)
347{
348 switch (symbol)
349 {
350 case ' ':
351 return "%20";
352
353 case '!':
354 return "%21";
355
356 case '\"':
357 return "%22";
358
359 case '#':
360 return "%23";
361
362 case '$':
363 return "%24";
364
365 case '%':
366 return "%25";
367
368 case '&':
369 return "%26";
370
371 case '\'':
372 return "%27";
373
374 case '(':
375 return "%28";
376
377 case ')':
378 return "%29";
379
380 case '*':
381 return "%2A";
382
383 case '+':
384 return "%2B";
385
386 case ',':
387 return "%2C";
388
389 case '/':
390 return "%2F";
391
392 case ':':
393 return "%3A";
394
395 case ';':
396 return "%3B";
397
398 case '=':
399 return "%3D";
400
401 case '?':
402 return "%3F";
403
404 case '@':
405 return "%40";
406
407 case '[':
408 return "%5B";
409
410 case ']':
411 return "%5D";
412
413 default:
414 return optional<string_view>();
415 }
416}
417
418optional<char> decodeSymbol(string_view symbol)
419{
420 static const unordered_map<string_view, char> symbols =
421 {
422 { "%20", ' ' },
423 { "%21", '!' },
424 { "%22", '\"' },
425 { "%23", '#' },
426 { "%24", '$' },
427 { "%25", '%' },
428 { "%26", '&' },
429 { "%27", '\'' },
430 { "%28", '(' },
431 { "%29", ')' },
432 { "%2A", '*' },
433 { "%2B", '+' },
434 { "%2C", ',' },
435 { "%2F", '/' },
436 { "%3A", ':' },
437 { "%3B", ';' },
438 { "%3D", '=' },
439 { "%3F", '?' },
440 { "%40", '@' },
441 { "%5B", '[' },
442 { "%5D", ']' }
443 };
444
445 auto it = symbols.find(symbol);
446
447 return it != symbols.end() ? it->second : optional<char>();
448}
constexpr MultipartParser(string_view format)
void getValues(string_view data, Args &... args) const
static constexpr std::string_view crlfcrlf
Definition HTTPParser.h:30
static constexpr std::string_view crlf
Definition HTTPParser.h:31
const std::string & getName() const
const std::optional< std::string > & getFileName() const
const std::string & getData() const
Multipart(std::string_view data)
const std::optional< std::string > & getContentType() const
ResponseCodes
Response codes.
Definition HTTPUtility.h:25
string __getMessageFromCode(int code)
string getHTTPLibraryVersion()
Get version of HTTP library.
string decodeUrl(string_view data)
string encodeUrl(string_view data)
void convert(string_view data, optional< string > &result)
void convert(string_view data, string &result)
constexpr void convert(string_view data, T &result)
bool operator()(const std::string &left, const std::string &right) const
size_t operator()(const std::string &value) const