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