Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
kcenon::network::internal::http_parser Class Reference

Parser and serializer for HTTP messages (requests and responses) More...

#include <http_parser.h>

Collaboration diagram for kcenon::network::internal::http_parser:
Collaboration graph

Static Public Member Functions

static auto parse_request (const std::vector< uint8_t > &data) -> Result< http_request >
 Parse HTTP request from raw bytes.
 
static auto parse_request (std::string_view data) -> Result< http_request >
 Parse HTTP request from string view.
 
static auto parse_response (const std::vector< uint8_t > &data) -> Result< http_response >
 Parse HTTP response from raw bytes.
 
static auto parse_response (std::string_view data) -> Result< http_response >
 Parse HTTP response from string view.
 
static auto serialize_request (const http_request &request) -> std::vector< uint8_t >
 Serialize HTTP request to raw bytes.
 
static auto serialize_response (const http_response &response) -> std::vector< uint8_t >
 Serialize HTTP response to raw bytes.
 
static auto url_encode (const std::string &value) -> std::string
 URL encode a string.
 
static auto url_decode (const std::string &value) -> std::string
 URL decode a string.
 
static auto parse_query_string (const std::string &query_string) -> std::map< std::string, std::string >
 Parse query string into key-value pairs.
 
static auto build_query_string (const std::map< std::string, std::string > &params) -> std::string
 Build query string from key-value pairs.
 
static auto parse_cookies (http_request &request) -> void
 Parse cookies from Cookie header.
 
static auto parse_multipart_form_data (http_request &request) -> VoidResult
 Parse multipart/form-data from request body.
 

Static Private Member Functions

static auto parse_request_line (std::string_view line) -> Result< http_request >
 
static auto parse_status_line (std::string_view line) -> Result< http_response >
 
static auto parse_headers (std::string_view headers_section, std::map< std::string, std::string > &headers) -> bool
 
static auto trim (std::string_view str) -> std::string_view
 
static auto split_line (std::string_view data) -> std::pair< std::string_view, std::string_view >
 
static auto serialize_chunked_response (const http_response &response) -> std::vector< uint8_t >
 

Detailed Description

Parser and serializer for HTTP messages (requests and responses)

Thread Safety

  • All methods are stateless and can be called from multiple threads
  • No internal state is maintained between calls

Features

  • Parse HTTP requests from raw bytes
  • Parse HTTP responses from raw bytes
  • Serialize HTTP requests to raw bytes
  • Serialize HTTP responses to raw bytes
  • URL encoding/decoding for query parameters
  • Chunked transfer encoding support (future)

Usage Example

// Parse HTTP request
std::vector<uint8_t> raw_data = ...;
auto result = http_parser::parse_request(raw_data);
if (result) {
const auto& request = result.value();
std::cout << "Method: " << http_method_to_string(request.method) << "\n";
std::cout << "URI: " << request.uri << "\n";
}
// Serialize HTTP response
http_response response;
response.status_code = 200;
response.set_body_string("Hello, World!");
auto bytes = http_parser::serialize_response(response);
static auto parse_request(const std::vector< uint8_t > &data) -> Result< http_request >
Parse HTTP request from raw bytes.
static auto serialize_response(const http_response &response) -> std::vector< uint8_t >
Serialize HTTP response to raw bytes.
auto http_method_to_string(http_method method) -> std::string
Convert HTTP method enum to string.
Represents an HTTP response message.
Definition http_types.h:162
auto set_body_string(const std::string &content) -> void
Set body from string.

Definition at line 49 of file http_parser.h.

Member Function Documentation

◆ build_query_string()

auto kcenon::network::internal::http_parser::build_query_string ( const std::map< std::string, std::string > & params) -> std::string
static

Build query string from key-value pairs.

Parameters
paramsMap of query parameters
Returns
Query string (e.g., "key1=value1&key2=value2")

Definition at line 522 of file http_parser.cpp.

