Skip to content

Advanced Networking

This guide covers advanced Glaze HTTP features including CORS support, WebSocket functionality, SSL/TLS encryption, and HTTP client capabilities.

CORS (Cross-Origin Resource Sharing)

Quick CORS Setup

#include "glaze/net/http_server.hpp"

glz::http_server server;

// Enable CORS with default settings (allows all origins)
server.enable_cors();

// Good for development, allows all origins and methods
server.bind(8080);
server.start();

Production CORS Configuration

// Restrict CORS to specific origins
std::vector<std::string> allowed_origins = {
    "https://myapp.com",
    "https://api.myapp.com",
    "https://admin.myapp.com"
};

server.enable_cors(allowed_origins, true); // Allow credentials

// Or use detailed configuration
glz::cors_config config;
config.allowed_origins = {"https://myapp.com", "https://app.myapp.com"};
config.allowed_methods = {"GET", "POST", "PUT", "DELETE"};
config.allowed_headers = {"Content-Type", "Authorization", "X-API-Key"};
config.exposed_headers = {"X-Total-Count", "X-Page-Info"};
config.allow_credentials = true;
config.max_age = 3600; // 1 hour preflight cache
config.handle_preflight = true;

server.enable_cors(config);

Custom CORS Middleware

// Create custom CORS handler
auto custom_cors = glz::create_cors_middleware({
    .allowed_origins = {"https://trusted-site.com"},
    .allowed_methods = {"GET", "POST"},
    .allowed_headers = {"Content-Type", "Authorization"},
    .allow_credentials = true,
    .max_age = 86400 // 24 hours
});

server.use(custom_cors);

CORS Testing Endpoint

server.get("/test-cors", [](const glz::request& req, glz::response& res) {
    res.json({
        {"message", "CORS test endpoint"},
        {"origin", req.headers.count("Origin") ? req.headers.at("Origin") : "none"},
        {"method", glz::to_string(req.method)},
        {"timestamp", std::time(nullptr)}
    });
});

WebSocket Support

Basic WebSocket Server

#include "glaze/net/websocket_connection.hpp"

// Create WebSocket server
auto ws_server = std::make_shared<glz::websocket_server>();

// Handle new connections
ws_server->on_open([](std::shared_ptr<glz::websocket_connection> conn, const glz::request& req) {
    std::cout << "WebSocket connection opened from " << conn->remote_address() << std::endl;
    conn->send_text("Welcome to the WebSocket server!");
});

// Handle incoming messages
ws_server->on_message([](std::shared_ptr<glz::websocket_connection> conn, std::string_view message, glz::ws_opcode opcode) {
    if (opcode == glz::ws_opcode::text) {
        std::cout << "Received: " << message << std::endl;

        // Echo message back
        conn->send_text("Echo: " + std::string(message));
    }
});

// Handle connection close
ws_server->on_close([](std::shared_ptr<glz::websocket_connection> conn) {
    std::cout << "WebSocket connection closed" << std::endl;
});

// Handle errors
ws_server->on_error([](std::shared_ptr<glz::websocket_connection> conn, std::error_code ec) {
    std::cerr << "WebSocket error: " << ec.message() << std::endl;
});

// Mount WebSocket on HTTP server
glz::http_server server;
server.websocket("/ws", ws_server);

server.bind(8080);
server.start();

Chat Room Example

class ChatRoom {
    std::set<std::shared_ptr<glz::websocket_connection>> connections_;
    std::mutex connections_mutex_;

public:
    void add_connection(std::shared_ptr<glz::websocket_connection> conn) {
        std::lock_guard<std::mutex> lock(connections_mutex_);
        connections_.insert(conn);
    }

    void remove_connection(std::shared_ptr<glz::websocket_connection> conn) {
        std::lock_guard<std::mutex> lock(connections_mutex_);
        connections_.erase(conn);
    }

    void broadcast_message(const std::string& message) {
        std::lock_guard<std::mutex> lock(connections_mutex_);

        auto it = connections_.begin();
        while (it != connections_.end()) {
            auto conn = *it;
            try {
                conn->send_text(message);
                ++it;
            } catch (...) {
                // Remove broken connections
                it = connections_.erase(it);
            }
        }
    }
};

// Setup chat WebSocket
ChatRoom chat_room;
auto chat_server = std::make_shared<glz::websocket_server>();

chat_server->on_open([&chat_room](auto conn, const glz::request& req) {
    chat_room.add_connection(conn);
    chat_room.broadcast_message("User joined the chat");
});

