Files
blackbird/libhttp
qing 85e0d0740b fix(libhttp): HttpClient 响应大小限制防 OOM
- request_stream_sync 改为真流式,按块回调不二次缓冲
- 新增 set_max_response_size/get_max_response_size(默认 10MB,对齐 WAF)
- write_callback 超限返回 0 中止传输并抛异常
- 更新审计文档 P1-2 状态、README v3.4.0、INDEX 移除不存在的文档
2026-08-04 15:12:06 +08:00
..
2026-07-20 11:12:29 +08:00
2026-07-20 11:12:29 +08:00
2026-07-20 11:12:29 +08:00

libhttp - UniServe HTTP/WebSocket Library

C++20 network library for the UniServe microservice framework, providing unified HTTP/HTTPS server and HTTP/WebSocket client capabilities built on Boost.Beast and libcurl.

Features

HttpServer

  • HTTP/HTTPS Server: Asynchronous HTTP 1.1 server with TLS support
  • WebSocket Integration: Single port can serve both HTTP(S) and WebSocket(S) endpoints
  • WebSocket Reverse Proxy: Transparent bidirectional WebSocket proxy with handshake-time auth/callback (supports ws:// and wss:// backends)
  • Routing: Exact path matching + wildcard (*) routing
  • Middleware: use() for middleware chains (CORS, auth, logging, etc.)
  • Static Files: setStaticDirectory() for serving static files
  • Thread Pool: Multi-threaded I/O with asio::strand for thread safety
  • Client Management: WebSocket client tracking, broadcast, and targeted send

HttpClient

  • HTTP Client: Synchronous HTTP requests via libcurl (GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS)
  • Streaming Response: request_stream_sync streams response body chunk-by-chunk to the callback (no full-body buffering)
  • Response Size Limit: set_max_response_size() caps downstream response body (default 10MB, aligned with WAF; 0 = unlimited) to prevent OOM on oversized responses
  • WebSocket Client: Synchronous WebSocket client via Boost.Beast
  • Dual Protocol: Same client instance handles both HTTP and WebSocket
  • ws:// and wss://: Supports both plain and TLS-encrypted WebSocket connections
  • Binary Support: Text and binary message sending
  • Backward Compatible: Original API preserved; WebSocketClient/WebSocketClientBase are aliases for HttpClient

Dependencies

  • C++20 compiler
  • Boost.Beast / Boost.Asio (header-only)
  • OpenSSL (for TLS)
  • libcurl (for HTTP client only — libus_httpclient.so)
  • pthread

Build

make           # builds build/libus_http.so and build/libus_httpclient.so
make install   # installs both .so to /lib and headers to /usr/include/us (requires root)

Two shared libraries are produced:

Library SONAME Contains Links
Server libus_http.so HttpServer (HTTP + WebSocket server + WS proxy) -lssl -lcrypto -lpthread
Client libus_httpclient.so HttpClient (HTTP via curl + WebSocket via Beast) -lssl -lcrypto -lcurl -lpthread

libus_http.so does not depend on libcurl. Link against libus_httpclient.so only when you need the client, and link both when you need both server and client (e.g. a proxy/gateway).

Source Layout

The server implementation is split into modular source files for faster incremental builds:

Source File Responsibility
src/HttpServer.cpp Core lifecycle (construct/destruct/start/stop/run), SSL init, session management, debug utilities
src/HttpServer_Listener.cpp TCP connection acceptor (Listener class)
src/HttpServer_Routes.cpp HTTP route registration, route matching, wildcard→regex conversion, query parameter parsing
src/HttpServer_Request.cpp Request dispatch pipeline, static file serving, CORS preflight, Session HTTP read/write
src/HttpServer_Ws.cpp WebSocket route registration, client registry, broadcast/send APIs
src/HttpServer_WsSession.cpp Session WebSocket handling (regular WS frames + bidirectional WS proxy bridging)

Internal shared definitions (exception classes, full Session/Listener declarations) live in inc/us/detail/HttpServerImpl.hpp (installed to /usr/include/us/detail/).

Modifying one module only requires recompiling its corresponding .o file — no need to rebuild the entire 1400-line translation unit.

Quick Start

HTTP + WebSocket Server

#include <us/HttpServer.hpp>
using namespace pmc::net;

int main() {
    // HTTP server on port 8080
    HttpServer server("0.0.0.0", 8080, 4); // 4 threads

    // HTTP GET route
    server.get("/hello", [](const auto& req, const auto& params) {
        http::response<http::string_body> res;
        res.result(http::status::ok);
        res.set(http::field::content_type, "text/plain");
        res.body() = "Hello, World!";
        res.prepare_payload();
        return res;
    });

    // WebSocket route
    server.ws("/ws", [](const std::string& clientId,
                         const std::string& message,
                         std::function<void(const std::string&)> send) {
        send("Echo: " + message);
    });

    server.onWsConnect([](const std::string& id, const std::string& addr) {
        std::cout << "Client " << id << " connected from " << addr << std::endl;
    });

    server.onWsDisconnect([](const std::string& id) {
        std::cout << "Client " << id << " disconnected" << std::endl;
    });

    server.start();
    // server.run(); // blocks with ioc.run()
    return 0;
}

HTTP + WebSocket Client

#include <us/HttpClient.hpp>
using namespace pmc::net;

// HTTP request
HttpClient http;
http.set_header("Content-Type: application/json");
http.post_stream_sync("http://localhost:8080/api/data", "{}",
    [](const std::string& resp) {
        std::cout << "Response: " << resp << std::endl;
    });

// WebSocket connection
HttpClient ws;
if (ws.ws_connect("ws://localhost:8080/ws")) {
    ws.ws_send("Hello WebSocket!");
    std::string reply = ws.ws_recv();
    std::cout << "Got: " << reply << std::endl;
    ws.ws_close();
}

// WSS (TLS) connection
HttpClient wss;
wss.ws_connect("wss://example.com/socket");

HTTPS Server

HttpServer secure("0.0.0.0", 443, "/path/to/cert.pem", "/path/to/key.pem", 4);
secure.get("/", handler);
secure.start();

API Reference

HttpServer

Constructors

HttpServer(const std::string& address, unsigned short port, unsigned int threads = 1);
HttpServer(const std::string& address, unsigned short port,
           const std::string& cert_file, const std::string& key_file,
           unsigned int threads = 1);

HTTP Routes

void get(const std::string& path, HttpRequestHandler handler);
void post(const std::string& path, HttpRequestHandler handler);
void put(const std::string& path, HttpRequestHandler handler);
void del(const std::string& path, HttpRequestHandler handler);
void options(const std::string& path, HttpRequestHandler handler);
void use(MiddlewareHandler middleware);
void setStaticDirectory(const std::string& path);

WebSocket Routes

void ws(const std::string& path, WebSocketMessageHandler handler);
void onWsConnect(WebSocketConnectHandler handler);
void onWsDisconnect(WebSocketDisconnectHandler handler);
bool sendToWsClient(const std::string& clientId, const std::string& message);
size_t broadcastWs(const std::string& message);
void disconnectWsClient(const std::string& clientId);
size_t getWsClientCount() const;

WebSocket Reverse Proxy

void ws_proxy(const std::string& path, WsProxyHandshakeHandler handshake);

The WsProxyHandshakeHandler is called during the HTTP Upgrade handshake. It receives the upgrade request and must return the backend WebSocket URL (ws://host:port/path or wss://host:port/path) to bridge to. Returning an empty string rejects the connection (HTTP 403 is sent to the client). Proxy routes take priority over regular ws() routes on the same path.

Example — admin-only proxy:

server.ws_proxy("/ws/admin", [](const http::request<http::string_body>& req) -> std::string {
    if (!isAdmin(req)) return "";           // reject non-admins
    return "ws://127.0.0.1:9000/stream";    // bridge to backend
});

Features:

  • Full-duplex async bridging (text and binary frames preserved)
  • Write queues in both directions to maintain message ordering
  • Automatic SNI for wss:// backends
  • Graceful close propagation between client and backend
  • Errors during backend connect/handshake close the client connection cleanly

Lifecycle

void start();          // non-blocking, spawns worker threads
void run();            // blocking (calls start() then ioc.run())
void stop();
bool isRunning() const;

HttpClient

HTTP Methods

void set_header(const std::string& header);
void clear_headers();
const std::map<std::string, std::string>& get_response_headers() const;
long get_response_code() const;

void get_stream_sync(const std::string& url, ResponseCallback cb);
void post_stream_sync(const std::string& url, const std::string& data, ResponseCallback cb);
void put_stream_sync(const std::string& url, const std::string& data, ResponseCallback cb);
void delete_stream_sync(const std::string& url, ResponseCallback cb);
void request_stream_sync(HttpMethod method, const std::string& url,
                          const std::string& data, ResponseCallback cb);

// Response size limit (防 OOM). Default 10MB (aligned with WAF); 0 = unlimited.
void set_max_response_size(size_t bytes);
size_t get_max_response_size() const;

WebSocket Methods

bool ws_connect(const std::string& url, int timeout_ms = 5000);
bool ws_connect(const std::string& host, const std::string& port,
                const std::string& path = "/", bool ssl = false, int timeout_ms = 5000);
bool ws_send(const std::string& message);
bool ws_send_binary(const void* data, size_t size);
std::string ws_recv(int timeout_ms = 0);
bool ws_is_connected() const;
void ws_close();
std::string ws_get_host() const;
std::string ws_get_port() const;
std::string ws_get_path() const;

Type Definitions

using HttpRequestHandler = std::function<http::response<http::string_body>(
    const http::request<http::string_body>&,
    const std::unordered_map<std::string, std::string>&)>;

using MiddlewareHandler = std::function<bool(
    const http::request<http::string_body>&,
    http::response<http::string_body>&,
    const std::unordered_map<std::string, std::string>&)>;

using WebSocketMessageHandler = std::function<void(
    const std::string& clientId,
    const std::string& message,
    std::function<void(const std::string&)> sendCallback)>;

using WebSocketConnectHandler = std::function<void(
    const std::string& clientId,
    const std::string& remoteAddress)>;

using WebSocketDisconnectHandler = std::function<void(
    const std::string& clientId)>;

using WsProxyHandshakeHandler = std::function<std::string(
    const http::request<http::string_body>& upgradeReq)>;

Backward Compatibility

The following type aliases are provided for existing code that used the standalone WebSocket client:

using WebSocketClient = HttpClient;
using WebSocketClientBase = HttpClient;

Version History

  • v3.4.0: HttpClient 响应大小限制(防 OOM)。request_stream_sync 改为真流式(按块回调,不二次缓冲);新增 set_max_response_size()/get_max_response_size()(默认 10MB,对齐 WAF,0 表示不限制),超限时 write_callback 返回 0 中止传输并抛异常。
  • v3.3.0: Split HttpServer into 6 modular translation units by functional area (core/lifecycle, listener, routes, request dispatch, WS management, WS session/proxy) for dramatically faster incremental builds. Added internal detail header inc/us/detail/HttpServerImpl.hpp for shared definitions across TU boundaries.
  • v3.2.0: Split into two shared libraries — libus_http.so (server only, no curl) and libus_httpclient.so (HttpClient, depends on curl). HttpClient implementation moved to src/HttpClient.cpp; HttpClient.hpp is declaration-only.
  • v3.1.0: Added WebSocket reverse proxy (ws_proxy()) with bidirectional async bridging, ws:// and wss:// backend support, handshake-time authorization callback.
  • v3.0.0: Merged WebSocketServer into HttpServer and WebSocketClient into HttpClient. Single-port HTTP+WebSocket support.
  • v2.x: Separate HttpServer, WebSocketServer, and HttpClient modules.