524 {
525 std::ostringstream oss;
526 bool first = true;
527
528 for (const auto& [key, value] : params)
529 {
530 if (!first)
531 {
532 oss << '&';
533 }
534 first = false;
535
536 oss << url_encode(key);
537 if (!value.empty())
538 {
539 oss << '=' << url_encode(value);
540 }
541 }
542
543 return oss.str();
544 }
static auto url_encode(const std::string &value) -> std::string
URL encode a string.

◆ parse_cookies()

auto kcenon::network::internal::http_parser::parse_cookies ( http_request & request) -> void
static

Parse cookies from Cookie header.

Parameters
requestHTTP request to parse cookies into

Parses the Cookie header (if present) and populates request.cookies Cookie format: "name1=value1; name2=value2"

Definition at line 546 of file http_parser.cpp.

547 {
548 auto cookie_header = request.get_header("Cookie");
549 if (!cookie_header) {
550 return; // No Cookie header present
551 }
552
553 const std::string& cookie_str = *cookie_header;
554 size_t pos = 0;
555
556 while (pos < cookie_str.size()) {
557 // Skip whitespace
558 while (pos < cookie_str.size() && (cookie_str[pos] == ' ' || cookie_str[pos] == '\t')) {
559 pos++;
560 }
561
562 // Find the next semicolon or end of string
563 size_t semi_pos = cookie_str.find(';', pos);
564 if (semi_pos == std::string::npos) {
565 semi_pos = cookie_str.size();
566 }
567
568 // Extract and parse the name=value pair
569 std::string pair = cookie_str.substr(pos, semi_pos - pos);
570
571 // Find the '=' separator
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);
576
577 // Trim whitespace from name and value
578 auto trim = [](std::string& s) {
579 // Trim leading whitespace
580 size_t start = 0;
581 while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) {
582 start++;
583 }
584 // Trim trailing whitespace
585 size_t end = s.size();
586 while (end > start && (s[end - 1] == ' ' || s[end - 1] == '\t')) {
587 end--;
588 }
589 s = s.substr(start, end - start);
590 };
591
592 trim(name);
593 trim(value);
594
595 if (!name.empty()) {
596 request.cookies[name] = value;
597 }
598 }
599
600 pos = semi_pos + 1;
601 }
602 }
static auto trim(std::string_view str) -> std::string_view

◆ parse_headers()

auto kcenon::network::internal::http_parser::parse_headers ( std::string_view headers_section,
std::map< std::string, std::string > & headers ) -> bool
staticprivate

Definition at line 149 of file http_parser.cpp.

151 {
152 while (!headers_section.empty())
153 {
154 auto [line, rest] = split_line(headers_section);
155 headers_section = rest;
156
157 if (line.empty())
158 {
159 break; // Empty line marks end of headers
160 }
161
162 auto colon_pos = line.find(':');
163 if (colon_pos == std::string_view::npos)
164 {
165 return false; // Invalid header
166 }
167
168 auto name = trim(line.substr(0, colon_pos));
169 auto value = trim(line.substr(colon_pos + 1));
170
171 headers[std::string(name)] = std::string(value);
172 }
173
174 return true;
175 }
static auto split_line(std::string_view data) -> std::pair< std::string_view, std::string_view >

◆ parse_multipart_form_data()

auto kcenon::network::internal::http_parser::parse_multipart_form_data ( http_request & request) -> VoidResult
static

Parse multipart/form-data from request body.

Parameters
requestHTTP request to parse multipart data from
Returns
Result indicating success or error

Parses multipart/form-data body and populates request.form_data and request.files Requires Content-Type header with boundary parameter

Definition at line 604 of file http_parser.cpp.