chat_server->on_message([&chat_room](auto conn, std::string_view message, glz::ws_opcode opcode) {
    if (opcode == glz::ws_opcode::text) {
        chat_room.broadcast_message(std::string(message));
    }
});

chat_server->on_close([&chat_room](auto conn) {
    chat_room.remove_connection(conn);
    chat_room.broadcast_message("User left the chat");
});

server.websocket("/chat", chat_server);

WebSocket Authentication

auto secure_ws = std::make_shared<glz::websocket_server>();

// Validate connections before upgrade
secure_ws->on_validate([](const glz::request& req) -> bool {
    auto auth_header = req.headers.find("Authorization");
    if (auth_header == req.headers.end()) {
        return false;
    }

    return validate_jwt_token(auth_header->second);
});

secure_ws->on_open([](auto conn, const glz::request& req) {
    // Store user info from validated token
    auto user_data = extract_user_from_token(req.headers.at("Authorization"));
    conn->set_user_data(std::make_shared<UserData>(user_data));

    conn->send_text("Authenticated successfully");
});

server.websocket("/secure-ws", secure_ws);

SSL/TLS Support

HTTPS Server Setup

#include "glaze/net/http_server.hpp"

// Create HTTPS server (EnableTLS template parameter)
glz::https_server server;  // or glz::http_server<true>

// Load SSL certificates
server.load_certificate("server-cert.pem", "server-key.pem");

// Configure SSL options
server.set_ssl_verify_mode(0); // No client certificate verification

// Optional: Require client certificates
// server.set_ssl_verify_mode(SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT);

// Setup routes as normal
server.get("/secure-data", [](const glz::request& req, glz::response& res) {
    res.json({
        {"secure", true},
        {"client_ip", req.remote_ip},
        {"timestamp", std::time(nullptr)}
    });
});

server.bind(8443); // Standard HTTPS port
server.start();

Mixed HTTP/HTTPS Server

// Run both HTTP and HTTPS on different ports
void run_dual_servers() {
    // HTTP server
    glz::http_server http_server;
    http_server.get("/health", health_check_handler);
    http_server.get("/redirect", [](const glz::request& req, glz::response& res) {
        res.status(301).header("Location", "https://localhost:8443" + req.target);
    });

    // HTTPS server
    glz::https_server https_server;
    https_server.load_certificate("cert.pem", "key.pem");
    setup_secure_routes(https_server);

    // Start both servers
    auto http_future = std::async([&]() {
        http_server.bind(8080);
        http_server.start();
    });

    auto https_future = std::async([&]() {
        https_server.bind(8443);
        https_server.start();
    });

    // Wait for shutdown signal
    wait_for_shutdown();

    http_server.stop();
    https_server.stop();

    http_future.wait();
    https_future.wait();
}

HTTP Client

Basic HTTP Requests

#include "glaze/net/http_client.hpp"

glz::http_client client;

// GET request
auto response = client.get("https://api.example.com/users");
if (response && response->status_code == 200) {
    std::cout << "Response: " << response->response_body << std::endl;
}

// POST request with JSON
User new_user{0, "John Doe", "john@example.com"};
auto create_response = client.post_json("https://api.example.com/users", new_user);

// POST with custom headers
std::unordered_map<std::string, std::string> headers = {
    {"Authorization", "Bearer " + token},
    {"Content-Type", "application/json"}
};

auto auth_response = client.post("https://api.example.com/data", 
                                json_data, headers);

Async HTTP Requests

// Async GET
auto future_response = client.get_async("https://api.example.com/slow-endpoint");

// Do other work while request is in progress
perform_other_tasks();

// Get result when ready
auto response = future_response.get();
if (response && response->status_code == 200) {
    process_response(response->response_body);
}

// Multiple concurrent requests
std::vector<std::future<std::expected<glz::response, std::error_code>>> futures;

for (int i = 0; i < 10; ++i) {
    std::string url = "https://api.example.com/data/" + std::to_string(i);
    futures.push_back(client.get_async(url));
}

// Collect all results
for (auto& future : futures) {
    auto response = future.get();
    if (response) {
        process_response(*response);
    }
}

Request/Response Middleware

Custom Middleware Development

// Timing middleware
auto timing_middleware = [](const glz::request& req, glz::response& res) {
    auto start = std::chrono::high_resolution_clock::now();

    // Store start time in response for later use
    res.header("X-Request-Start", std::to_string(
        std::chrono::duration_cast<std::chrono::milliseconds>(
            start.time_since_epoch()).count()));
};

