CppServer 1.0.6.0
C++ Server Library
Loading...
Searching...
No Matches
http_request.cpp
Go to the documentation of this file.
1
8
10
11#include "string/string_utils.h"
12#include "utility/countof.h"
13
14#include <cassert>
15
16namespace CppServer {
17namespace HTTP {
18
19std::tuple<std::string_view, std::string_view> HTTPRequest::header(size_t i) const noexcept
20{
21 assert((i < _headers.size()) && "Index out of bounds!");
22 if (i >= _headers.size())
23 return std::make_tuple(std::string_view(), std::string_view());
24
25 auto item = _headers[i];
26
27 return std::make_tuple(std::string_view(_cache.data() + std::get<0>(item), std::get<1>(item)), std::string_view(_cache.data() + std::get<2>(item), std::get<3>(item)));
28}
29
30std::tuple<std::string_view, std::string_view> HTTPRequest::cookie(size_t i) const noexcept
31{
32 assert((i < _cookies.size()) && "Index out of bounds!");
33 if (i >= _cookies.size())
34 return std::make_tuple(std::string_view(), std::string_view());
35
36 auto item = _cookies[i];
37
38 return std::make_tuple(std::string_view(_cache.data() + std::get<0>(item), std::get<1>(item)), std::string_view(_cache.data() + std::get<2>(item), std::get<3>(item)));
39}
40
42{
43 _error = false;
44 _method_index = 0;
45 _method_size = 0;
46 _url_index = 0;
47 _url_size = 0;
48 _protocol_index = 0;
49 _protocol_size = 0;
50 _headers.clear();
51 _cookies.clear();
52 _body_index = 0;
53 _body_size = 0;
54 _body_length = 0;
55 _body_length_provided = false;
56
57 _cache.clear();
58 _cache_size = 0;
59 return *this;
60}
61
62HTTPRequest& HTTPRequest::SetBegin(std::string_view method, std::string_view url, std::string_view protocol)
63{
64 // Clear the HTTP request cache
65 Clear();
66
67 size_t index = 0;
68
69 // Append the HTTP request method
70 _cache.append(method);
71 _method_index = index;
72 _method_size = method.size();
73
74 _cache.append(" ");
75 index = _cache.size();
76
77 // Append the HTTP request URL
78 _cache.append(url);
79 _url_index = index;
80 _url_size = url.size();
81
82 _cache.append(" ");
83 index = _cache.size();
84
85 // Append the HTTP request protocol version
86 _cache.append(protocol);
87 _protocol_index = index;
88 _protocol_size = protocol.size();
89
90 _cache.append("\r\n");
91 return *this;
92}
93
94HTTPRequest& HTTPRequest::SetHeader(std::string_view key, std::string_view value)
95{
96 size_t index = _cache.size();
97
98 // Append the HTTP request header's key
99 _cache.append(key);
100 size_t key_index = index;
101 size_t key_size = key.size();
102
103 _cache.append(": ");
104 index = _cache.size();
105
106 // Append the HTTP request header's value
107 _cache.append(value);
108 size_t value_index = index;
109 size_t value_size = value.size();
110
111 _cache.append("\r\n");
112
113 // Add the header to the corresponding collection
114 _headers.emplace_back(key_index, key_size, value_index, value_size);
115 return *this;
116}
117
118HTTPRequest& HTTPRequest::SetCookie(std::string_view name, std::string_view value)
119{
120 size_t index = _cache.size();
121
122 // Append the HTTP request header's key
123 _cache.append("Cookie");
124 size_t key_index = index;
125 size_t key_size = 6;
126
127 _cache.append(": ");
128 index = _cache.size();
129
130 // Append the HTTP request header's value
131 size_t value_index = index;
132
133 // Append Cookie
134 index = _cache.size();
135 _cache.append(name);
136 size_t name_index = index;
137 size_t name_size = name.size();
138 _cache.append("=");
139 index = _cache.size();
140 _cache.append(value);
141 size_t cookie_index = index;
142 size_t cookie_size = value.size();
143
144 size_t value_size = _cache.size() - value_index;
145
146 _cache.append("\r\n");
147
148 // Add the header to the corresponding collection
149 _headers.emplace_back(key_index, key_size, value_index, value_size);
150 // Add the cookie to the corresponding collection
151 _cookies.emplace_back(name_index, name_size, cookie_index, cookie_size);
152 return *this;
153}
154
155HTTPRequest& HTTPRequest::AddCookie(std::string_view name, std::string_view value)
156{
157 // Append Cookie
158 _cache.append("; ");
159 size_t index = _cache.size();
160 _cache.append(name);
161 size_t name_index = index;
162 size_t name_size = name.size();
163 _cache.append("=");
164 index = _cache.size();
165 _cache.append(value);
166 size_t cookie_index = index;
167 size_t cookie_size = value.size();
168
169 // Add the cookie to the corresponding collection
170 _cookies.emplace_back(name_index, name_size, cookie_index, cookie_size);
171 return *this;
172}
173
175{
176 // Append content length header
177 char buffer[32];
178 SetHeader("Content-Length", FastConvert(body.size(), buffer, CppCommon::countof(buffer)));
179
180 _cache.append("\r\n");
181
182 size_t index = _cache.size();
183
184 // Append the HTTP request body
185 _cache.append(body);
186 _body_index = index;
187 _body_size = body.size();
188 _body_length = body.size();
189 _body_length_provided = true;
190 return *this;
191}
192
194{
195 // Append content length header
196 char buffer[32];
197 SetHeader("Content-Length", FastConvert(length, buffer, CppCommon::countof(buffer)));
198
199 _cache.append("\r\n");
200
201 size_t index = _cache.size();
202
203 // Clear the HTTP request body
204 _body_index = index;
205 _body_size = 0;
206 _body_length = length;
207 _body_length_provided = true;
208 return *this;
209}
210
212{
213 Clear();
214 SetBegin("HEAD", url);
215 SetBody();
216 return *this;
217}
218
220{
221 Clear();
222 SetBegin("GET", url);
223 SetBody();
224 return *this;
225}
226
227HTTPRequest& HTTPRequest::MakeQueryRequest(std::string_view url, std::string_view content, std::string_view content_type)
228{
229 Clear();
230 SetBegin("QUERY", url);
231 if (!content_type.empty())
232 SetHeader("Content-Type", content_type);
233 SetBody(content);
234 return *this;
235}
236
237HTTPRequest& HTTPRequest::MakePostRequest(std::string_view url, std::string_view content, std::string_view content_type)
238{
239 Clear();
240 SetBegin("POST", url);
241 if (!content_type.empty())
242 SetHeader("Content-Type", content_type);
243 SetBody(content);
244 return *this;
245}
246
247HTTPRequest& HTTPRequest::MakePutRequest(std::string_view url, std::string_view content, std::string_view content_type)
248{
249 Clear();
250 SetBegin("PUT", url);
251 if (!content_type.empty())
252 SetHeader("Content-Type", content_type);
253 SetBody(content);
254 return *this;
255}
256
258{
259 Clear();
260 SetBegin("DELETE", url);
261 SetBody();
262 return *this;
263}
264
266{
267 Clear();
268 SetBegin("OPTIONS", url);
269 SetBody();
270 return *this;
271}
272
274{
275 Clear();
276 SetBegin("TRACE", url);
277 SetBody();
278 return *this;
279}
280
281bool HTTPRequest::IsPendingHeader() const
282{
283 return (!_error && (_body_index == 0));
284}
285
286bool HTTPRequest::IsPendingBody() const
287{
288 return (!_error && (_body_index > 0) && (_body_size > 0));
289}
290
291bool HTTPRequest::ReceiveHeader(const void* buffer, size_t size)
292{
293 // Update the request cache
294 _cache.insert(_cache.end(), (const char*)buffer, (const char*)buffer + size);
295
296 // Try to seek for HTTP header separator
297 for (size_t i = _cache_size; i < _cache.size(); ++i)
298 {
299 // Check for the request cache out of bounds
300 if ((i + 3) >= _cache.size())
301 break;
302
303 // Check for the header separator
304 if ((_cache[i + 0] == '\r') && (_cache[i + 1] == '\n') && (_cache[i + 2] == '\r') && (_cache[i + 3] == '\n'))
305 {
306 size_t index = 0;
307
308 // Set the error flag for a while...
309 _error = true;
310
311 // Parse method
312 _method_index = index;
313 _method_size = 0;
314 while (_cache[index] != ' ')
315 {
316 ++_method_size;
317 ++index;
318 if (index >= _cache.size())
319 return false;
320 }
321 ++index;
322 if (index >= _cache.size())
323 return false;
324
325 // Parse URL
326 _url_index = index;
327 _url_size = 0;
328 while (_cache[index] != ' ')
329 {
330 ++_url_size;
331 ++index;
332 if (index >= _cache.size())
333 return false;
334 }
335 ++index;
336 if (index >= _cache.size())
337 return false;
338
339 // Parse protocol version
340 _protocol_index = index;
341 _protocol_size = 0;
342 while (_cache[index] != '\r')
343 {
344 ++_protocol_size;
345 ++index;
346 if (index >= _cache.size())
347 return false;
348 }
349 ++index;
350 if ((index >= _cache.size()) || (_cache[index] != '\n'))
351 return false;
352 ++index;
353 if (index >= _cache.size())
354 return false;
355
356 // Parse headers
357 while ((index < _cache.size()) && (index < i))
358 {
359 // Parse header name
360 size_t header_name_index = index;
361 size_t header_name_size = 0;
362 while (_cache[index] != ':')
363 {
364 ++header_name_size;
365 ++index;
366 if (index >= i)
367 break;
368 if (index >= _cache.size())
369 return false;
370 }
371 ++index;
372 if (index >= i)
373 break;
374 if (index >= _cache.size())
375 return false;
376
377 // Skip all prefix space characters
378 while (std::isspace(_cache[index]))
379 {
380 ++index;
381 if (index >= i)
382 break;
383 if (index >= _cache.size())
384 return false;
385 }
386
387 // Parse header value
388 size_t header_value_index = index;
389 size_t header_value_size = 0;
390 while (_cache[index] != '\r')
391 {
392 ++header_value_size;
393 ++index;
394 if (index >= i)
395 break;
396 if (index >= _cache.size())
397 return false;
398 }
399 ++index;
400 if ((index >= _cache.size()) || (_cache[index] != '\n'))
401 return false;
402 ++index;
403 if (index >= _cache.size())
404 return false;
405
406 // Validate header name and value (sometimes value can be empty)
407 if (header_name_size == 0)
408 return false;
409
410 // Add a new header
411 _headers.emplace_back(header_name_index, header_name_size, header_value_index, header_value_size);
412
413 // Try to find the body content length
414 if (CppCommon::StringUtils::CompareNoCase(std::string_view(_cache.data() + header_name_index, header_name_size), "Content-Length"))
415 {
416 _body_length = 0;
417 for (size_t j = header_value_index; j < (header_value_index + header_value_size); ++j)
418 {
419 if ((_cache[j] < '0') || (_cache[j] > '9'))
420 return false;
421 _body_length *= 10;
422 _body_length += _cache[j] - '0';
423 _body_length_provided = true;
424 }
425 }
426
427 // Try to find Cookies
428 if (CppCommon::StringUtils::CompareNoCase(std::string_view(_cache.data() + header_name_index, header_name_size), "Cookie"))
429 {
430 bool name = true;
431 bool token = false;
432 size_t current = header_value_index;
433 size_t name_index = index;
434 size_t name_size = 0;
435 size_t cookie_index = index;
436 size_t cookie_size = 0;
437 for (size_t j = header_value_index; j < (header_value_index + header_value_size); ++j)
438 {
439 if (_cache[j] == ' ')
440 {
441 if (token)
442 {
443 if (name)
444 {
445 name_index = current;
446 name_size = j - current;
447 }
448 else
449 {
450 cookie_index = current;
451 cookie_size = j - current;
452 }
453 }
454 token = false;
455 continue;
456 }
457 if (_cache[j] == '=')
458 {
459 if (token)
460 {
461 if (name)
462 {
463 name_index = current;
464 name_size = j - current;
465 }
466 else
467 {
468 cookie_index = current;
469 cookie_size = j - current;
470 }
471 }
472 token = false;
473 name = false;
474 continue;
475 }
476 if (_cache[j] == ';')
477 {
478 if (token)
479 {
480 if (name)
481 {
482 name_index = current;
483 name_size = j - current;
484 }
485 else
486 {
487 cookie_index = current;
488 cookie_size = j - current;
489 }
490
491 // Validate the cookie
492 if ((name_size > 0) && (cookie_size > 0))
493 {
494 // Add the cookie to the corresponding collection
495 _cookies.emplace_back(name_index, name_size, cookie_index, cookie_size);
496
497 // Resset the current cookie values
498 name_index = j;
499 name_size = 0;
500 cookie_index = j;
501 cookie_size = 0;
502 }
503 }
504 token = false;
505 name = true;
506 continue;
507 }
508 if (!token)
509 {
510 current = j;
511 token = true;
512 }
513 }
514
515 // Process the last cookie
516 if (token)
517 {
518 if (name)
519 {
520 name_index = current;
521 name_size = header_value_index + header_value_size - current;
522 }
523 else
524 {
525 cookie_index = current;
526 cookie_size = header_value_index + header_value_size - current;
527 }
528
529 // Validate the cookie
530 if ((name_size > 0) && (cookie_size > 0))
531 {
532 // Add the cookie to the corresponding collection
533 _cookies.emplace_back(name_index, name_size, cookie_index, cookie_size);
534 }
535 }
536 }
537 }
538
539 // Reset the error flag
540 _error = false;
541
542 // Update the body index and size
543 _body_index = i + 4;
544 _body_size = _cache.size() - i - 4;
545
546 // Update the parsed cache size
547 _cache_size = _cache.size();
548
549 return true;
550 }
551 }
552
553 // Update the parsed cache size
554 _cache_size = (_cache.size() >= 3) ? (_cache.size() - 3) : 0;
555
556 return false;
557}
558
559bool HTTPRequest::ReceiveBody(const void* buffer, size_t size)
560{
561 // Update HTTP request cache
562 _cache.insert(_cache.end(), (const char*)buffer, (const char*)buffer + size);
563
564 // Update the parsed cache size
565 _cache_size = _cache.size();
566
567 // Update body size
568 _body_size += size;
569
570 // Check if the body length was provided
571 if (_body_length_provided)
572 {
573 // Was the body fully received?
574 if (_body_size >= _body_length)
575 {
576 _body_size = _body_length;
577 return true;
578 }
579 }
580 else
581 {
582 // HEAD/GET/DELETE/OPTIONS/TRACE request might have no body
583 if ((method() == "HEAD") || (method() == "GET") || (method() == "QUERY") || (method() == "DELETE") || (method() == "OPTIONS") || (method() == "TRACE"))
584 {
585 _body_length = 0;
586 _body_size = 0;
587 return true;
588 }
589
590 // Check the body content to find the request body end
591 if (_body_size >= 4)
592 {
593 size_t index = _body_index + _body_size - 4;
594
595 // Was the body fully received?
596 if ((_cache[index + 0] == '\r') && (_cache[index + 1] == '\n') && (_cache[index + 2] == '\r') && (_cache[index + 3] == '\n'))
597 {
598 _body_length = _body_size;
599 return true;
600 }
601 }
602 }
603
604 // Body was received partially...
605 return false;
606}
607
608std::string_view HTTPRequest::FastConvert(size_t value, char* buffer, size_t size)
609{
610 size_t index = size;
611 do
612 {
613 buffer[--index] = '0' + (value % 10);
614 value /= 10;
615 }
616 while (value > 0);
617 return std::string_view(buffer + index, size - index);
618}
619
620std::ostream& operator<<(std::ostream& os, const HTTPRequest& request)
621{
622 os << "Request method: " << request.method() << std::endl;
623 os << "Request URL: " << request.url() << std::endl;
624 os << "Request protocol: " << request.protocol() << std::endl;
625 os << "Request headers: " << request.headers() << std::endl;
626 for (size_t i = 0; i < request.headers(); ++i)
627 {
628 auto header = request.header(i);
629 os << std::get<0>(header) << ": " << std::get<1>(header) << std::endl;
630 }
631 os << "Request body:" << request.body_length() << std::endl;
632 os << request.body() << std::endl;
633 return os;
634}
635
636void HTTPRequest::swap(HTTPRequest& request) noexcept
637{
638 using std::swap;
639 swap(_error, request._error);
640 swap(_method_index, request._method_index);
641 swap(_method_size, request._method_size);
642 swap(_url_index, request._url_index);
643 swap(_url_size, request._url_size);
644 swap(_protocol_index, request._protocol_index);
645 swap(_protocol_size, request._protocol_size);
646 swap(_headers, request._headers);
647 swap(_cookies, request._cookies);
648 swap(_body_index, request._body_index);
649 swap(_body_size, request._body_size);
650 swap(_body_length, request._body_length);
651 swap(_body_length_provided, request._body_length_provided);
652 swap(_cache, request._cache);
653 swap(_cache_size, request._cache_size);
654}
655
656} // namespace HTTP
657} // namespace CppServer
size_t body_length() const noexcept
Get the HTTP request body length.
HTTPRequest & SetBody(std::string_view body="")
Set the HTTP request body.
HTTPRequest & MakePutRequest(std::string_view url, std::string_view content, std::string_view content_type="text/plain; charset=UTF-8")
Make PUT request.
void swap(HTTPRequest &request) noexcept
Swap two instances.
HTTPRequest & MakeHeadRequest(std::string_view url)
Make HEAD request.
HTTPRequest & Clear()
Clear the HTTP request cache.
std::tuple< std::string_view, std::string_view > cookie(size_t i) const noexcept
Get the HTTP request cookie by index.
HTTPRequest & MakeQueryRequest(std::string_view url, std::string_view content, std::string_view content_type="text/plain; charset=UTF-8")
Make QUERY request.
HTTPRequest & MakeDeleteRequest(std::string_view url)
Make DELETE request.
HTTPRequest & MakePostRequest(std::string_view url, std::string_view content, std::string_view content_type="text/plain; charset=UTF-8")
Make POST request.
HTTPRequest & AddCookie(std::string_view name, std::string_view value)
Add the HTTP request cookie.
HTTPRequest & MakeOptionsRequest(std::string_view url)
Make OPTIONS request.
HTTPRequest & SetBodyLength(size_t length)
Set the HTTP request body length.
HTTPRequest & SetBegin(std::string_view method, std::string_view url, std::string_view protocol="HTTP/1.1")
Set the HTTP request begin with a given method, URL and protocol.
HTTPRequest()
Initialize an empty HTTP request.
HTTPRequest & MakeGetRequest(std::string_view url)
Make GET request.
size_t headers() const noexcept
Get the HTTP request headers count.
HTTPRequest & SetHeader(std::string_view key, std::string_view value)
Set the HTTP request header.
std::string_view method() const noexcept
Get the HTTP request method.
std::tuple< std::string_view, std::string_view > header(size_t i) const noexcept
Get the HTTP request header by index.
HTTPRequest & SetCookie(std::string_view name, std::string_view value)
Set the HTTP request cookie.
HTTPRequest & MakeTraceRequest(std::string_view url)
Make TRACE request.
std::string_view url() const noexcept
Get the HTTP request URL.
std::string_view body() const noexcept
Get the HTTP request body.
std::string_view protocol() const noexcept
Get the HTTP request protocol version.
HTTP request definition.
HTTP definitions.
std::ostream & operator<<(std::ostream &os, const HTTPRequest &request)
C++ Server project definitions.
Definition asio.h:56