HTTP Server#

HttpServer provides a multi-threaded HTTP/HTTPS web server with routing, static file serving, and dynamic content generation. It uses a thread pool to handle concurrent requests efficiently.

HttpServer is designed to be:

  • fast — multi-threaded request handling
  • flexible — route mapping and handlers
  • complete — static files, dynamic content, redirects
  • secure — HTTPS support via HttpsServer

Basic usage#

Simple server#

#include <join/httpserver.hpp>

// Create server with 4 worker threads
HttpServer server(4);

// Serve static files from /var/www
server.baseLocation("/var/www");
server.addDocumentRoot("/*", "*");

// Start server on port 8080
Tcp::Endpoint endpoint(Tcp::v4(), 8080);
server.create(endpoint);

// Server runs until closed

HTTPS server#

// Create HTTPS server
HttpsServer server(4);

// Set certificate and key
server.setCertificate("/etc/ssl/cert.pem", "/etc/ssl/key.pem");

// Start on port 443
Tcp::Endpoint endpoint(Tcp::v4(), 443);
server.create(endpoint);

Construction#

// Default: uses hardware concurrency
HttpServer server();

// Specify worker threads
HttpServer server(8);

// HTTPS server
HttpsServer secureServer(4);

Static file serving#

Document root#

// Set base directory
server.baseLocation("/var/www");

// Serve all files from base
server.addDocumentRoot("/*", "*");

// Requests to /images/logo.png serve /var/www/images/logo.png

Path aliases#

// Map /static to /opt/assets
server.addAlias("/static", "*", "/opt/assets");

// /static/logo.png serves /opt/assets/logo.png

// Pattern matching
server.addAlias("/images", "*.png", "/opt/images");
server.addAlias("/docs", "*.pdf", "/opt/documents");

Dynamic content#

Route handlers#

// GET endpoint
server.addExecute(
    HttpMethod::Get,
    "/api",
    "users",
    [](HttpServer::Worker* worker) {
        worker->header("Content-Type", "application/json");

        std::string json = R"({"users":["Alice","Bob"]})";
        worker->header("Content-Length", std::to_string(json.size()));

        worker->sendHeaders();
        *worker << json;
        worker->flush();
    }
);

// POST endpoint
server.addExecute(
    HttpMethod::Post,
    "/api",
    "submit",
    [](HttpServer::Worker* worker) {
        // Read request body
        size_t length = worker->contentLength();
        std::string body(length, '\0');
        worker->read(&body[0], length);

        // Process data
        processData(body);

        // Send response
        worker->header("Content-Type", "application/json");
        *worker << R"({"status":"success"})";
        worker->sendHeaders();
        worker->flush();
    }
);

Multiple methods#

// Handle GET and POST
server.addExecute(
    HttpMethod::Get | HttpMethod::Post,
    "/api",
    "endpoint",
    [](HttpServer::Worker* worker) {
        // Handler for both methods
    }
);

Worker API#

Reading request data#

[](HttpServer::Worker* worker) {
    // Check headers
    if (worker->hasHeader("Content-Type"))
    {
        std::string type = worker->header("Content-Type");
    }

    // Get content length
    size_t length = worker->contentLength();

    // Read body
    std::string body(length, '\0');
    worker->read(&body[0], length);
}

Sending responses#

[](HttpServer::Worker* worker) {
    // Set response headers
    worker->header("Content-Type", "text/html");
    worker->header("Cache-Control", "no-cache");

    // Write headers
    worker->sendHeaders();

    // Write body
    *worker << "<html><body>Hello</body></html>";

    // Flush
    worker->flush();
}

Helper methods#

[](HttpServer::Worker* worker) {
    // Send file
    worker->sendFile("/var/www/index.html");

    // Send error
    worker->sendError("404", "Not Found");

    // Send redirect
    worker->sendRedirect("302", "Found", "https://example.com/new");
}

Routing#

Pattern matching#

// Exact match
server.addExecute(Get, "/api", "users", handler);

// Wildcard file name
server.addDocumentRoot("/images", "*.png");

// Wildcard directory
server.addDocumentRoot("/*", "index.html");

// Multiple wildcards (uses fnmatch)
server.addAlias("/downloads", "*.{zip,tar.gz}", "/opt/files");

Redirects#

Simple redirect#

// Redirect /old to /new
server.addRedirect("/old", "*", "/new");

// Redirect to external URL
server.addRedirect("/external", "*", "https://example.com");

Dynamic redirects#

// Use variables in redirect
server.addRedirect(
    "/api/v1",
    "*",
    "$scheme://$host/api/v2$path$query"
);

// Available variables:
// $root, $scheme, $host, $port, $path, $query, $urn

Authentication#

Access control#

// Define access handler
auto authHandler = [](const std::string& type,
                      const std::string& credentials,
                      std::error_code& err) -> bool {
    if (type != "Bearer")
    {
        err = HttpErrc::Unauthorized;
        return false;
    }

    if (!validateToken(credentials))
    {
        err = HttpErrc::Forbidden;
        return false;
    }

    return true;
};

// Apply to route
server.addExecute(
    HttpMethod::Get,
    "/api",
    "protected",
    handler,
    authHandler  // Access control
);