605 {
606 // Get Content-Type header
607 auto content_type = request.get_header("Content-Type");
608 if (!content_type) {
609 return error<std::monostate>(-1, "Missing Content-Type header");
610 }
611
612 // Extract boundary from Content-Type header
613 // Format: "multipart/form-data; boundary=----WebKitFormBoundary..."
614 std::string boundary;
615 size_t boundary_pos = content_type->find("boundary=");
616 if (boundary_pos == std::string::npos) {
617 return error<std::monostate>(-1, "Missing boundary in Content-Type header");
618 }
619
620 boundary = "--" + content_type->substr(boundary_pos + 9);
621
622 // Remove quotes if present
623 if (!boundary.empty() && boundary.front() == '"') {
624 boundary = boundary.substr(1);
625 }
626 if (!boundary.empty() && boundary.back() == '"') {
627 boundary.pop_back();
628 }
629
630 // Parse multipart body
631 const std::vector<uint8_t>& body = request.body;
632 std::string body_str(body.begin(), body.end());
633
634 size_t pos = 0;
635 const std::string boundary_delimiter = boundary;
636 const std::string boundary_end = boundary + "--";
637
638 while (pos < body_str.size()) {
639 // Find the next boundary
640 size_t boundary_start = body_str.find(boundary_delimiter, pos);
641 if (boundary_start == std::string::npos) {
642 break;
643 }
644
645 // Move past the boundary and CRLF
646 pos = boundary_start + boundary_delimiter.size();
647 if (pos + 2 <= body_str.size() && body_str.substr(pos, 2) == "\r\n") {
648 pos += 2;
649 }
650
651 // Check if this is the final boundary
652 if (pos >= 2 && body_str.substr(pos - 2, 2) == "--") {
653 break; // Final boundary found
654 }
655
656 // Parse part headers
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) {
660 break;
661 }
662
663 std::string headers_section = body_str.substr(pos, headers_end - pos);
664 pos = headers_end + 4; // Skip "\r\n\r\n"
665
666 // Parse Content-Disposition header
667 size_t disp_start = headers_section.find("Content-Disposition:");
668 if (disp_start == std::string::npos) {
669 continue;
670 }
671
672 size_t disp_end = headers_section.find("\r\n", disp_start);
673 std::string disposition = headers_section.substr(
674 disp_start + 20, // Length of "Content-Disposition:"
675 disp_end == std::string::npos ? std::string::npos : disp_end - disp_start - 20
676 );
677
678 // Extract field name
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);
686 }
687 }
688
689 // Check if this is a file upload
690 size_t filename_pos = disposition.find("filename=\"");
691 bool is_file = (filename_pos != std::string::npos);
692
693 // Find the part content (until next boundary)
694 size_t next_boundary = body_str.find(boundary_delimiter, pos);
695 if (next_boundary == std::string::npos) {
696 next_boundary = body_str.size();
697 }
698
699 // Extract content (remove trailing \r\n before boundary)
700 size_t content_end = next_boundary;
701 if (content_end >= 2 && body_str.substr(content_end - 2, 2) == "\r\n") {
702 content_end -= 2;
703 }
704
705 if (is_file) {
706 // Extract filename
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);
712 }
713
714 // Extract Content-Type if present
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(
721 ct_start,
722 ct_end == std::string::npos ? std::string::npos : ct_end - ct_start
723 );
724 // Trim whitespace
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);
729 }
730 }
731
732 // Create multipart_file
733 multipart_file file;
734 file.field_name = field_name;
735 file.filename = filename;
736 file.content_type = content_type_value;
737 file.content.assign(body_str.begin() + pos, body_str.begin() + content_end);
738
739 request.files[field_name] = std::move(file);
740 } else {
741 // Regular form field
742 std::string value = body_str.substr(pos, content_end - pos);
743 request.form_data[field_name] = value;
744 }
745
746 pos = next_boundary;
747 }
748
749 return ok(std::monostate{});
750 }
@ error
Black hole detected, reset to base.
VoidResult ok()

References kcenon::network::internal::multipart_file::content, kcenon::network::internal::multipart_file::content_type, kcenon::network::protocols::quic::error, kcenon::network::internal::multipart_file::field_name, kcenon::network::internal::multipart_file::filename, and kcenon::network::ok().

