Files
blackbird/libhttp
qing bd1d7ca4d0 refactor(libhttp): split HttpServer into modular TUs for faster incremental builds
Split the 1400-line HttpServer.cpp into 6 focused translation units by
functional area:
- HttpServer.cpp: core lifecycle, SSL init, session management, debug
- HttpServer_Listener.cpp: TCP connection acceptor
- HttpServer_Routes.cpp: route registration, matching, wildcard/query parsing
- HttpServer_Request.cpp: request dispatch, static files, CORS, HTTP I/O
- HttpServer_Ws.cpp: WS route registry, client management, broadcast APIs
- HttpServer_WsSession.cpp: per-session WS frames + bidirectional WS proxy

Add inc/us/detail/HttpServerImpl.hpp for internal shared definitions
(exception classes, full Session/Listener declarations) installed under
/usr/include/us/detail/. Update Makefile with proper dependency tracking.
Update README with Source Layout section and v3.3.0 changelog entry.

Modifying one module now only requires recompiling its corresponding .o
instead of the entire monolithic translation unit.
2026-08-01 13:15:52 +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
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)
  • 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);

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.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.