// Response time middleware (would need to be applied after handler)
auto response_time_middleware = [](const glz::request& req, glz::response& res) {
    auto start_header = res.response_headers.find("X-Request-Start");
    if (start_header != res.response_headers.end()) {
        auto start_ms = std::stoll(start_header->second);
        auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::high_resolution_clock::now().time_since_epoch()).count();

        res.header("X-Response-Time", std::to_string(now_ms - start_ms) + "ms");
    }
};

server.use(timing_middleware);

Security Middleware

// Security headers middleware
auto security_middleware = [](const glz::request& req, glz::response& res) {
    res.header("X-Content-Type-Options", "nosniff");
    res.header("X-Frame-Options", "DENY");
    res.header("X-XSS-Protection", "1; mode=block");
    res.header("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
    res.header("Content-Security-Policy", "default-src 'self'");
};

// Rate limiting middleware
class RateLimiter {
    std::unordered_map<std::string, std::vector<std::chrono::steady_clock::time_point>> requests_;
    std::mutex mutex_;
    const int max_requests_ = 100;
    const std::chrono::minutes window_{1};

public:
    glz::handler create_middleware() {
        return [this](const glz::request& req, glz::response& res) {
            std::lock_guard<std::mutex> lock(mutex_);

            auto now = std::chrono::steady_clock::now();
            auto& client_requests = requests_[req.remote_ip];

            // Remove old requests outside the window
            client_requests.erase(
                std::remove_if(client_requests.begin(), client_requests.end(),
                    [now, this](const auto& time) {
                        return now - time > window_;
                    }),
                client_requests.end());

            // Check rate limit
            if (client_requests.size() >= max_requests_) {
                res.status(429).json({{"error", "Rate limit exceeded"}});
                return;
            }

            client_requests.push_back(now);
        };
    }
};

RateLimiter rate_limiter;
server.use(security_middleware);
server.use(rate_limiter.create_middleware());

Performance Optimization

Connection Pooling

// The HTTP client automatically manages connections
class APIClient {
    glz::http_client client_;  // Reuse same client instance

public:
    // Multiple requests reuse connections automatically
    std::vector<User> get_all_users() {
        auto response = client_.get("https://api.example.com/users");
        // Connection is pooled for reuse

        if (response && response->status_code == 200) {
            return glz::read_json<std::vector<User>>(response->response_body).value_or(std::vector<User>{});
        }
        return {};
    }

    User get_user(int id) {
        auto response = client_.get("https://api.example.com/users/" + std::to_string(id));
        // Reuses pooled connection if available

        if (response && response->status_code == 200) {
            return glz::read_json<User>(response->response_body).value_or(User{});
        }
        return {};
    }
};

Async Processing

// Process requests asynchronously for better throughput
server.get("/heavy-work", [](const glz::request& req, glz::response& res) {
    // Offload heavy work to thread pool
    auto future = std::async([&]() {
        auto result = perform_heavy_computation();
        res.json(result);
    });

    // Response will be sent when async work completes
    future.wait();
});

// Or use the built-in async handlers
server.get_async("/async-work", [](const glz::request& req, glz::response& res) -> std::future<void> {
    return std::async([&req, &res]() {
        auto data = fetch_data_from_external_api();
        res.json(data);
    });
});

Error Handling and Logging

Structured Error Responses

struct ErrorResponse {
    std::string error;
    std::string message;
    int code;
    std::string timestamp;
};

auto error_handler = [](const std::exception& e, glz::response& res) {
    ErrorResponse error;
    error.timestamp = get_iso_timestamp();

    if (auto validation_error = dynamic_cast<const ValidationException*>(&e)) {
        error.error = "validation_error";
        error.message = validation_error->what();
        error.code = 1001;
        res.status(400);
    } else if (auto not_found = dynamic_cast<const NotFoundException*>(&e)) {
        error.error = "not_found";
        error.message = not_found->what();
        error.code = 1002;
        res.status(404);
    } else {
        error.error = "internal_error";
        error.message = "An unexpected error occurred";
        error.code = 1000;
        res.status(500);
    }

    res.json(error);
};

Request Logging

// Structured logging middleware
auto logging_middleware = [](const glz::request& req, glz::response& res) {
    auto start_time = std::chrono::high_resolution_clock::now();

    // Log request
    std::cout << "REQUEST: " << glz::to_string(req.method) << " " << req.target
              << " from " << req.remote_ip << ":" << req.remote_port << std::endl;

    // Store start time for response logging
    res.header("X-Start-Time", std::to_string(
        std::chrono::duration_cast<std::chrono::microseconds>(
            start_time.time_since_epoch()).count()));
};

server.use(logging_middleware);