Here is the call graph for this function:

◆ parse_query_string()

auto kcenon::network::internal::http_parser::parse_query_string ( const std::string & query_string) -> std::map<std::string, std::string>
static

Parse query string into key-value pairs.

Parameters
query_stringQuery string (e.g., "key1=value1&key2=value2")
Returns
Map of query parameters

Definition at line 496 of file http_parser.cpp.

498 {
499 std::map<std::string, std::string> params;
500 std::istringstream iss(query_string);
501 std::string pair;
502
503 while (std::getline(iss, pair, '&'))
504 {
505 auto eq_pos = pair.find('=');
506 if (eq_pos != std::string::npos)
507 {
508 auto key = url_decode(pair.substr(0, eq_pos));
509 auto value = url_decode(pair.substr(eq_pos + 1));
510 params[key] = value;
511 }
512 else
513 {
514 // Parameter without value
515 params[url_decode(pair)] = "";
516 }
517 }
518
519 return params;
520 }
static auto url_decode(const std::string &value) -> std::string
URL decode a string.

Referenced by kcenon::network::core::http_url::parse().

Here is the caller graph for this function:

◆ parse_request() [1/2]

auto kcenon::network::internal::http_parser::parse_request ( const std::vector< uint8_t > & data) -> Result<http_request>
static

Parse HTTP request from raw bytes.

Parameters
dataRaw HTTP request data
Returns
Result containing parsed request or error

Parses the following format:

METHOD URI HTTP/VERSION\r\n
Header1: Value1\r\n
Header2: Value2\r\n
\r\n
[body]

Definition at line 179 of file http_parser.cpp.

180 {
181 std::string_view str_view(reinterpret_cast<const char*>(data.data()), data.size());
182 return parse_request(str_view);
183 }

Referenced by kcenon::network::core::http_server::handle_request(), and kcenon::network::core::http_server::start().

Here is the caller graph for this function:

◆ parse_request() [2/2]

auto kcenon::network::internal::http_parser::parse_request ( std::string_view data) -> Result<http_request>
static

Parse HTTP request from string view.

Parameters
dataString view of HTTP request
Returns
Result containing parsed request or error

Definition at line 185 of file http_parser.cpp.

186 {
187 // Split request line
188 auto [request_line, rest] = split_line(data);
189 if (request_line.empty())
190 {
191 return error<http_request>(-1, "Empty HTTP request");
192 }
193
194 // Parse request line
195 auto request_result = parse_request_line(request_line);
196 if (request_result.is_err())
197 {
198 return request_result;
199 }
200
201 auto request = std::move(request_result.value());
202
203 // Find end of headers (empty line)
204 auto headers_end = rest.find("\r\n\r\n");
205 if (headers_end == std::string_view::npos)
206 {
207 // No body, all remaining data is headers
208 if (!parse_headers(rest, request.headers))
209 {
210 return error<http_request>(-1, "Failed to parse headers");
211 }
212 }
213 else
214 {
215 // Parse headers
216 auto headers_section = rest.substr(0, headers_end);
217 if (!parse_headers(headers_section, request.headers))
218 {
219 return error<http_request>(-1, "Failed to parse headers");
220 }
221
222 // Parse body
223 auto body_start = rest.substr(headers_end + 4);
224 if (!body_start.empty())
225 {
226 request.body.assign(body_start.begin(), body_start.end());
227 }
228 }
229
230 return ok(std::move(request));
231 }
static auto parse_headers(std::string_view headers_section, std::map< std::string, std::string > &headers) -> bool
static auto parse_request_line(std::string_view line) -> Result< http_request >

References kcenon::network::protocols::quic::error, and kcenon::network::ok().

Here is the call graph for this function:

◆ parse_request_line()

auto kcenon::network::internal::http_parser::parse_request_line ( std::string_view line) -> Result<http_request>
staticprivate

Definition at line 44 of file http_parser.cpp.

