Refactor to use format for http responses

This commit is contained in:
2025-09-03 22:45:59 -04:00
parent 978861c430
commit b2ffe3bfab
3 changed files with 81 additions and 58 deletions

View File

@@ -79,6 +79,8 @@
* - **Simple concatenation**: Basic string + number + string combinations * - **Simple concatenation**: Basic string + number + string combinations
* - **Compile-time optimization**: When all types/values known at compile time * - **Compile-time optimization**: When all types/values known at compile time
* - **Template contexts**: Where compile-time buffer sizing is beneficial * - **Template contexts**: Where compile-time buffer sizing is beneficial
* - **IMPORTANT**: Only works with compile-time string literals, NOT runtime
* const char*
* *
* ## Optimization Details: * ## Optimization Details:
* The function uses `ArenaAllocator::allocate_remaining_space()` to claim all * The function uses `ArenaAllocator::allocate_remaining_space()` to claim all
@@ -206,10 +208,12 @@ inline constexpr DoubleTerm term(double s) { return DoubleTerm(s); }
* optimized term writers for maximum speed. * optimized term writers for maximum speed.
* *
* ## Supported Types: * ## Supported Types:
* - **String literals**: C-style string literals and arrays * - **String literals**: C-style string literals and arrays ("Hello", "World")
* - **Integers**: All integral types (int, int64_t, uint32_t, etc.) * - **Integers**: All integral types (int, int64_t, uint32_t, etc.)
* - **Floating point**: double (uses high-precision Grisu2 algorithm) * - **Floating point**: double (uses high-precision Grisu2 algorithm)
* - **Custom types**: Via specialization of `detail::term()` * - **Custom types**: Via specialization of `detail::term()`
* - **NOT supported**: const char* variables, std::string, std::string_view
* variables
* *
* ## Performance Characteristics: * ## Performance Characteristics:
* - **Compile-time buffer sizing**: Buffer size calculated at compile time (no * - **Compile-time buffer sizing**: Buffer size calculated at compile time (no
@@ -245,16 +249,23 @@ inline constexpr DoubleTerm term(double s) { return DoubleTerm(s); }
* *
* ## When to Use: * ## When to Use:
* - **Hot paths**: Performance-critical code where formatting speed matters * - **Hot paths**: Performance-critical code where formatting speed matters
* - **Known types**: When argument types are known at compile time * - **Compile-time string literals**: All string arguments must be string
* literals (e.g., "Hello")
* - **Simple formatting**: Concatenation and basic type conversion * - **Simple formatting**: Concatenation and basic type conversion
* - **Template code**: Where compile-time optimization is beneficial * - **Template code**: Where compile-time optimization is beneficial
* - **CANNOT use runtime strings**: No const char*, std::string, or string_view
* variables
* *
* ## When to Use format() Instead: * ## When to Use format() Instead:
* - **Printf-style formatting**: When you need format specifiers like "%d", * - **Printf-style formatting**: When you need format specifiers like "%d",
* "%.2f" * "%.2f"
* - **Runtime flexibility**: When format strings come from variables/config * - **Runtime strings**: When you have const char*, std::string, or string_view
* - **Complex formatting**: When you need padding, precision, etc. * variables
* - **Convenience**: For quick debugging or non-critical paths * - **Dynamic content**: When format strings come from variables/config/user
* input
* - **Complex formatting**: When you need padding, precision, width specifiers
* - **Mixed literal/runtime**: When combining string literals with runtime
* string data
* *
* @note All arguments are passed by forwarding reference for optimal * @note All arguments are passed by forwarding reference for optimal
* performance * performance

View File

@@ -195,8 +195,9 @@ void HttpHandler::handlePostCommit(Connection &conn,
const char *error = state.commit_parser const char *error = state.commit_parser
? state.commit_parser->get_parse_error() ? state.commit_parser->get_parse_error()
: "No parser initialized"; : "No parser initialized";
std::string error_msg = "Parse failed: "; ArenaAllocator &arena = conn.get_arena();
error_msg += error ? error : "Unknown error"; std::string_view error_msg =
format(arena, "Parse failed: %s", error ? error : "Unknown error");
send_error_response(conn, 400, error_msg, state.connection_close); send_error_response(conn, 400, error_msg, state.connection_close);
return; return;
} }
@@ -319,57 +320,47 @@ void HttpHandler::handleNotFound(Connection &conn,
void HttpHandler::sendResponse(Connection &conn, int status_code, void HttpHandler::sendResponse(Connection &conn, int status_code,
std::string_view content_type, std::string_view content_type,
std::string_view body, bool close_connection) { std::string_view body, bool close_connection) {
[[maybe_unused]] ArenaAllocator &arena = conn.get_arena(); ArenaAllocator &arena = conn.get_arena();
// Build HTTP response using arena
std::string response;
response.reserve(256 + body.size());
response += "HTTP/1.1 ";
response += std::to_string(status_code);
response += " ";
// Status text
switch (status_code) {
case 200:
response += "OK";
break;
case 400:
response += "Bad Request";
break;
case 404:
response += "Not Found";
break;
case 500:
response += "Internal Server Error";
break;
default:
response += "Unknown";
break;
}
auto *state = static_cast<HttpConnectionState *>(conn.user_data); auto *state = static_cast<HttpConnectionState *>(conn.user_data);
response += "\r\n"; // Status text
response += "Content-Type: "; std::string_view status_text;
response += content_type; switch (status_code) {
response += "\r\n"; case 200:
response += "Content-Length: "; status_text = "OK";
response += std::to_string(body.size()); break;
response += "\r\n"; case 400:
response += "X-Response-ID: "; status_text = "Bad Request";
response += std::to_string(state->request_id); break;
response += "\r\n"; case 404:
status_text = "Not Found";
if (close_connection) { break;
response += "Connection: close\r\n"; case 500:
conn.close_after_send(); // Signal connection should be closed after sending status_text = "Internal Server Error";
} else { break;
response += "Connection: keep-alive\r\n"; default:
status_text = "Unknown";
break;
} }
response += "\r\n"; const char *connection_header = close_connection ? "close" : "keep-alive";
response += body;
std::string_view response =
format(arena,
"HTTP/1.1 %d %.*s\r\n"
"Content-Type: %.*s\r\n"
"Content-Length: %zu\r\n"
"X-Response-ID: %ld\r\n"
"Connection: %s\r\n"
"\r\n%.*s",
status_code, static_cast<int>(status_text.size()),
status_text.data(), static_cast<int>(content_type.size()),
content_type.data(), body.size(), state->request_id,
connection_header, static_cast<int>(body.size()), body.data());
if (close_connection) {
conn.close_after_send();
}
conn.append_message(response); conn.append_message(response);
} }
@@ -383,11 +374,11 @@ void HttpHandler::send_json_response(Connection &conn, int status_code,
void HttpHandler::send_error_response(Connection &conn, int status_code, void HttpHandler::send_error_response(Connection &conn, int status_code,
std::string_view message, std::string_view message,
bool close_connection) { bool close_connection) {
[[maybe_unused]] ArenaAllocator &arena = conn.get_arena(); ArenaAllocator &arena = conn.get_arena();
std::string json = R"({"error":")"; std::string_view json =
json += message; format(arena, R"({"error":"%.*s"})", static_cast<int>(message.size()),
json += R"("})"; message.data());
send_json_response(conn, status_code, json, close_connection); send_json_response(conn, status_code, json, close_connection);
} }

View File

@@ -102,6 +102,27 @@ auto addr = reinterpret_cast<uintptr_t>(ptr); // Pointer to integer conv
- **String views** with `std::string_view` to minimize unnecessary copying - **String views** with `std::string_view` to minimize unnecessary copying
- **Arena allocation** for efficient memory management (~1ns vs ~20-270ns for malloc) - **Arena allocation** for efficient memory management (~1ns vs ~20-270ns for malloc)
### String Formatting
- **Always use `format.hpp` functions** - formats directly into arena-allocated memory
- **Use `static_format()` for performance-sensitive code** - faster but less flexible than `format()`
- **Use `format()` function with arena allocator** for printf-style formatting
```cpp
// Most performance-sensitive - compile-time optimized concatenation
std::string_view response = static_format(arena,
"HTTP/1.1 ", status_code, " OK\r\n",
"Content-Length: ", body.size(), "\r\n",
"\r\n", body);
// Printf-style formatting - runtime flexible
ArenaAllocator& arena = conn.get_arena();
std::string_view response = format(arena,
"HTTP/1.1 %d OK\r\n"
"Content-Length: %zu\r\n"
"\r\n%.*s",
status_code, body.size(),
static_cast<int>(body.size()), body.data());
```
### Complexity Control ### Complexity Control
- **Encapsulation is the main tool for controlling complexity** - **Encapsulation is the main tool for controlling complexity**
- **Header files define the interface** - they are the contract with users of your code - **Header files define the interface** - they are the contract with users of your code