Refactor to use format for http responses
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
21
style.md
21
style.md
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user