45 {
46 http_request request;
47
48 // Parse: METHOD URI HTTP/VERSION
49 auto first_space = line.find(' ');
50 if (first_space == std::string_view::npos)
51 {
52 return error<http_request>(-1, "Invalid request line: no spaces found");
53 }
54
55 auto method_str = std::string(line.substr(0, first_space));
56 auto method_result = string_to_http_method(method_str);
57 if (method_result.is_err())
58 {
59 return error<http_request>(method_result.error().code, method_result.error().message);
60 }
61 request.method = method_result.value();
62
63 line = line.substr(first_space + 1);
64 auto second_space = line.find(' ');
65 if (second_space == std::string_view::npos)
66 {
67 return error<http_request>(-1, "Invalid request line: missing HTTP version");
68 }
69
70 auto uri_with_query = std::string(line.substr(0, second_space));
71
72 // Parse query parameters from URI
73 auto query_pos = uri_with_query.find('?');
74 if (query_pos != std::string::npos)
75 {
76 request.uri = uri_with_query.substr(0, query_pos);
77 auto query_string = uri_with_query.substr(query_pos + 1);
78 request.query_params = parse_query_string(query_string);
79 }
80 else
81 {
82 request.uri = uri_with_query;
83 }
84
85 auto version_str = std::string(line.substr(second_space + 1));
86 auto version_result = string_to_http_version(version_str);
87 if (version_result.is_err())
88 {
89 return error<http_request>(version_result.error().code, version_result.error().message);
90 }
91 request.version = version_result.value();
92
93 return ok(std::move(request));
94 }
static auto parse_query_string(const std::string &query_string) -> std::map< std::string, std::string >
Parse query string into key-value pairs.
auto string_to_http_method(const std::string &method_str) -> ::kcenon::network::internal::Result< http_method >
Convert string to HTTP method enum.
auto string_to_http_version(const std::string &version_str) -> ::kcenon::network::internal::Result< http_version >
Convert string to HTTP version enum.

References kcenon::network::protocols::quic::error, kcenon::network::internal::http_request::method, kcenon::network::ok(), kcenon::network::internal::http_request::query_params, kcenon::network::internal::string_to_http_method(), kcenon::network::internal::string_to_http_version(), kcenon::network::internal::http_request::uri, and kcenon::network::internal::http_request::version.

Here is the call graph for this function:

◆ parse_response() [1/2]

auto kcenon::network::internal::http_parser::parse_response ( const std::vector< uint8_t > & data) -> Result<http_response>
static

Parse HTTP response from raw bytes.

Parameters
dataRaw HTTP response data
Returns
Result containing parsed response or error

Parses the following format:

HTTP/VERSION STATUS_CODE STATUS_MESSAGE\r\n
Header1: Value1\r\n
Header2: Value2\r\n
\r\n
[body]

Definition at line 235 of file http_parser.cpp.

236 {
237 std::string_view str_view(reinterpret_cast<const char*>(data.data()), data.size());
238 return parse_response(str_view);
239 }
static auto parse_response(const std::vector< uint8_t > &data) -> Result< http_response >
Parse HTTP response from raw bytes.

Referenced by kcenon::network::core::http_client::request().

Here is the caller graph for this function:

◆ parse_response() [2/2]

auto kcenon::network::internal::http_parser::parse_response ( std::string_view data) -> Result<http_response>
static

Parse HTTP response from string view.

Parameters
dataString view of HTTP response
Returns
Result containing parsed response or error

Definition at line 241 of file http_parser.cpp.

