# 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) - pthread ## Build ```bash make # builds build/libus_http.so make install # installs to /usr/lib and /usr/include/us (requires root) ``` Makefile links: `-lssl -lcrypto -lcurl -lpthread` with Homebrew paths for macOS. ## Quick Start ### HTTP + WebSocket Server ```cpp #include 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 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 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 ```cpp #include 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 ```cpp 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** ```cpp 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** ```cpp 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** ```cpp 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** ```cpp 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: ```cpp server.ws_proxy("/ws/admin", [](const http::request& 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** ```cpp void start(); // non-blocking, spawns worker threads void run(); // blocking (calls start() then ioc.run()) void stop(); bool isRunning() const; ``` ### HttpClient **HTTP Methods** ```cpp void set_header(const std::string& header); void clear_headers(); const std::map& 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** ```cpp 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 ```cpp using HttpRequestHandler = std::function( const http::request&, const std::unordered_map&)>; using MiddlewareHandler = std::function&, http::response&, const std::unordered_map&)>; using WebSocketMessageHandler = std::function sendCallback)>; using WebSocketConnectHandler = std::function; using WebSocketDisconnectHandler = std::function; using WsProxyHandshakeHandler = std::function& upgradeReq)>; ``` ## Backward Compatibility The following type aliases are provided for existing code that used the standalone WebSocket client: ```cpp using WebSocketClient = HttpClient; using WebSocketClientBase = HttpClient; ``` ## Version History - **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.