17 std::map<std::string, std::string> &extracted_params)
const ->
bool {
19 if (method != req_method) {
25 if (!std::regex_match(path, matches, regex_pattern)) {
30 extracted_params.clear();
31 for (std::size_t i = 0; i < param_names.size() && i + 1 < matches.size();
33 extracted_params[param_names[i]] = matches[i + 1].str();
43 if (data.size() + chunk.size() > MAX_REQUEST_SIZE) {
48 data.insert(data.end(), chunk.begin(), chunk.end());
51 if (!headers_complete) {
52 auto marker_pos = find_header_end(data);
53 if (marker_pos != std::string::npos) {
54 headers_complete =
true;
55 headers_end_pos = marker_pos + 4;
56 content_length = parse_content_length(data, headers_end_pos);
57 }
else if (data.size() > MAX_HEADER_SIZE) {
72 for (std::size_t i = 0; i + 3 < data.size(); ++i) {
73 if (data[i] ==
'\r' && data[i + 1] ==
'\n' && data[i + 2] ==
'\r' &&
74 data[i + 3] ==
'\n') {
78 return std::string::npos;
82 std::size_t headers_end)
85 std::string headers_str(
reinterpret_cast<const char *
>(data.data()),
86 std::min(headers_end, data.size()));
89 const std::string content_length_key =
"content-length:";
92 while (pos < headers_str.size()) {
94 std::size_t line_start = pos;
95 std::size_t line_end = headers_str.find(
"\r\n", pos);
96 if (line_end == std::string::npos) {
100 std::string line = headers_str.substr(line_start, line_end - line_start);
103 std::string line_lower = line;
104 std::transform(line_lower.begin(), line_lower.end(), line_lower.begin(),
105 [](
unsigned char c) { return std::tolower(c); });
108 if (line_lower.find(content_length_key) == 0) {
110 std::size_t colon_pos = line.find(
':');
111 if (colon_pos != std::string::npos) {
112 std::string value = line.substr(colon_pos + 1);
114 value.erase(0, value.find_first_not_of(
" \t"));
115 value.erase(value.find_last_not_of(
" \t\r\n") + 1);
118 return std::stoull(value);
156 tcp_server_->set_receive_callback(
158 std::shared_ptr<kcenon::network::session::messaging_session> session,
159 const std::vector<uint8_t> &data) {
165 std::unique_lock<std::mutex> lock(buffers_mutex_);
166 auto &buffer = session_buffers_[session];
170 if (!buffer.append(data)) {
173 session_buffers_.erase(session);
178 auto error_response =
179 create_error_response(413,
"Payload Too Large");
182 session->send_packet(std::move(response_data));
184 auto error_response =
185 create_error_response(431,
"Request Header Fields Too Large");
188 session->send_packet(std::move(response_data));
194 if (buffer.is_complete()) {
196 auto request_result =
201 session_buffers_.erase(session);
204 if (request_result.is_err()) {
206 auto error_response = create_error_response(400,
"Bad Request");
209 session->send_packet(std::move(response_data));
211 session->stop_session();
215 auto http_request = std::move(request_result.value());
218 auto response = process_http_request(http_request);
221 bool close_conn = should_close_connection(http_request, response);
226 session->send_packet(std::move(response_data));
233 session->stop_session();
240 return tcp_server_->start_server(port);
286 not_found_handler_ = std::move(handler);
290 error_handler_ = std::move(handler);
295 std::lock_guard<std::mutex> lock(error_handlers_mutex_);
296 error_handlers_[code] = std::move(handler);
300 std::lock_guard<std::mutex> lock(error_handlers_mutex_);
301 default_error_handler_ = std::move(handler);
306 request_timeout_ = timeout;
310 use_json_errors_ = enable;
317 std::lock_guard<std::mutex> lock(error_handlers_mutex_);
318 auto it = error_handlers_.find(
error.code);
319 if (it != error_handlers_.end()) {
320 return it->second(
error);
324 if (default_error_handler_) {
325 return default_error_handler_(
error);
330 if (use_json_errors_) {
337 const std::string &pattern,
342 route.
handler = std::move(handler);
345 auto regex_str = pattern_to_regex(pattern, route.
param_names);
348 std::lock_guard<std::mutex> lock(routes_mutex_);
349 routes_.push_back(std::move(route));
354 std::map<std::string, std::string> &path_params)
const
358 auto &mutable_mutex =
const_cast<std::mutex &
>(routes_mutex_);
359 std::lock_guard<std::mutex> lock(mutable_mutex);
361 for (
const auto &route : routes_) {
362 if (route.matches(method, path, path_params)) {
371 -> std::vector<uint8_t> {
374 if (request_result.is_err()) {
376 auto error_response = create_error_response(400,
"Bad Request");
380 auto http_request = std::move(request_result.value());
384 ctx.
request = std::move(http_request);
395 response = route->handler(ctx);
398 response = not_found_handler_(ctx);
400 }
catch (
const std::exception &e) {
402 response = error_handler_(ctx);
405 response = error_handler_(ctx);
410 response.
set_header(
"Content-Length", std::to_string(response.
body.size()));
414 response.
set_header(
"Server",
"NetworkSystem-HTTP-Server/1.0");
436 response = route->handler(ctx);
439 response = not_found_handler_(ctx);
441 }
catch (
const std::exception &e) {
443 response = error_handler_(ctx);
446 response = error_handler_(ctx);
451 response.
set_header(
"Content-Length", std::to_string(response.
body.size()));
455 response.
set_header(
"Server",
"NetworkSystem-HTTP-Server/1.0");
462 auto conn = http_request.get_header(
"Connection");
463 if (conn && *conn ==
"keep-alive") {
464 response.
set_header(
"Connection",
"keep-alive");
470 auto conn = http_request.get_header(
"Connection");
471 if (conn && *conn ==
"close") {
479 apply_compression(http_request, response);
488 auto response_conn = response.get_header(
"Connection");
490 std::string conn_lower = *response_conn;
491 std::transform(conn_lower.begin(), conn_lower.end(), conn_lower.begin(),
492 [](
unsigned char c) { return std::tolower(c); });
493 if (conn_lower ==
"close") {
496 if (conn_lower ==
"keep-alive") {
502 auto request_conn = request.get_header(
"Connection");
504 std::string conn_lower = *request_conn;
505 std::transform(conn_lower.begin(), conn_lower.end(), conn_lower.begin(),
506 [](
unsigned char c) { return std::tolower(c); });
507 if (conn_lower ==
"close") {
528 return build_error_response(
error);
532 std::vector<std::string> ¶m_names)
536 std::ostringstream regex_str;
540 while (pos < pattern.length()) {
542 auto param_start = pattern.find(
':', pos);
544 if (param_start == std::string::npos) {
546 auto remaining = pattern.substr(pos);
548 for (
char c : remaining) {
549 if (c ==
'.' || c ==
'*' || c ==
'+' || c ==
'?' || c ==
'[' ||
550 c ==
']' || c ==
'(' || c ==
')' || c ==
'{' || c ==
'}' ||
551 c ==
'^' || c ==
'$' || c ==
'|' || c ==
'\\') {
560 for (std::size_t i = pos; i < param_start; ++i) {
562 if (c ==
'.' || c ==
'*' || c ==
'+' || c ==
'?' || c ==
'[' ||
563 c ==
']' || c ==
'(' || c ==
')' || c ==
'{' || c ==
'}' ||
564 c ==
'^' || c ==
'$' || c ==
'|' || c ==
'\\') {
571 auto param_end = param_start + 1;
572 while (param_end < pattern.length() &&
573 (std::isalnum(pattern[param_end]) || pattern[param_end] ==
'_')) {
579 pattern.substr(param_start + 1, param_end - param_start - 1);
580 param_names.push_back(param_name);
583 regex_str <<
"([^/]+)";
590 return regex_str.str();
594 std::lock_guard<std::mutex> lock(compression_mutex_);
595 compression_enabled_ = enable;
599 std::lock_guard<std::mutex> lock(compression_mutex_);
600 compression_threshold_ = threshold_bytes;
610 std::string accept_lower = accept_encoding;
611 std::transform(accept_lower.begin(), accept_lower.end(), accept_lower.begin(),
612 [](
unsigned char c) { return std::tolower(c); });
615 if (accept_lower.find(
"gzip") != std::string::npos) {
620 if (accept_lower.find(
"deflate") != std::string::npos) {
632 std::lock_guard<std::mutex> lock(compression_mutex_);
633 if (!compression_enabled_) {
637 if (response.body.size() < compression_threshold_) {
643 auto accept_encoding = request.get_header(
"Accept-Encoding");
644 if (!accept_encoding) {
649 auto algorithm = choose_compression_algorithm(*accept_encoding);
655 const size_t original_size = response.body.size();
658 auto pipeline = std::make_shared<utils::compression_pipeline>(algorithm);
661 auto compressed_result = pipeline->compress(response.body);
663 if (compressed_result.is_err()) {
670 const size_t compressed_size = compressed_result.value().size();
673 response.body = std::move(compressed_result.value());
677 response.set_header(
"Content-Encoding",
"gzip");
679 response.set_header(
"Content-Encoding",
"deflate");
683 response.set_header(
"Content-Length", std::to_string(response.body.size()));
687 (void)compressed_size;
http_handler not_found_handler_
auto options(const std::string &pattern, http_handler handler) -> void
Register OPTIONS route handler.
auto start(unsigned short port) -> VoidResult
Start HTTP server on specified port.
auto get(const std::string &pattern, http_handler handler) -> void
Register GET route handler.
http_server(const std::string &server_id)
Construct HTTP server with server ID.
auto should_close_connection(const internal::http_request &request, const internal::http_response &response) const -> bool
Determine if connection should be closed after response.
auto set_not_found_handler(http_handler handler) -> void
Set custom 404 Not Found handler.
auto find_route(internal::http_method method, const std::string &path, std::map< std::string, std::string > &path_params) const -> const http_route *
Find matching route for request.
auto create_error_response(int status_code, const std::string &message) -> internal::http_response
Create default error response.
auto del(const std::string &pattern, http_handler handler) -> void
Register DELETE route handler.
static auto pattern_to_regex(const std::string &pattern, std::vector< std::string > ¶m_names) -> std::string
Convert route pattern to regex pattern.
~http_server()
Destructor - stops server if running.
auto set_json_error_responses(bool enable) -> void
Enable JSON format for error responses.
auto process_http_request(const internal::http_request &request) -> internal::http_response
Process HTTP request and generate response object.
auto set_request_timeout(std::chrono::milliseconds timeout) -> void
Set request timeout duration.
auto stop() -> VoidResult
Stop HTTP server.
auto set_compression_enabled(bool enable) -> void
Enable automatic response compression.
auto handle_request(const std::vector< uint8_t > &request_data) -> std::vector< uint8_t >
Handle incoming HTTP request.
auto register_route(internal::http_method method, const std::string &pattern, http_handler handler) -> void
Register route with method and pattern.
auto wait_for_stop() -> void
Wait for server to stop (blocking)
auto post(const std::string &pattern, http_handler handler) -> void
Register POST route handler.
auto put(const std::string &pattern, http_handler handler) -> void
Register PUT route handler.
auto build_error_response(const internal::http_error &error) -> internal::http_response
Build error response using registered handlers.
http_handler error_handler_
auto set_error_handler(http_handler handler) -> void
Set custom 500 Internal Server Error handler.
auto choose_compression_algorithm(const std::string &accept_encoding) const -> utils::compression_algorithm
Determine compression algorithm from Accept-Encoding header.
auto head(const std::string &pattern, http_handler handler) -> void
Register HEAD route handler.
auto apply_compression(const internal::http_request &request, internal::http_response &response) -> void
Apply compression to response if appropriate.
auto set_compression_threshold(size_t threshold_bytes) -> void
Set minimum response size for compression.
auto set_default_error_handler(error_handler handler) -> void
Set default error handler for all unhandled error codes.
std::shared_ptr< messaging_server > tcp_server_
auto patch(const std::string &pattern, http_handler handler) -> void
Register PATCH route handler.
A server class that manages incoming TCP connections, creating messaging_session instances for each a...
static auto build_json_error(const http_error &error) -> http_response
Build JSON format error response (RFC 7807)
static auto build_html_error(const http_error &error) -> http_response
Build HTML format error response.
static auto make_error(http_error_code code, const std::string &detail="", const std::string &request_id="") -> http_error
Create http_error from error code.
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.
Messaging session managing bidirectional message exchange.
std::function< internal::http_response(const http_request_context &ctx)> http_handler
Handler function for HTTP requests.
std::function< internal::http_response(const internal::http_error &error)> error_handler
Handler function for HTTP errors.
http_error_code
Standard HTTP error codes (RFC 7231)
http_method
HTTP request methods (verbs)
compression_algorithm
Supported compression algorithms.
auto append(const std::vector< uint8_t > &chunk) -> bool
Append new data chunk to buffer.
static auto parse_content_length(const std::vector< uint8_t > &data, std::size_t headers_end) -> std::size_t
Parse Content-Length from headers.
std::vector< uint8_t > data
std::size_t content_length
auto is_complete() const -> bool
Check if complete HTTP request has been received.
static constexpr std::size_t MAX_REQUEST_SIZE
static auto find_header_end(const std::vector< uint8_t > &data) -> std::size_t
Find end of HTTP headers (\r\n\r\n marker)
std::size_t headers_end_pos
Context for an HTTP request with parsed components.
internal::http_request request
std::map< std::string, std::string > path_params
Route definition with pattern matching and handler.
auto matches(internal::http_method method, const std::string &path, std::map< std::string, std::string > &path_params) const -> bool
Check if request matches this route.
std::vector< std::string > param_names
internal::http_method method
Structured HTTP error information.
Represents an HTTP request message.
Represents an HTTP response message.
auto set_header(const std::string &name, const std::string &value) -> void
Set a header value.
auto get_header(const std::string &name) const -> std::optional< std::string >
Get the value of a header (case-insensitive)
std::vector< uint8_t > body