242 {
243 // Split status line
244 auto [status_line, rest] = split_line(data);
245 if (status_line.empty())
246 {
247 return error<http_response>(-1, "Empty HTTP response");
248 }
249
250 // Parse status line
251 auto response_result = parse_status_line(status_line);
252 if (response_result.is_err())
253 {
254 return response_result;
255 }
256
257 auto response = std::move(response_result.value());
258
259 // Find end of headers (empty line)
260 auto headers_end = rest.find("\r\n\r\n");
261 if (headers_end == std::string_view::npos)
262 {
263 // No body, all remaining data is headers
264 if (!parse_headers(rest, response.headers))
265 {
266 return error<http_response>(-1, "Failed to parse headers");
267 }
268 }
269 else
270 {
271 // Parse headers
272 auto headers_section = rest.substr(0, headers_end);
273 if (!parse_headers(headers_section, response.headers))
274 {
275 return error<http_response>(-1, "Failed to parse headers");
276 }
277
278 // Parse body
279 auto body_start = rest.substr(headers_end + 4);
280 if (!body_start.empty())
281 {
282 response.body.assign(body_start.begin(), body_start.end());
283 }
284 }
285
286 return ok(std::move(response));
287 }
static auto parse_status_line(std::string_view line) -> Result< http_response >

References kcenon::network::protocols::quic::error, and kcenon::network::ok().

Here is the call graph for this function:

◆ parse_status_line()

auto kcenon::network::internal::http_parser::parse_status_line ( std::string_view line) -> Result<http_response>
staticprivate

Definition at line 96 of file http_parser.cpp.

97 {
98 http_response response;
99
100 // Parse: HTTP/VERSION STATUS_CODE STATUS_MESSAGE
101 auto first_space = line.find(' ');
102 if (first_space == std::string_view::npos)
103 {
104 return error<http_response>(-1, "Invalid status line: no spaces found");
105 }
106
107 auto version_str = std::string(line.substr(0, first_space));
108 auto version_result = string_to_http_version(version_str);
109 if (version_result.is_err())
110 {
111 return error<http_response>(version_result.error().code, version_result.error().message);
112 }
113 response.version = version_result.value();
114
115 line = line.substr(first_space + 1);
116 auto second_space = line.find(' ');
117
118 std::string status_code_str;
119 if (second_space == std::string_view::npos)
120 {
121 // No status message, only status code
122 status_code_str = std::string(line);
123 response.status_message = get_status_message(response.status_code);
124 }
125 else
126 {
127 status_code_str = std::string(line.substr(0, second_space));
128 response.status_message = std::string(line.substr(second_space + 1));
129 }
130
131 try
132 {
133 response.status_code = std::stoi(status_code_str);
134 }
135 catch (const std::exception&)
136 {
137 return error<http_response>(-1, "Invalid status code: " + status_code_str);
138 }
139
140 // If status message is empty, use default
141 if (response.status_message.empty())
142 {
143 response.status_message = get_status_message(response.status_code);
144 }
145
146 return ok(std::move(response));
147 }
auto get_status_message(int status_code) -> std::string
Get HTTP status message for a status code.

References kcenon::network::protocols::quic::error, kcenon::network::internal::get_status_message(), kcenon::network::ok(), kcenon::network::internal::http_response::status_code, kcenon::network::internal::http_response::status_message, kcenon::network::internal::string_to_http_version(), and kcenon::network::internal::http_response::version.

Here is the call graph for this function:

◆ serialize_chunked_response()

auto kcenon::network::internal::http_parser::serialize_chunked_response ( const http_response & response) -> std::vector<uint8_t>
staticprivate

Definition at line 366 of file http_parser.cpp.

