49 auto first_space = line.find(
' ');
50 if (first_space == std::string_view::npos)
55 auto method_str = std::string(line.substr(0, first_space));
57 if (method_result.is_err())
61 request.
method = method_result.value();
63 line = line.substr(first_space + 1);
64 auto second_space = line.find(
' ');
65 if (second_space == std::string_view::npos)
70 auto uri_with_query = std::string(line.substr(0, second_space));
73 auto query_pos = uri_with_query.find(
'?');
74 if (query_pos != std::string::npos)
76 request.
uri = uri_with_query.substr(0, query_pos);
77 auto query_string = uri_with_query.substr(query_pos + 1);
82 request.
uri = uri_with_query;
85 auto version_str = std::string(line.substr(second_space + 1));
87 if (version_result.is_err())
91 request.
version = version_result.value();
93 return ok(std::move(request));
101 auto first_space = line.find(
' ');
102 if (first_space == std::string_view::npos)
107 auto version_str = std::string(line.substr(0, first_space));
109 if (version_result.is_err())
113 response.
version = version_result.value();
115 line = line.substr(first_space + 1);
116 auto second_space = line.find(
' ');
118 std::string status_code_str;
119 if (second_space == std::string_view::npos)
122 status_code_str = std::string(line);
127 status_code_str = std::string(line.substr(0, second_space));
128 response.
status_message = std::string(line.substr(second_space + 1));
135 catch (
const std::exception&)
146 return ok(std::move(response));
150 std::map<std::string, std::string>& headers) ->
bool
152 while (!headers_section.empty())
154 auto [line, rest] = split_line(headers_section);
155 headers_section = rest;
162 auto colon_pos = line.find(
':');
163 if (colon_pos == std::string_view::npos)
168 auto name = trim(line.substr(0, colon_pos));
169 auto value = trim(line.substr(colon_pos + 1));
171 headers[std::string(name)] = std::string(value);
188 auto [request_line, rest] = split_line(data);
189 if (request_line.empty())
195 auto request_result = parse_request_line(request_line);
196 if (request_result.is_err())
198 return request_result;
201 auto request = std::move(request_result.value());
204 auto headers_end = rest.find(
"\r\n\r\n");
205 if (headers_end == std::string_view::npos)
208 if (!parse_headers(rest, request.headers))
216 auto headers_section = rest.substr(0, headers_end);
217 if (!parse_headers(headers_section, request.headers))
223 auto body_start = rest.substr(headers_end + 4);
224 if (!body_start.empty())
226 request.body.assign(body_start.begin(), body_start.end());
230 return ok(std::move(request));
244 auto [status_line, rest] = split_line(data);
245 if (status_line.empty())
251 auto response_result = parse_status_line(status_line);
252 if (response_result.is_err())
254 return response_result;
257 auto response = std::move(response_result.value());
260 auto headers_end = rest.find(
"\r\n\r\n");
261 if (headers_end == std::string_view::npos)
264 if (!parse_headers(rest, response.headers))
272 auto headers_section = rest.substr(0, headers_end);
273 if (!parse_headers(headers_section, response.headers))
279 auto body_start = rest.substr(headers_end + 4);
280 if (!body_start.empty())
282 response.body.assign(body_start.begin(), body_start.end());
286 return ok(std::move(response));
331 return serialize_chunked_response(response);
334 std::ostringstream oss;
338 oss << response.status_code <<
" ";
339 oss << response.status_message << CRLF;
342 for (
const auto& [name, value] : response.headers)
344 oss << name << HEADER_SEPARATOR << value << CRLF;
348 for (
const auto&
cookie : response.set_cookies)
357 auto str = oss.str();
358 std::vector<uint8_t> result(str.begin(), str.end());
359 result.insert(result.end(), response.body.begin(), response.body.end());
368 std::ostringstream oss;
372 oss << response.status_code <<
" ";
373 oss << response.status_message << CRLF;
376 for (
const auto& [name, value] : response.headers)
379 if (name !=
"Content-Length" && name !=
"content-length")
381 oss << name << HEADER_SEPARATOR << value << CRLF;
386 oss <<
"Transfer-Encoding" << HEADER_SEPARATOR <<
"chunked" << CRLF;
389 for (
const auto&
cookie : response.set_cookies)
398 auto str = oss.str();
399 std::vector<uint8_t> result(str.begin(), str.end());
402 constexpr size_t CHUNK_SIZE = 8192;
405 while (offset < response.body.size())
407 size_t chunk_size = std::min(CHUNK_SIZE, response.body.size() - offset);
410 std::ostringstream chunk_header;
411 chunk_header << std::hex << chunk_size << CRLF;
412 std::string chunk_header_str = chunk_header.str();
413 result.insert(result.end(), chunk_header_str.begin(), chunk_header_str.end());
416 result.insert(result.end(),
417 response.body.begin() + offset,
418 response.body.begin() + offset + chunk_size);
421 result.push_back(
'\r');
422 result.push_back(
'\n');
424 offset += chunk_size;
428 std::string last_chunk =
"0\r\n\r\n";
429 result.insert(result.end(), last_chunk.begin(), last_chunk.end());
432 std::to_string(response.body.size()) +
" bytes in " +
433 std::to_string((response.body.size() + CHUNK_SIZE - 1) / CHUNK_SIZE) +
" chunks");
548 auto cookie_header = request.get_header(
"Cookie");
549 if (!cookie_header) {
553 const std::string& cookie_str = *cookie_header;
556 while (pos < cookie_str.size()) {
558 while (pos < cookie_str.size() && (cookie_str[pos] ==
' ' || cookie_str[pos] ==
'\t')) {
563 size_t semi_pos = cookie_str.find(
';', pos);
564 if (semi_pos == std::string::npos) {
565 semi_pos = cookie_str.size();
569 std::string pair = cookie_str.substr(pos, semi_pos - pos);
572 size_t eq_pos = pair.find(
'=');
573 if (eq_pos != std::string::npos) {
574 std::string name = pair.substr(0, eq_pos);
575 std::string value = pair.substr(eq_pos + 1);
578 auto trim = [](std::string& s) {
581 while (start < s.size() && (s[start] ==
' ' || s[start] ==
'\t')) {
585 size_t end = s.size();
586 while (end > start && (s[end - 1] ==
' ' || s[end - 1] ==
'\t')) {
589 s = s.substr(start, end - start);
596 request.cookies[name] = value;
607 auto content_type = request.get_header(
"Content-Type");
614 std::string boundary;
615 size_t boundary_pos = content_type->find(
"boundary=");
616 if (boundary_pos == std::string::npos) {
620 boundary =
"--" + content_type->substr(boundary_pos + 9);
623 if (!boundary.empty() && boundary.front() ==
'"') {
624 boundary = boundary.substr(1);
626 if (!boundary.empty() && boundary.back() ==
'"') {
631 const std::vector<uint8_t>& body = request.body;
632 std::string body_str(body.begin(), body.end());
635 const std::string boundary_delimiter = boundary;
636 const std::string boundary_end = boundary +
"--";
638 while (pos < body_str.size()) {
640 size_t boundary_start = body_str.find(boundary_delimiter, pos);
641 if (boundary_start == std::string::npos) {
646 pos = boundary_start + boundary_delimiter.size();
647 if (pos + 2 <= body_str.size() && body_str.substr(pos, 2) ==
"\r\n") {
652 if (pos >= 2 && body_str.substr(pos - 2, 2) ==
"--") {
657 std::map<std::string, std::string> part_headers;
658 size_t headers_end = body_str.find(
"\r\n\r\n", pos);
659 if (headers_end == std::string::npos) {
663 std::string headers_section = body_str.substr(pos, headers_end - pos);
664 pos = headers_end + 4;
667 size_t disp_start = headers_section.find(
"Content-Disposition:");
668 if (disp_start == std::string::npos) {
672 size_t disp_end = headers_section.find(
"\r\n", disp_start);
673 std::string disposition = headers_section.substr(
675 disp_end == std::string::npos ? std::string::npos : disp_end - disp_start - 20
679 std::string field_name;
680 size_t name_pos = disposition.find(
"name=\"");
681 if (name_pos != std::string::npos) {
682 size_t name_start = name_pos + 6;
683 size_t name_end = disposition.find(
'"', name_start);
684 if (name_end != std::string::npos) {
685 field_name = disposition.substr(name_start, name_end - name_start);
690 size_t filename_pos = disposition.find(
"filename=\"");
691 bool is_file = (filename_pos != std::string::npos);
694 size_t next_boundary = body_str.find(boundary_delimiter, pos);
695 if (next_boundary == std::string::npos) {
696 next_boundary = body_str.size();
700 size_t content_end = next_boundary;
701 if (content_end >= 2 && body_str.substr(content_end - 2, 2) ==
"\r\n") {
707 std::string filename;
708 size_t filename_start = filename_pos + 10;
709 size_t filename_end = disposition.find(
'"', filename_start);
710 if (filename_end != std::string::npos) {
711 filename = disposition.substr(filename_start, filename_end - filename_start);
715 std::string content_type_value =
"application/octet-stream";
716 size_t ct_pos = headers_section.find(
"Content-Type:");
717 if (ct_pos != std::string::npos) {
718 size_t ct_start = ct_pos + 13;
719 size_t ct_end = headers_section.find(
"\r\n", ct_start);
720 content_type_value = headers_section.substr(
722 ct_end == std::string::npos ? std::string::npos : ct_end - ct_start
725 size_t first = content_type_value.find_first_not_of(
" \t");
726 size_t last = content_type_value.find_last_not_of(
" \t\r\n");
727 if (first != std::string::npos) {
728 content_type_value = content_type_value.substr(first, last - first + 1);
737 file.
content.assign(body_str.begin() + pos, body_str.begin() + content_end);
739 request.files[field_name] = std::move(file);
742 std::string value = body_str.substr(pos, content_end - pos);
743 request.form_data[field_name] = value;
749 return ok(std::monostate{});