Configuration#

Keep-alive#

// Set keep-alive timeout and max requests
server.keepAlive(
    std::chrono::seconds(30),  // Timeout
    100                         // Max requests per connection
);

// Get settings
auto timeout = server.keepAliveTimeout();
int max = server.keepAliveMax();

Base location#

// Set document root
server.baseLocation("/var/www/html");

// Get current location
std::string location = server.baseLocation();

HTTPS configuration#

Certificates#

HttpsServer server(4);

// Set server certificate and private key
server.setCertificate(
    "/etc/ssl/server-cert.pem",
    "/etc/ssl/server-key.pem"
);

// Set CA certificate for client verification
server.setCaCertificate("/etc/ssl/ca-cert.pem");

// Require client certificates
server.setVerify(true, 5);  // depth=5

Cipher configuration#

// TLS 1.2 and below
server.setCipher("ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256");

// TLS 1.3
server.setCipher_1_3("TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256");

// Curves (OpenSSL 3.0+)
server.setCurve("P-521:P-384:P-256");

Complete examples#

REST API server#

HttpServer server(4);

// GET /api/users
server.addExecute(
    HttpMethod::Get, "/api", "users",
    [](auto* w) {
        w->header("Content-Type", "application/json");
        w->sendHeaders();
        *w << getUsersJson();
        w->flush();
    }
);

// POST /api/users
server.addExecute(
    HttpMethod::Post, "/api", "users",
    [](auto* w) {
        std::string body(w->contentLength(), '\0');
        w->read(&body[0], body.size());

        auto user = createUser(body);

        w->header("Content-Type", "application/json");
        w->sendHeaders();
        *w << user.toJson();
        w->flush();
    }
);

// DELETE /api/users
server.addExecute(
    HttpMethod::Delete, "/api", "users",
    [](auto* w) {
        deleteUser();
        w->sendError("204", "No Content");
    }
);

Tcp::Endpoint endpoint(Tcp::v4(), 8080);
server.create(endpoint);

Static + dynamic content#

HttpServer server(4);

// Static files
server.baseLocation("/var/www");
server.addDocumentRoot("/*", "*");

// API endpoints
server.addExecute(
    HttpMethod::Get, "/api", "*",
    [](auto* w) {
        w->header("Content-Type", "application/json");
        w->sendHeaders();
        *w << R"({"api":"v1"})";
        w->flush();
    }
);

Tcp::Endpoint endpoint(Tcp::v4(), 80);
server.create(endpoint);

File server with auth#

HttpServer server(4);

auto checkAuth = [](const std::string& type,
                    const std::string& creds,
                    std::error_code& err) -> bool {
    return validateCredentials(type, creds);
};

// Protected downloads
server.addDocumentRoot(
    "/downloads",
    "*",
    checkAuth
);

Tcp::Endpoint endpoint(Tcp::v4(), 8080);
server.create(endpoint);

Best practices#

Worker threads#

// Use hardware concurrency
HttpServer server(std::thread::hardware_concurrency());

// Or based on workload
HttpServer cpuBound(std::thread::hardware_concurrency() * 2);
HttpServer ioBound(std::thread::hardware_concurrency() * 4);

Always set Content-Length or use chunking#

[](auto* worker) {
    std::string body = generateBody();

    // Option 1: Content-Length
    worker->header("Content-Length", std::to_string(body.size()));
    worker->sendHeaders();
    *worker << body;

    // Option 2: Chunked transfer
    worker->header("Transfer-Encoding", "chunked");
    worker->sendHeaders();
    *worker << body;
    worker->flush();
}

Error handling in handlers#

[](auto* worker) {
    try {
        // Process request
        processRequest(worker);
    } catch (const std::exception& e) {
        worker->sendError("500", "Internal Server Error");
    }
}

Close server gracefully#

HttpServer server(4);

// Start server
server.create(endpoint);

// Handle shutdown signal
signal(SIGINT, [](int)
{
    server.close();
});

// Wait for workers to finish
// (destructor waits automatically)

Performance tips#

File caching#

The server automatically caches files in memory and tracks modifications:

// Files are cached automatically
// Cache invalidates on file modification
server.addDocumentRoot("/*", "*");

// No manual cache management needed

Keep-alive tuning#

// Long timeout for interactive clients
server.keepAlive(std::chrono::seconds(60), 1000);

// Short timeout for API clients
server.keepAlive(std::chrono::seconds(5), 100);

Compression#

// Server automatically handles:
// - gzip encoding
// - deflate encoding
// - chunked transfer

// Client requests with Accept-Encoding: gzip
// Server responds with Content-Encoding: gzip (if applicable)

Security headers#

The server automatically adds security headers:

// Automatically added:
// - Strict-Transport-Security (HTTPS only)
// - Content-Security-Policy
// - X-XSS-Protection
// - X-Content-Type-Options
// - X-Frame-Options

// Override if needed
[](auto* worker) {
    worker->header("X-Frame-Options", "DENY");
    worker->sendHeaders();
}

Summary#

FeatureSupported
Multi-threaded
Static file serving
Dynamic content
Route pattern matching
HTTP/HTTPS
Keep-alive
Compression
Chunked transfer
Authentication
File caching
Redirects