[2026] C++ HTTP Fundamentals Complete Guide | Request/Response Parsing, Headers, Chunked Encoding, Beast [#30-1]

[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:

  1. Boost.Beast: RFC-compliant parser, built-in error handling
  2. flat_buffer: preserves data during parsing
  3. http::read: automatically determines request/response completion
  4. 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

  1. HTTP Protocol Structure
  2. Request Parsing
  3. Response Parsing
  4. Header Handling
  5. Chunked Encoding
  6. Beast-Based Complete Parser
  7. Common Errors and Solutions
  8. Best Practices
  9. 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-Length or Transfer-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

HeaderPurposeExample
Content-TypeBody MIME typeapplication/json, text/html
Content-LengthBody byte count1024
Transfer-EncodingTransfer encodingchunked
HostRequest target hostexample.com:8080
ConnectionConnection persistencekeep-alive, close
Accept-EncodingCompression supportgzip, 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_after timeout
  • 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


Summary

Key Points

  1. HTTP structure: Request line/status line, headers, blank line, body
  2. Parsing: Beast handles edge cases (CRLF, chunked, Content-Length)
  3. Headers: Case-insensitive, handle duplicates
  4. Chunked encoding: Beast auto-decodes
  5. Best practices: Limits, timeouts, prepare_payload, Keep-Alive hygiene
  6. Production: Logging, CORS, health checks, connection pools

HTTP Message Components

ComponentRequestResponse
First lineGET /path HTTP/1.1HTTP/1.1 200 OK
HeadersHost: example.comContent-Type: application/json
Blank line\r\n\r\n
BodyJSON, form data, etc.JSON, HTML, etc.

Common Error Codes

CodeMeaningWhen
200OKSuccess
201CreatedPOST success
204No ContentDELETE success
400Bad RequestInvalid input
401UnauthorizedAuth failure
404Not FoundResource missing
500Internal Server ErrorServer error

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.

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3