11 #include "errors/exceptions.h"
12 #include "string/format.h"
13 #include "string/string_utils.h"
14 #include "utility/countof.h"
21 const std::unordered_map<std::string, std::string> HTTPResponse::_mime_table =
24 {
".html",
"text/html" },
25 {
".css",
"text/css" },
26 {
".js",
"text/javascript" },
27 {
".vue",
"text/html" },
28 {
".xml",
"text/xml" },
31 {
".atom",
"application/atom+xml" },
32 {
".fastsoap",
"application/fastsoap" },
33 {
".gzip",
"application/gzip" },
34 {
".json",
"application/json" },
35 {
".map",
"application/json" },
36 {
".pdf",
"application/pdf" },
37 {
".ps",
"application/postscript" },
38 {
".soap",
"application/soap+xml" },
39 {
".sql",
"application/sql" },
40 {
".xslt",
"application/xslt+xml" },
41 {
".zip",
"application/zip" },
42 {
".zlib",
"application/zlib" },
45 {
".aac",
"audio/aac" },
46 {
".ac3",
"audio/ac3" },
47 {
".mp3",
"audio/mpeg" },
48 {
".ogg",
"audio/ogg" },
51 {
".ttf",
"font/ttf" },
54 {
".bmp",
"image/bmp" },
55 {
".emf",
"image/emf" },
56 {
".gif",
"image/gif" },
57 {
".jpg",
"image/jpeg" },
58 {
".jpm",
"image/jpm" },
59 {
".jpx",
"image/jpx" },
60 {
".jrx",
"image/jrx" },
61 {
".png",
"image/png" },
62 {
".svg",
"image/svg+xml" },
63 {
".tiff",
"image/tiff" },
64 {
".wmf",
"image/wmf" },
67 {
".http",
"message/http" },
68 {
".s-http",
"message/s-http" },
71 {
".mesh",
"model/mesh" },
72 {
".vrml",
"model/vrml" },
75 {
".csv",
"text/csv" },
76 {
".plain",
"text/plain" },
77 {
".richtext",
"text/richtext" },
78 {
".rtf",
"text/rtf" },
79 {
".rtx",
"text/rtx" },
80 {
".sgml",
"text/sgml" },
81 {
".strings",
"text/strings" },
82 {
".url",
"text/uri-list" },
85 {
".H264",
"video/H264" },
86 {
".H265",
"video/H265" },
87 {
".mp4",
"video/mp4" },
88 {
".mpeg",
"video/mpeg" },
89 {
".raw",
"video/raw" }
94 assert((i < _headers.size()) &&
"Index out of bounds!");
95 if (i >= _headers.size())
96 return std::make_tuple(std::string_view(), std::string_view());
98 auto item = _headers[i];
100 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)));
107 _status_phrase_index = 0;
108 _status_phrase_size = 0;
115 _body_length_provided =
false;
136 case 203:
status_phrase =
"Non-Authoritative Information";
break;
162 case 407:
status_phrase =
"Proxy Authentication Required";
break;
183 case 431:
status_phrase =
"Request Header Fields Too Large";
break;
185 case 451:
status_phrase =
"Unavailable For Legal Reasons";
break;
192 case 505:
status_phrase =
"HTTP Version Not Supported";
break;
198 case 511:
status_phrase =
"Network Authentication Required";
break;
216 _protocol_index = index;
220 index = _cache.size();
224 _cache.append(FastConvert(
status, buffer, CppCommon::countof(buffer)));
228 index = _cache.size();
232 _status_phrase_index = index;
235 _cache.append(
"\r\n");
242 const auto& mime = _mime_table.find(std::string(extension));
243 if (mime != _mime_table.end())
244 return SetHeader(
"Content-Type", mime->second);
251 size_t index = _cache.size();
255 size_t key_index = index;
256 size_t key_size = key.size();
259 index = _cache.size();
262 _cache.append(value);
263 size_t value_index = index;
264 size_t value_size = value.size();
266 _cache.append(
"\r\n");
269 _headers.emplace_back(key_index, key_size, value_index, value_size);
273 HTTPResponse&
HTTPResponse::SetCookie(std::string_view name, std::string_view value,
size_t max_age, std::string_view path, std::string_view domain,
bool secure,
bool strict,
bool http_only)
275 size_t index = _cache.size();
278 _cache.append(
"Set-Cookie");
279 size_t key_index = index;
280 size_t key_size = 10;
283 index = _cache.size();
286 size_t value_index = index;
293 _cache.append(value);
294 _cache.append(
"; Max-Age=");
295 _cache.append(FastConvert(max_age, buffer, CppCommon::countof(buffer)));
298 _cache.append(
"; Domain=");
299 _cache.append(domain);
303 _cache.append(
"; Path=");
307 _cache.append(
"; Secure");
309 _cache.append(
"; SameSite=Strict");
311 _cache.append(
"; HttpOnly");
313 size_t value_size = _cache.size() - value_index;
315 _cache.append(
"\r\n");
318 _headers.emplace_back(key_index, key_size, value_index, value_size);
326 SetHeader(
"Content-Length", FastConvert(
body.size(), buffer, CppCommon::countof(buffer)));
328 _cache.append(
"\r\n");
330 size_t index = _cache.size();
335 _body_size =
body.size();
336 _body_length =
body.size();
337 _body_length_provided =
true;
345 SetHeader(
"Content-Length", FastConvert(length, buffer, CppCommon::countof(buffer)));
347 _cache.append(
"\r\n");
349 size_t index = _cache.size();
354 _body_length = length;
355 _body_length_provided =
true;
371 if (!content_type.empty())
389 if (!content_type.empty())
408 SetHeader(
"Content-Type",
"message/http");
413 bool HTTPResponse::IsPendingHeader()
const
415 return (!_error && (_body_index == 0));
418 bool HTTPResponse::IsPendingBody()
const
420 return (!_error && (_body_index > 0) && (_body_size > 0));
423 bool HTTPResponse::ReceiveHeader(
const void* buffer,
size_t size)
426 _cache.insert(_cache.end(), (
const char*)buffer, (
const char*)buffer + size);
429 for (
size_t i = _cache_size; i < _cache.size(); ++i)
432 if ((i + 3) >= _cache.size())
436 if ((_cache[i + 0] ==
'\r') && (_cache[i + 1] ==
'\n') && (_cache[i + 2] ==
'\r') && (_cache[i + 3] ==
'\n'))
444 _protocol_index = index;
446 while (_cache[index] !=
' ')
450 if (index >= _cache.size())
454 if (index >= _cache.size())
458 size_t status_index = index;
459 size_t status_size = 0;
460 while (_cache[index] !=
' ')
462 if ((_cache[index] <
'0') || (_cache[index] >
'9'))
466 if (index >= _cache.size())
470 for (
size_t j = status_index; j < (status_index + status_size); ++j)
473 _status += _cache[j] -
'0';
476 if (index >= _cache.size())
480 _status_phrase_index = index;
481 _status_phrase_size = 0;
482 while (_cache[index] !=
'\r')
484 ++_status_phrase_size;
486 if (index >= _cache.size())
490 if ((index >= _cache.size()) || (_cache[index] !=
'\n'))
493 if (index >= _cache.size())
497 while ((index < _cache.size()) && (index < i))
500 size_t header_name_index = index;
501 size_t header_name_size = 0;
502 while (_cache[index] !=
':')
508 if (index >= _cache.size())
514 if (index >= _cache.size())
518 while (std::isspace(_cache[index]))
523 if (index >= _cache.size())
528 size_t header_value_index = index;
529 size_t header_value_size = 0;
530 while (_cache[index] !=
'\r')
536 if (index >= _cache.size())
540 if ((index >= _cache.size()) || (_cache[index] !=
'\n'))
543 if (index >= _cache.size())
547 if (header_name_size == 0)
551 _headers.emplace_back(header_name_index, header_name_size, header_value_index, header_value_size);
554 if (CppCommon::StringUtils::CompareNoCase(std::string_view(_cache.data() + header_name_index, header_name_size),
"Content-Length"))
557 for (
size_t j = header_value_index; j < (header_value_index + header_value_size); ++j)
559 if ((_cache[j] <
'0') || (_cache[j] >
'9'))
562 _body_length += _cache[j] -
'0';
563 _body_length_provided =
true;
573 _body_size = _cache.size() - i - 4;
576 _cache_size = _cache.size();
583 _cache_size = (_cache.size() >= 3) ? (_cache.size() - 3) : 0;
588 bool HTTPResponse::ReceiveBody(
const void* buffer,
size_t size)
591 _cache.insert(_cache.end(), (
const char*)buffer, (
const char*)buffer + size);
594 _cache_size = _cache.size();
600 if (_body_length_provided)
603 if (_body_size >= _body_length)
605 _body_size = _body_length;
614 size_t index = _body_index + _body_size - 4;
617 if ((_cache[index + 0] ==
'\r') && (_cache[index + 1] ==
'\n') && (_cache[index + 2] ==
'\r') && (_cache[index + 3] ==
'\n'))
619 _body_length = _body_size;
629 std::string_view HTTPResponse::FastConvert(
size_t value,
char* buffer,
size_t size)
634 buffer[--index] =
'0' + (value % 10);
638 return std::string_view(buffer + index, size - index);
643 os <<
"Status: " << response.
status() << std::endl;
644 os <<
"Status phrase: " << response.
status_phrase() << std::endl;
645 os <<
"Protocol: " << response.
protocol() << std::endl;
646 os <<
"Headers: " << response.
headers() << std::endl;
647 for (
size_t i = 0; i < response.
headers(); ++i)
649 auto header = response.
header(i);
650 os << std::get<0>(header) <<
": " << std::get<1>(header) << std::endl;
652 os <<
"Body:" << response.
body_length() << std::endl;
653 os << response.
body() << std::endl;
660 swap(_error, response._error);
661 swap(_status, response._status);
662 swap(_status_phrase_index, response._status_phrase_index);
663 swap(_status_phrase_size, response._status_phrase_size);
664 swap(_protocol_index, response._protocol_index);
665 swap(_protocol_size, response._protocol_size);
666 swap(_headers, response._headers);
667 swap(_body_index, response._body_index);
668 swap(_body_size, response._body_size);
669 swap(_body_length, response._body_length);
670 swap(_body_length_provided, response._body_length_provided);
671 swap(_cache, response._cache);
672 swap(_cache_size, response._cache_size);
HTTPResponse & SetBody(std::string_view body="")
Set the HTTP response body.
HTTPResponse & MakeHeadResponse()
Make HEAD response.
HTTPResponse & MakeOptionsResponse(std::string_view allow="HEAD,GET,POST,PUT,DELETE,OPTIONS,TRACE")
Make OPTIONS response.
HTTPResponse & MakeGetResponse(std::string_view content="", std::string_view content_type="text/plain; charset=UTF-8")
Make GET response.
size_t body_length() const noexcept
Get the HTTP response body length.
HTTPResponse & SetHeader(std::string_view key, std::string_view value)
Set the HTTP response header.
void swap(HTTPResponse &response) noexcept
Swap two instances.
HTTPResponse & MakeOKResponse(int status=200)
Make OK response.
HTTPResponse & SetCookie(std::string_view name, std::string_view value, size_t max_age=86400, std::string_view path="", std::string_view domain="", bool secure=true, bool strict=true, bool http_only=true)
Set the HTTP response cookie.
HTTPResponse & Clear()
Clear the HTTP response cache.
size_t headers() const noexcept
Get the HTTP response headers count.
int status() const noexcept
Get the HTTP response status.
HTTPResponse & SetBodyLength(size_t length)
Set the HTTP response body length.
std::string_view protocol() const noexcept
Get the HTTP response protocol version.
HTTPResponse & MakeErrorResponse(std::string_view content="", std::string_view content_type="text/plain; charset=UTF-8")
Make ERROR response.
HTTPResponse & MakeTraceResponse(std::string_view request)
Make TRACE response.
std::string_view status_phrase() const noexcept
Get the HTTP response status phrase.
HTTPResponse & SetBegin(int status, std::string_view protocol="HTTP/1.1")
Set the HTTP response begin with a given status and protocol.
HTTPResponse & SetContentType(std::string_view extension)
Set the HTTP response content type.
std::tuple< std::string_view, std::string_view > header(size_t i) const noexcept
Get the HTTP response header by index.
std::string_view body() const noexcept
Get the HTTP response body.
HTTP response definition.
std::ostream & operator<<(std::ostream &os, const HTTPRequest &request)
C++ Server project definitions.