Usage Guide

This guide provides examples of how to use the HTTPServer library to build a web server.

Basic Server Setup

To start a server, you need to initialize the Server object (optionally with a configuration file), register your routes, and call start().

#include <httpserver>

int main() {
    using namespace HTTPServer;

    // Initialize server with a config file
    Server server("config.toml");

    // Safe shutdown on SIGINT/SIGTERM
    server.installSignalHandlers();

    // Define routes...

    // Start the blocking event loop
    server.start();
}

Routing

The Router is a singleton used to map HTTP methods and paths to callback functions.

Static Routes

Simple path matching for specific methods:

Router::instance().addRoute("GET", "/hello", [](const HttpRequest& req) {
    return Responses::ok(req, "Hello, World!");
});

Dynamic Routes

Paths can contain placeholders in the format {name}. These are extracted and passed in the req.params map. You can use multiple placeholders in a single path.

Router::instance().addRoute("GET", "/post/{year}/{slug}", [](const HttpRequest& req) {
    auto year = req.params.at("year");
    auto slug = req.params.at("slug");
    return Responses::ok(req, "Viewing post from " + year + ": " + slug);
});

Query Parameters

The server also parses query parameters from the URL (e.g., ?key=value) and merges them into the same req.params map used for dynamic path variables.

// Handling a request like GET /search?q=cpp
Router::instance().addRoute("GET", "/search", [](const HttpRequest& req) {
    auto it = req.params.find("q");
    if (it != req.params.end()) {
        return Responses::ok(req, "Search results for: " + it->second);
    }
    return Responses::badRequest(StatusCode::BadRequest);
});

Route Precedence

When multiple routes could potentially match a single request, the Router follows these precedence rules:

  1. Exact Matches: Static routes take the highest priority.

  2. Dynamic Routes: If no exact match is found, the server looks for dynamic routes with placeholders.

  3. Wildcard/Directory Routes: If neither of the above match, the server attempts to match static directory routes.

For directory routes, the longest prefix match always wins. If you have both /static/ and /static/images/ registered, a request to /static/images/logo.png will be served from the more specific /static/images/ handler.

Security & Sanitization

The router includes built-in protection against Directory Traversal attacks. Any attempt to use .. in a URI to access files outside of a registered static directory (e.g., /static/../../etc/passwd) will be blocked and return a 401 Unauthorized response.

Serving Static Files

You can serve an entire directory of static assets (HTML, CSS, JS) using a single route.

// Map /static/* to the local public/ directory
Router::instance().addStaticDirectoryRoute("/static", "public/");

// Or serve a specific file for a route
Router::instance().addRoute("GET", "/", [](const HttpRequest& req) {
    return Responses::file(req, "public/index.html");
});

Pre-made Responses

The Responses namespace provides high-level helpers for common response types.

  • Responses::ok(req, body, [mime_type]): Returns a 200 OK response with the given body. Defaults to text/plain.

  • Responses::file(req, path): Reads a file from disk and serves it with the correct Content-Type based on its extension.

  • Responses::notFound(req): Returns a 404 response with a standard HTML error page.

  • Responses::badRequest(status_code): Returns a response with the specified error code (e.g., 400, 403) and a standard HTML error page.

  • Responses::redirection(req, port): Returns a 301 Moved Permanently response redirecting to the HTTPS version of the current path.

Response Building

For more control, use the HttpResponseBuilder fluent API.

Router::instance().addRoute("POST", "/data", [](const HttpRequest& req) {
    return HttpResponseBuilder()
        .setStatus(StatusCode::CREATED)
        .setBody("{\"status\":\"success\"}")
        .addHeader("Content-Type", "application/json")
        .build(req);
});

Logging

The library includes a thread-safe singleton Logger and a LogEvent helper for structured logging.

Simple Logging

Use the convenience macros for basic messages:

LOG_INFO << "Server starting...";
LOG_WARN << "Rate limit exceeded for IP: " << ip;
LOG_ERROR << "Failed to open configuration file";

Structured Logging

The LogEvent class allows you to build structured log lines with key-value pairs, which are easier to parse with log aggregators.

#include <httpserver_impl/monitoring/log_event.h>

LOG_EVENT(LogLevel::INFO,
    LogEvent("request_processed")
        .add("method", req.method)
        .add("path", req.path)
        .add("status", 200)
        .add("duration_ms", 5)
);