[2026] C++ HTTP Fundamentals Complete Guide | Request/Response Parsing, Headers, Chunked Encoding, Beast [#30-1]
이 글의 핵심
Master C++ HTTP: RFC-compliant parsing, headers, chunked encoding, Boost.Beast, common errors, and production patterns.
Introduction: “HTTP request parsing is full of bugs”
Problem Scenario 1: Manual Parsing Pitfalls
아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ❌ Problem: manual parsing crashes on edge cases
std::string parse_path(const std::string& raw) {
auto pos = raw.find(" ");
auto pos2 = raw.find(" ", pos + 1);
return raw.substr(pos + 1, pos2 - pos - 1); // 💥 Multiple spaces? Empty string?
}
// Issues:
// - HTTP/1.0 vs HTTP/1.1 differences
// - Consecutive spaces, tabs, CRLF vs LF mixing
// - Multi-byte characters (Content-Length vs actual bytes)
// - Chunked encoding: Transfer-Encoding: chunked handling missing
Why does this happen?
HTTP protocol looks simple but has many edge cases: \r\n vs \n, consecutive spaces, percent encoding, chunked encoding, Keep-Alive, etc. Manual parsing accumulates bugs.
Additional Problem Scenarios
Scenario 2: Content-Length and body mismatch
Client sends Content-Length: 100 but only 50 bytes arrive—async_read waits forever without timeout, blocking server threads.
Scenario 3: Chunked encoding parsing failure
Streaming responses with Transfer-Encoding: chunked not handled—body truncated. Essential for large file downloads and real-time streaming.
Scenario 4: Header case and duplicates
Content-Type vs content-type, multiple Set-Cookie headers not handled—parsing errors or security vulnerabilities.
Scenario 5: Request split across TCP packets
One request arrives over multiple async_read_some calls. Failure to detect “request complete” passes wrong data to next request.
Solution:
- Boost.Beast: RFC-compliant parser, built-in error handling
- flat_buffer: preserves data during parsing
- http::read: automatically determines request/response completion
- chunked encoding: Beast handles automatically Goals:
- Understand HTTP request/response structure completely
- Header parsing (case-insensitive, duplicates, encoding)
- Chunked encoding (Transfer-Encoding: chunked)
- Beast parser usage
- Common errors and solutions
- Best practices and production patterns Requirements: Boost.Beast 1.70+, Boost.Asio 1.70+ What you’ll learn:
- Precise HTTP protocol structure
- Safe request/response parsing with Beast
- Production-grade HTTP server/client foundations
Conceptual Analogy
Sockets and async I/O are like mailbox addresses and delivery routes. With correct address (IP·port), data arrives, and Asio is like one post office with multiple carriers (threads·handlers) dividing work.
Table of Contents
- HTTP Protocol Structure
- Request Parsing
- Response Parsing
- Header Handling
- Chunked Encoding
- Beast-Based Complete Parser
- Common Errors and Solutions
- Best Practices
- Production Patterns
1. HTTP Protocol Structure
Request/Response Flow
아래 코드는 mermaid를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
sequenceDiagram
participant C as Client
participant S as Server
C->>S: Request Line + Headers + CRLF + Body
Note over S: Parse → Route → Process
S->>C: Status Line + Headers + CRLF + Body
HTTP Request Structure
아래 코드는 text를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
GET /api/users?id=1 HTTP/1.1\r\n
Host: example.com\r\n
Content-Type: application/json\r\n
Content-Length: 0\r\n
\r\n
Components:
- Request Line:
METHOD SP Request-URI SP HTTP-Version CRLF - Headers:
Field-Name: Field-Value CRLF(repeated) - Blank line:
CRLF(separates headers from body) - Body: length determined by
Content-LengthorTransfer-Encoding: chunked
HTTP Response Structure
아래 코드는 text를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
HTTP/1.1 200 OK\r\n
Content-Type: application/json\r\n
Content-Length: 27\r\n
\r\n
{"message":"Hello World"}
Components:
- Status Line:
HTTP-Version SP Status-Code SP Reason-Phrase CRLF - Headers: same format as request
- Blank line: separates headers from body
- Body: response content
HTTP Message Parsing Visualization
아래 코드는 mermaid를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
flowchart TB
subgraph Request[HTTP Request]
RL[Request Line\nGET /path HTTP/1.1]
H1[Headers\nHost: example.com\nContent-Type: ...]
BL[Blank Line CRLF]
BD[Body\nContent Data]
end
RL --> H1 --> BL --> BD
style RL fill:#4caf50
style BL fill:#ff9800
2. Request Parsing
Request Line Parsing
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <string>
#include <sstream>
#include <stdexcept>
struct ParsedRequestLine {
std::string method; // GET, POST, ...
std::string path; // /api/users
std::string query; // id=1 (query string)
std::string version; // HTTP/1.1
};
ParsedRequestLine parse_request_line(const std::string& line) {
std::istringstream iss(line);
ParsedRequestLine result;
// METHOD SP Request-URI SP HTTP-Version
if (!(iss >> result.method >> result.path >> result.version)) {
throw std::runtime_error("Invalid request line");
}
// Split query string: /api/users?id=1 → path=/api/users, query=id=1
auto qpos = result.path.find('?');
if (qpos != std::string::npos) {
result.query = result.path.substr(qpos + 1);
result.path = result.path.substr(0, qpos);
}
return result;
}
// Usage
int main() {
auto parsed = parse_request_line("GET /api/users?id=1 HTTP/1.1");
// parsed.method == "GET"
// parsed.path == "/api/users"
// parsed.query == "id=1"
// parsed.version == "HTTP/1.1"
}
Note: Production requires percent decoding (%20 → space) and path traversal attack prevention (/../../../etc/passwd). Beast handles these.
Headers and Body Separation
다음은 cpp를 활용한 상세한 구현 코드입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// CRLF twice = end of headers
std::pair<std::string, std::string> split_headers_and_body(
const std::string& raw)
{
// Find \r\n\r\n or \n\n (some clients use LF only)
const std::string crlfcrlf = "\r\n\r\n";
const std::string lflf = "\n\n";
auto pos = raw.find(crlfcrlf);
if (pos == std::string::npos) {
pos = raw.find(lflf);
}
if (pos == std::string::npos) {
return {"", ""}; // Still receiving headers
}
size_t header_end = (raw.find(crlfcrlf) != std::string::npos)
? pos + crlfcrlf.size()
: pos + lflf.size();
return {
raw.substr(0, pos),
raw.substr(header_end)
};
}
Content-Length Based Body Reading
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <cstdlib>
#include <optional>
std::optional<size_t> get_content_length(const std::string& headers) {
// Extract 123 from Content-Length: 123
const std::string key = "Content-Length:";
auto pos = headers.find(key);
if (pos == std::string::npos) {
return std::nullopt; // No body or chunked
}
pos += key.size();
while (pos < headers.size() && headers[pos] == ' ') ++pos;
char* end;
long value = std::strtol(headers.c_str() + pos, &end, 10);
if (value < 0 || end == headers.c_str() + pos) {
return std::nullopt; // Invalid format
}
return static_cast<size_t>(value);
}
3. Response Parsing
Status Line Parsing
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
struct ParsedStatusLine {
std::string version; // HTTP/1.1
int status_code; // 200, 404, ...
std::string reason; // OK, Not Found, ...
};
ParsedStatusLine parse_status_line(const std::string& line) {
std::istringstream iss(line);
ParsedStatusLine result;
if (!(iss >> result.version >> result.status_code)) {
throw std::runtime_error("Invalid status line");
}
std::getline(iss, result.reason); // Rest: " OK\r" or " OK"
// Trim whitespace
result.reason.erase(0, result.reason.find_first_not_of(" \t\r\n"));
result.reason.erase(result.reason.find_last_not_of(" \t\r\n") + 1);
return result;
}
// Usage
// parse_status_line("HTTP/1.1 200 OK") → 200, "OK"
// parse_status_line("HTTP/1.1 404 Not Found") → 404, "Not Found"
Response Body Reading Strategy
다음은 cpp를 활용한 상세한 구현 코드입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// Body reading strategy
enum class BodyReadStrategy {
NoBody, // HEAD, 204, 304, etc.
ContentLength, // Content-Length present
Chunked, // Transfer-Encoding: chunked
UntilClose // HTTP/1.0, read until connection close
};
BodyReadStrategy determine_strategy(
int status_code,
const std::string& method,
const std::map<std::string, std::string>& headers)
{
if (method == "HEAD" || status_code == 204 || status_code == 304) {
return BodyReadStrategy::NoBody;
}
auto it = headers.find("transfer-encoding");
if (it != headers.end() &&
it->second.find("chunked") != std::string::npos) {
return BodyReadStrategy::Chunked;
}
if (headers.count("content-length")) {
return BodyReadStrategy::ContentLength;
}
return BodyReadStrategy::UntilClose; // HTTP/1.0 fallback
}
4. Header Handling
Header Parsing (Case-Insensitive)
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <map>
#include <algorithm>
#include <cctype>
std::map<std::string, std::string> parse_headers(const std::string& header_block) {
std::map<std::string, std::string> headers;
std::istringstream iss(header_block);
std::string line;
while (std::getline(iss, line) && !line.empty() &&
(line.back() == '\r' ? (line.pop_back(), true) : true)) {
auto colon = line.find(':');
if (colon == std::string::npos) continue;
std::string name = line.substr(0, colon);
std::string value = line.substr(colon + 1);
// Trim whitespace
value.erase(0, value.find_first_not_of(" \t"));
value.erase(value.find_last_not_of(" \t\r\n") + 1);
// Normalize header name to lowercase (HTTP headers are case-insensitive)
std::transform(name.begin(), name.end(), name.begin(),
[](unsigned char c) { return std::tolower(c); });
// Multiple headers with same name: Set-Cookie, etc. need special handling
if (headers.count(name)) {
headers[name] += ", " + value; // Simple merge
} else {
headers[name] = value;
}
}
return headers;
}
Key Headers
| Header | Purpose | Example |
|---|---|---|
Content-Type | Body MIME type | application/json, text/html |
Content-Length | Body byte count | 1024 |
Transfer-Encoding | Transfer encoding | chunked |
Host | Request target host | example.com:8080 |
Connection | Connection persistence | keep-alive, close |
Accept-Encoding | Compression support | gzip, deflate, br |
Content-Type Parsing (MIME + charset)
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
struct ParsedContentType {
std::string media_type; // application/json
std::string charset; // utf-8 (if present)
};
ParsedContentType parse_content_type(const std::string& value) {
ParsedContentType result;
auto semicolon = value.find(';');
result.media_type = value.substr(0, semicolon);
result.media_type.erase(0, result.media_type.find_first_not_of(" \t"));
result.media_type.erase(result.media_type.find_last_not_of(" \t") + 1);
if (semicolon != std::string::npos) {
std::string rest = value.substr(semicolon + 1);
auto eq = rest.find('=');
if (eq != std::string::npos) {
std::string key = rest.substr(0, eq);
std::string val = rest.substr(eq + 1);
// Remove whitespace and quotes
val.erase(0, val.find_first_not_of(" \t\""));
val.erase(val.find_last_not_of(" \t\"") + 1);
if (key.find("charset") != std::string::npos) {
result.charset = val;
}
}
}
return result;
}
5. Chunked Encoding
Chunk Format
아래 코드는 text를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
5\r\n
Hello\r\n
6\r\n
World\r\n
0\r\n
\r\n
Format: [hex size]\r\n[data]\r\n repeated, ending with 0\r\n\r\n
Chunk Decoding Implementation
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <vector>
#include <cctype>
std::pair<std::vector<char>, size_t> decode_chunk(
const char* data, size_t size, size_t& consumed)
{
std::vector<char> body;
consumed = 0;
const char* p = data;
const char* end = data + size;
while (p < end) {
// Read chunk size (hex)
if (p + 2 > end) break; // Need at least "0\r\n"
char* hex_end;
unsigned long chunk_size = std::strtoul(p, &hex_end, 16);
p = hex_end;
// Skip \r\n
if (p + 2 > end) break;
if (p[0] != '\r' || p[1] != '\n') {
throw std::runtime_error("Invalid chunk: expected CRLF");
}
p += 2;
consumed = p - data;
if (chunk_size == 0) {
// Last chunk, may have trailing \r\n
if (p + 2 <= end && p[0] == '\r' && p[1] == '\n') {
consumed += 2;
}
break;
}
// Chunk data
if (p + chunk_size + 2 > end) {
break; // Insufficient data
}
body.insert(body.end(), p, p + chunk_size);
p += chunk_size;
consumed = p - data;
if (p[0] != '\r' || p[1] != '\n') {
throw std::runtime_error("Invalid chunk: expected CRLF after data");
}
p += 2;
consumed = p - data;
}
return {body, consumed};
}
Chunked Encoding Visualization
아래 코드는 mermaid를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
flowchart LR
subgraph Chunked[Chunked Encoding]
C1[5\r\nHello\r\n]
C2["6\r\n World\r\n"]
C3[0\r\n\r\n]
end
C1 --> C2 --> C3
subgraph Decoded[Decoded Result]
D["Hello World"]
end
Chunked -->|decode_chunk| Decoded
6. Beast-Based Complete Parser
Reading HTTP Request with Beast
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <boost/beast.hpp>
#include <boost/asio.hpp>
namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
using tcp = net::ip::tcp;
void read_http_request(tcp::socket& socket) {
beast::flat_buffer buffer;
http::request<http::string_body> req;
beast::error_code ec;
http::read(socket, buffer, req, ec);
if (ec) {
if (ec == http::error::end_of_stream) {
// Connection closed (normal)
return;
}
std::cerr << "Read error: " << ec.message() << "\n";
return;
}
// Use parsed request
std::cout << "Method: " << req.method_string() << "\n";
std::cout << "Path: " << req.target() << "\n";
std::cout << "Version: " << req.version() << "\n";
for (const auto& field : req) {
std::cout << field.name() << ": " << field.value() << "\n";
}
std::cout << "Body: " << req.body() << "\n";
}
Reading HTTP Response with Beast (Automatic Chunked Handling)
다음은 cpp를 활용한 상세한 구현 코드입니다. 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
void read_http_response(beast::tcp_stream& stream) {
beast::flat_buffer buffer;
http::response_parser<http::string_body> parser;
parser.body_limit(std::numeric_limits<std::uint64_t>::max()); // Body limit
beast::error_code ec;
http::read(stream, buffer, parser, ec);
if (ec) {
std::cerr << "Read error: " << ec.message() << "\n";
return;
}
auto res = parser.get();
std::cout << "Status: " << res.result_int() << "\n";
std::cout << "Body: " << res.body() << "\n";
// Beast automatically decodes Transfer-Encoding: chunked
}
Async Request Reading
다음은 cpp를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
void do_read_async(beast::tcp_stream& stream,
std::function<void(http::request<http::string_body>)> on_request)
{
auto buffer = std::make_shared<beast::flat_buffer>();
auto req = std::make_shared<http::request<http::string_body>>();
http::async_read(stream, *buffer, *req,
[&stream, buffer, req, on_request](beast::error_code ec, std::size_t) {
if (ec) {
if (ec != http::error::end_of_stream) {
std::cerr << "Read error: " << ec.message() << "\n";
}
return;
}
on_request(std::move(*req));
});
}
HTTP Response Generation and Sending
다음은 cpp를 활용한 상세한 구현 코드입니다. 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
void send_json_response(beast::tcp_stream& stream,
unsigned status, const std::string& json_body)
{
http::response<http::string_body> res{http::status::ok, 11};
res.set(http::field::server, "MyServer/1.0");
res.set(http::field::content_type, "application/json");
res.body() = json_body;
res.prepare_payload(); // Auto-set Content-Length
if (status != 200) {
res.result(static_cast<http::status>(status));
}
beast::error_code ec;
http::write(stream, res, ec);
if (ec) {
std::cerr << "Write error: " << ec.message() << "\n";
}
}
Complete HTTP Server Example (Beast)
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <boost/beast.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <memory>
namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
using tcp = net::ip::tcp;
class HttpSession : public std::enable_shared_from_this<HttpSession> {
beast::tcp_stream stream_;
beast::flat_buffer buffer_;
http::request<http::string_body> req_;
public:
explicit HttpSession(tcp::socket socket)
: stream_(std::move(socket)) {}
void start() { do_read(); }
private:
void do_read() {
req_ = {};
buffer_.consume(buffer_.size());
auto self = shared_from_this();
http::async_read(stream_, buffer_, req_,
[self, this](beast::error_code ec, std::size_t) {
if (ec) {
if (ec != http::error::end_of_stream)
std::cerr << "read: " << ec.message() << "\n";
return;
}
handle_request();
});
}
void handle_request() {
http::response<http::string_body> res{http::status::ok, req_.version()};
res.set(http::field::server, "Beast-HTTP-Server");
res.set(http::field::content_type, "text/plain");
if (req_.method() == http::verb::get && req_.target() == "/") {
res.body() = "Hello, World!";
} else if (req_.method() == http::verb::get &&
req_.target().starts_with("/api/")) {
res.set(http::field::content_type, "application/json");
res.body() = "{\"message\":\"API response\"}";
} else {
res.result(http::status::not_found);
res.body() = "Not Found";
}
res.prepare_payload();
auto self = shared_from_this();
http::async_write(stream_, res,
[self, this](beast::error_code ec, std::size_t) {
if (!ec) {
if (req_.keep_alive()) {
do_read(); // Keep-Alive: next request
}
}
});
}
};
int main() {
net::io_context ioc;
tcp::acceptor acceptor(ioc, {tcp::v4(), 8080});
std::function<void()> do_accept;
do_accept = [&]() {
acceptor.async_accept(
[&](beast::error_code ec, tcp::socket socket) {
if (!ec) {
std::make_shared<HttpSession>(std::move(socket))->start();
}
do_accept();
});
};
do_accept();
std::cout << "HTTP server on :8080\n";
ioc.run();
}
7. Common Errors and Solutions
Problem 1: “end_of_stream” or “connection reset”
Cause: Client disconnects mid-request (browser refresh, timeout). Solution: 아래 코드는 cpp를 사용한 구현 예제입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
http::async_read(stream_, buffer_, req_,
[self, this](beast::error_code ec, std::size_t) {
if (ec) {
if (ec == http::error::end_of_stream ||
ec == net::error::connection_reset) {
// Treat as normal connection close
return;
}
std::cerr << "read error: " << ec.message() << "\n";
return;
}
handle_request();
});
Problem 2: “body limit exceeded”
Cause: Request body exceeds body_limit (DoS prevention default).
Solution:
http::request_parser<http::string_body> parser;
parser.body_limit(10 * 1024 * 1024); // 10MB
http::async_read(stream_, buffer_, parser, ...);
Problem 3: “partial message” or infinite read wait
Cause: Content-Length mismatch with actual body, or chunked encoding parsing error.
Solution:
- Beast handles automatically. For manual parsing, validate
Content-Length. - Set timeout to prevent infinite wait:
stream_.expires_after(std::chrono::seconds(30));
http::async_read(stream_, buffer_, req_, handler);
Problem 4: Keep-Alive next request parsing failure
Cause: Multiple requests on one connection, previous request buffer not cleared. Solution: 아래 코드는 cpp를 사용한 구현 예제입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
void do_read() {
req_ = {}; // Reset request
buffer_.consume(buffer_.size()); // Clear buffer
http::async_read(stream_, buffer_, req_, ...);
}
Problem 5: Header Injection (CRLF Injection)
Cause: User input directly in headers allows \r\n to inject new headers.
Solution:
아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ❌ Dangerous
res.set("X-Custom", user_input);
// ✅ Safe: remove CRLF
std::string safe_value = user_input;
safe_value.erase(
std::remove(safe_value.begin(), safe_value.end(), '\r'),
safe_value.end());
safe_value.erase(
std::remove(safe_value.begin(), safe_value.end(), '\n'),
safe_value.end());
res.set("X-Custom", safe_value);
Problem 6: Large Body Memory Explosion
Cause: string_body for 1GB file upload uses 1GB memory.
Solution: Use dynamic_body or file_body:
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
http::request<http::dynamic_body> req;
// Or
http::request_parser<http::file_body> parser;
parser.body_limit(100 * 1024 * 1024); // 100MB
boost::beast::file_mode mode = boost::beast::file_mode::write;
parser.get().body().open("/tmp/upload.dat", mode);
8. Best Practices
1. Always Use Beast
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// ❌ Manual parsing: edge case bugs
std::string path = extract_path(raw_request);
// ✅ Beast: RFC-compliant, validated
http::request<http::string_body> req;
http::read(socket, buffer, req);
std::string path = std::string(req.target());
2. Set body_limit
http::request_parser<http::string_body> parser;
parser.body_limit(1024 * 1024); // 1MB limit (upload size limit)
3. Set Timeout
stream_.expires_after(std::chrono::seconds(30));
4. Call prepare_payload()
res.body() = "Hello";
res.prepare_payload(); // Auto-set Content-Length
5. Handle Keep-Alive
아래 코드는 cpp를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
if (req.keep_alive()) {
res.keep_alive(true);
do_read(); // Wait for next request
} else {
res.keep_alive(false);
stream_.socket().shutdown(tcp::socket::shutdown_send);
}
6. Consistent Error Responses
다음은 cpp를 활용한 상세한 구현 코드입니다. 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
std::string escape_json(const std::string& s) {
std::string out;
for (char c : s) {
if (c == '"') out += "\\\"";
else if (c == '\\') out += "\\\\";
else if (c == '\n') out += "\\n";
else if (c == '\r') out += "\\r";
else out += c;
}
return out;
}
void send_error(beast::tcp_stream& stream, unsigned status,
const std::string& message)
{
http::response<http::string_body> res{
static_cast<http::status>(status), 11};
res.set(http::field::content_type, "application/json");
res.body() = "{\"error\":\"" + escape_json(message) + "\"}";
res.prepare_payload();
http::write(stream, res);
}
9. Production Patterns
Pattern 1: Request Logging Middleware
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
void log_request(const http::request<http::string_body>& req) {
auto now = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(now);
std::cerr << std::put_time(std::localtime(&time), "%Y-%m-%d %H:%M:%S")
<< " " << req.method_string() << " " << req.target()
<< " " << req.version() << "\n";
}
Pattern 2: Request Size Limits (Rate Limiting)
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
constexpr size_t MAX_HEADER_SIZE = 8 * 1024; // 8KB
constexpr size_t MAX_BODY_SIZE = 10 * 1024 * 1024; // 10MB
http::request_parser<http::string_body> parser;
parser.header_limit(MAX_HEADER_SIZE);
parser.body_limit(MAX_BODY_SIZE);
Pattern 3: Graceful Shutdown
다음은 cpp를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
std::atomic<bool> shutdown_requested{false};
void do_accept() {
if (shutdown_requested) return;
acceptor_.async_accept(
[this](beast::error_code ec, tcp::socket socket) {
if (shutdown_requested) return;
if (!ec) {
std::make_shared<HttpSession>(std::move(socket))->start();
}
do_accept();
});
}
// SIGINT handler
void on_signal() {
shutdown_requested = true;
acceptor_.close();
}
Pattern 4: Connection Pool (Client)
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class HttpClientPool {
net::io_context& ioc_;
std::queue<std::unique_ptr<beast::tcp_stream>> pool_;
std::mutex mtx_;
tcp::resolver::results_type endpoints_;
public:
void get_connection(std::function<void(beast::tcp_stream&)> callback) {
std::unique_lock lock(mtx_);
if (!pool_.empty()) {
auto stream = std::move(pool_.front());
pool_.pop();
lock.unlock();
callback(*stream);
return;
}
lock.unlock();
auto stream = std::make_unique<beast::tcp_stream>(ioc_);
stream->async_connect(endpoints_,
[this, cb = std::move(callback), s = stream.get()]
(beast::error_code ec) {
if (!ec) cb(*s);
});
}
void release_connection(std::unique_ptr<beast::tcp_stream> stream) {
std::lock_guard lock(mtx_);
pool_.push(std::move(stream));
}
};
Pattern 5: Health Check Endpoint
아래 코드는 cpp를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
if (req.target() == "/health") {
res.result(http::status::ok);
res.set(http::field::content_type, "application/json");
res.body() = "{\"status\":\"ok\"}";
res.prepare_payload();
// Skip DB/cache checks for fast response
return;
}
Pattern 6: CORS Headers
아래 코드는 cpp를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
res.set("Access-Control-Allow-Origin", "*");
res.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
res.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
if (req.method() == http::verb::options) {
res.result(http::status::ok);
res.body() = "";
res.prepare_payload();
return; // Preflight response
}
Implementation Checklist
- Use Beast
http::read/http::write(avoid manual parsing) - Set
body_limit(DoS prevention) - Set
expires_aftertimeout - Call
prepare_payload() - Handle Keep-Alive (
buffer_.consume,req_ = {}) - Prevent CRLF injection (validate header values)
- Consistent error responses (JSON format)
- Logging middleware
- Graceful shutdown
References
- RFC 7230 - HTTP/1.1: Message Syntax and Routing
- RFC 7231 - HTTP/1.1: Semantics and Content
- Boost.Beast Documentation
Summary
Key Points
- HTTP structure: Request line/status line, headers, blank line, body
- Parsing: Beast handles edge cases (CRLF, chunked, Content-Length)
- Headers: Case-insensitive, handle duplicates
- Chunked encoding: Beast auto-decodes
- Best practices: Limits, timeouts, prepare_payload, Keep-Alive hygiene
- Production: Logging, CORS, health checks, connection pools
HTTP Message Components
| Component | Request | Response |
|---|---|---|
| First line | GET /path HTTP/1.1 | HTTP/1.1 200 OK |
| Headers | Host: example.com | Content-Type: application/json |
| Blank line | \r\n | \r\n |
| Body | JSON, form data, etc. | JSON, HTML, etc. |
Common Error Codes
| Code | Meaning | When |
|---|---|---|
| 200 | OK | Success |
| 201 | Created | POST success |
| 204 | No Content | DELETE success |
| 400 | Bad Request | Invalid input |
| 401 | Unauthorized | Auth failure |
| 404 | Not Found | Resource missing |
| 500 | Internal Server Error | Server error |
Related Articles
- C++ Multithreaded Network Server Guide
- C++ SSL/TLS Secure Communication
- C++ REST API Server Complete Guide
Keywords
C++ HTTP, Boost.Beast, HTTP parsing, chunked encoding, Asio, REST, HTTP/1.1, request/response One-line summary: Master C++ HTTP with Beast for RFC-compliant parsing, automatic chunked decoding, and production-ready server/client foundations.