367 {
368 std::ostringstream oss;
369
370 // Status line: HTTP/VERSION STATUS_CODE STATUS_MESSAGE
371 oss << http_version_to_string(response.version) << " ";
372 oss << response.status_code << " ";
373 oss << response.status_message << CRLF;
374
375 // Headers (excluding Content-Length, adding Transfer-Encoding)
376 for (const auto& [name, value] : response.headers)
377 {
378 // Skip Content-Length header for chunked encoding
379 if (name != "Content-Length" && name != "content-length")
380 {
381 oss << name << HEADER_SEPARATOR << value << CRLF;
382 }
383 }
384
385 // Add Transfer-Encoding: chunked header
386 oss << "Transfer-Encoding" << HEADER_SEPARATOR << "chunked" << CRLF;
387
388 // Set-Cookie headers
389 for (const auto& cookie : response.set_cookies)
390 {
391 oss << "Set-Cookie" << HEADER_SEPARATOR << cookie.to_header_value() << CRLF;
392 }
393
394 // Empty line to separate headers from body
395 oss << CRLF;
396
397 // Convert header to bytes
398 auto str = oss.str();
399 std::vector<uint8_t> result(str.begin(), str.end());
400
401 // Encode body as chunks
402 constexpr size_t CHUNK_SIZE = 8192; // 8KB chunks
403 size_t offset = 0;
404
405 while (offset < response.body.size())
406 {
407 size_t chunk_size = std::min(CHUNK_SIZE, response.body.size() - offset);
408
409 // Chunk size in hexadecimal + CRLF
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());
414
415 // Chunk data
416 result.insert(result.end(),
417 response.body.begin() + offset,
418 response.body.begin() + offset + chunk_size);
419
420 // CRLF after chunk data
421 result.push_back('\r');
422 result.push_back('\n');
423
424 offset += chunk_size;
425 }
426
427 // Last chunk: "0\r\n\r\n"
428 std::string last_chunk = "0\r\n\r\n";
429 result.insert(result.end(), last_chunk.begin(), last_chunk.end());
430
431 NETWORK_LOG_DEBUG("[http_parser] Serialized chunked response: " +
432 std::to_string(response.body.size()) + " bytes in " +
433 std::to_string((response.body.size() + CHUNK_SIZE - 1) / CHUNK_SIZE) + " chunks");
434
435 return result;
436 }
#define NETWORK_LOG_DEBUG(msg)
auto http_version_to_string(http_version version) -> std::string
Convert HTTP version enum to string.

References kcenon::network::internal::http_version_to_string(), NETWORK_LOG_DEBUG, and kcenon::network::internal::cookie::to_header_value().

Here is the call graph for this function:

◆ serialize_request()

auto kcenon::network::internal::http_parser::serialize_request ( const http_request & request) -> std::vector<uint8_t>
static

Serialize HTTP request to raw bytes.

Parameters
requestHTTP request to serialize
Returns
Serialized request as byte vector

Definition at line 291 of file http_parser.cpp.

292 {
293 std::ostringstream oss;
294
295 // Request line: METHOD URI HTTP/VERSION
296 oss << http_method_to_string(request.method) << " ";
297 oss << request.uri;
298
299 // Add query parameters if present
300 if (!request.query_params.empty())
301 {
302 oss << "?" << build_query_string(request.query_params);
303 }
304
305 oss << " " << http_version_to_string(request.version) << CRLF;
306
307 // Headers
308 for (const auto& [name, value] : request.headers)
309 {
310 oss << name << HEADER_SEPARATOR << value << CRLF;
311 }
312
313 // Empty line to separate headers from body
314 oss << CRLF;
315
316 // Body
317 auto str = oss.str();
318 std::vector<uint8_t> result(str.begin(), str.end());
319 result.insert(result.end(), request.body.begin(), request.body.end());
320
321 return result;
322 }
static auto build_query_string(const std::map< std::string, std::string > &params) -> std::string
Build query string from key-value pairs.

References kcenon::network::internal::http_method_to_string(), and kcenon::network::internal::http_version_to_string().

Referenced by kcenon::network::core::http_client::request().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ serialize_response()

auto kcenon::network::internal::http_parser::serialize_response ( const http_response & response) -> std::vector<uint8_t>
static

Serialize HTTP response to raw bytes.

Parameters
responseHTTP response to serialize
Returns
Serialized response as byte vector

Definition at line 326 of file http_parser.cpp.

