cpp-httplib

repository·master·Indexed 12 days ago

https://github.com/yhirose/cpp-httplib

A lightweight, single-file, header-only C++11 library for implementing HTTP/1.1 servers and clients. It provides cross-platform support and integrates with SSL/TLS backends including OpenSSL, Mbed TLS, and wolfSSL. Features include support for multipart form data, static file serving, content streaming via content providers, and a built-in ThreadPool for server concurrency. Note: The library uses blocking I/O and does not support 32-bit platforms or HTTP/2/3.

Tokens
82.7K
Snippets
295
Records
342
Agent score
96%

What's inside cpp-httplib

  1. Overview of cpp-httplib features and limitations

    master

    cpp-httplib is a C++11 (or later) library designed for simplicity and ease of use when building HTTP/HTTPS servers and clients.

    Key Features:

    • Lambda-based API: Uses a natural lambda-based design for defining routes and handling requests.
    • HTTPS Support: Enables TLS support by linking with OpenSSL or mbedTLS.
    • Advanced HTTP Features: Supports Content-Encoding (gzip, Brotli, etc.), file uploads, and WebSockets.
    • Cross-Platform: Works on Windows, macOS, and Linux.

    Architecture & Use Cases:

    • I/O Model: Uses blocking I/O with a thread pool.
    • Best For: API servers, embedded HTTP in tools, and mock servers for testing.
    • Not Recommended For: Handling massive numbers of simultaneous connections (due to the blocking I/O model).
  2. Difference between WebSocketClient and Client timeouts

    master

    While WebSocketClient and the standard Client share similar timeout APIs, there is a key functional difference:

    WebSocketClient does not have a set_max_timeout() method. Unlike a standard HTTP Client which can cap the total duration of a request, a WebSocketClient connection remains open as long as the user continues to call read() and the connection is not closed by either peer.

  3. Use path parameters and regex in routing

    master

    You can capture segments of a URL to use as variables in your handler.

    Path Parameters

    Use the :name syntax in the path. Captured values are stored in req.path_params and can be accessed via .at("name").

    Regex Patterns

    You can use regular expressions directly in the path string. Captured groups are available via req.matches (a std::smatch object). req.matches[1] retrieves the first capture group.

    Example of regex constraining a path to numeric IDs:

    // Path Parameter
    svr.Get("/users/:id", [](const auto &req, auto &res) {
        auto id = req.path_params.at("id");
        res.set_content("User ID: " + id, "text/plain");
    });
    
    // Regex Pattern (numeric IDs only)
    svr.Get(R"(/files/(\d+))", [](const auto &req, auto &res) {
        auto id = req.matches[1];
        res.set_content("File ID: " + std::string(id), "text/plain");
    });
  4. When to use Unix Domain Sockets

    master

    Unix domain sockets are ideal for Inter-Process Communication (IPC) on the same host. Use them when:

    • Behind a reverse proxy: Connecting Nginx to a backend for better performance and easier port management.
    • Local-only APIs: Providing services that should not be reachable via the network.
    • In-container IPC: Communicating between processes within the same container or pod.
    • Dev environments: Avoiding port conflicts during local development.
  5. Negotiate WebSocket Subprotocols

    master

    To support protocols like graphql-ws, the server can provide a subprotocol selector function during registration. The client proposes subprotocols via the Sec-WebSocket-Protocol header. The server's selector function receives a list of proposed protocols and returns the chosen one (or an empty string to decline).

    // Server: register a handler with a subprotocol selector
    svr.WebSocket(
        "/ws",
        [](const httplib::Request &req, httplib::ws::WebSocket &ws) {
            std::string msg;
            while (ws.read(msg)) {
                ws.send("echo: " + msg);
            }
        },
        [](const std::vector<std::string> &protocols) -> std::string {
            for (const auto &p : protocols) {
                if (p == "graphql-ws" || p == "graphql-transport-ws") {
                    return p;
                }
            }
            return "";  // Decline all
        });
    
    // Client: propose subprotocols via Sec-WebSocket-Protocol header
    httplib::Headers headers = {
        {"Sec-WebSocket-Protocol", "graphql-ws, graphql-transport-ws"}
    };
    httplib::ws::WebSocketClient ws("ws://localhost:8080/ws", headers);
    
    if (ws.connect()) {
        std::cout << "Subprotocol: " << ws.subprotocol() << std::endl;
        ws.close();
    }
  6. Detect client disconnection in chunked responses

    master

    When using set_chunked_content_provider, the server can detect if a client has closed the connection by checking sink.os.good(). If this returns false, the connection is lost, and you should stop any ongoing heavy computations (like model inference) to prevent wasted CPU/GPU cycles.

    // Inside the chunked content provider callback
    llm.chat(prompt, [&](std::string_view token) {
        sink.os << "data: " << token << "\n\n";
        return sink.os.good(); // Returns false if client disconnected, aborting the loop
    });
  7. Choose between pre-routing and pre-request handlers

    master

    When deciding how to implement middleware-like logic, consider the scope and timing:

    • Global logic (Pre-routing): Use set_pre_routing_handler() for logic that must run before any routing occurs (e.g., global logging or blocking specific paths entirely). It catches all requests, including 404s.
    • Per-route logic (Pre-request): If you need different authentication or validation rules for different specific routes, use set_pre_request_handler() instead. This runs after a route has been matched.
    • Response modification (Post-routing): If your goal is only to modify the response (e.g., adding headers) after a handler has finished, use set_post_routing_handler().
  8. Platform-specific certificate handling

    master

    cpp-httplib automatically integrates with OS certificate stores on macOS and Windows.

    • macOS: Loads system certs from Keychain. Requires Apple Clang and linking CoreFoundation and Security frameworks. Disable via CPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES.
    • Windows: Verifies certs via CryptoAPI with revocation checking. Disable via CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE. You can also disable verification at runtime using cli.enable_windows_certificate_verification(false).
  9. How WebSocket support works in cpp-httplib

    master

    The WebSocket implementation is a simple, blocking I/O model using a thread-per-connection approach (plus one heartbeat thread per connection).

    Important Design Constraints:

    • Scale: It is intended for small- to mid-scale workloads. It is not designed for high-concurrency scenarios requiring thousands of simultaneous connections (non-blocking/async I/O).
    • Extensions: WebSocket extensions (like permessage-deflate) are not supported. If a client requests extensions via Sec-WebSocket-Extensions, the server will silently decline them, and the connection will run without extensions.
    • Compliance: It is RFC 6455 compliant for the core protocol.