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.
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::strandfor 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/WebSocketClientBaseare aliases forHttpClient
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.hppfor shared definitions across TU boundaries. - v3.2.0: Split into two shared libraries —
libus_http.so(server only, no curl) andlibus_httpclient.so(HttpClient, depends on curl). HttpClient implementation moved tosrc/HttpClient.cpp;HttpClient.hppis 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.