327 {
328 // Use chunked encoding if requested
329 if (response.use_chunked_encoding && response.version == http_version::HTTP_1_1)
330 {
331 return serialize_chunked_response(response);
332 }
333
334 std::ostringstream oss;
335
336 // Status line: HTTP/VERSION STATUS_CODE STATUS_MESSAGE
337 oss << http_version_to_string(response.version) << " ";
338 oss << response.status_code << " ";
339 oss << response.status_message << CRLF;
340
341 // Headers
342 for (const auto& [name, value] : response.headers)
343 {
344 oss << name << HEADER_SEPARATOR << value << CRLF;
345 }
346
347 // Set-Cookie headers
348 for (const auto& cookie : response.set_cookies)
349 {
350 oss << "Set-Cookie" << HEADER_SEPARATOR << cookie.to_header_value() << CRLF;
351 }
352
353 // Empty line to separate headers from body
354 oss << CRLF;
355
356 // Body
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());
360
361 return result;
362 }
static auto serialize_chunked_response(const http_response &response) -> std::vector< uint8_t >

References kcenon::network::internal::HTTP_1_1, kcenon::network::internal::http_version_to_string(), and kcenon::network::internal::cookie::to_header_value().

Referenced by kcenon::network::core::http_server::handle_request(), and kcenon::network::core::http_server::start().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ split_line()

auto kcenon::network::internal::http_parser::split_line ( std::string_view data) -> std::pair<std::string_view, std::string_view>
staticprivate

Definition at line 33 of file http_parser.cpp.

34 {
35 auto pos = data.find("\r\n");
36 if (pos == std::string_view::npos)
37 {
38 return {data, std::string_view{}};
39 }
40
41 return {data.substr(0, pos), data.substr(pos + 2)};
42 }

◆ trim()

auto kcenon::network::internal::http_parser::trim ( std::string_view str) -> std::string_view
staticprivate

Definition at line 22 of file http_parser.cpp.

23 {
24 const auto start = std::find_if(str.begin(), str.end(),
25 [](unsigned char c) { return !std::isspace(c); });
26
27 const auto end = std::find_if(str.rbegin(), str.rend(),
28 [](unsigned char c) { return !std::isspace(c); }).base();
29
30 return (start < end) ? std::string_view(&*start, std::distance(start, end)) : std::string_view{};
31 }

◆ url_decode()

auto kcenon::network::internal::http_parser::url_decode ( const std::string & value) -> std::string
static

URL decode a string.

Parameters
valueString to decode
Returns
URL-decoded string

Decodes XX sequences back to original characters Handles both %20 and + as spaces

Definition at line 463 of file http_parser.cpp.

464 {
465 std::ostringstream decoded;
466
467 for (std::size_t i = 0; i < value.length(); ++i)
468 {
469 if (value[i] == '%')
470 {
471 if (i + 2 < value.length())
472 {
473 // Decode %XX
474 std::string hex = value.substr(i + 1, 2);
475 int decoded_char = std::stoi(hex, nullptr, 16);
476 decoded << static_cast<char>(decoded_char);
477 i += 2;
478 }
479 }
480 else if (value[i] == '+')
481 {
482 // + can be used for spaces
483 decoded << ' ';
484 }
485 else
486 {
487 decoded << value[i];
488 }
489 }
490
491 return decoded.str();
492 }

◆ url_encode()

auto kcenon::network::internal::http_parser::url_encode ( const std::string & value) -> std::string
static

URL encode a string.

Parameters
valueString to encode
Returns
URL-encoded string

Encodes special characters as XX where XX is hex code Spaces are encoded as %20 (not +)

Definition at line 440 of file http_parser.cpp.

441 {
442 std::ostringstream escaped;
443 escaped.fill('0');
444 escaped << std::hex << std::uppercase;
445
446 for (unsigned char c : value)
447 {
448 // Keep alphanumeric and other safe characters
449 if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~')
450 {
451 escaped << c;
452 }
453 else
454 {
455 // Encode as %XX
456 escaped << '%' << std::setw(2) << int(c);
457 }
458 }
459
460 return escaped.str();
461 }

The documentation for this class was generated from the following files: