Non-blocking HTTP Server Implementation Using cpp-httplib with Threaded Execution and Controller-based Architecture

The cpp-httplib library provides a header-only HTTP server implementation to C++. A primary limitation is its listen() function blocks the calling thread, prevanting concurrent execution of other tasks.

Threaded Server Wrapper

A wrapper class HttpServer manages httplib::Server instances in separate threads to prevent blocking:

class HttpServer {
    httplib::Server* server;
    MyHttpServerThreadPool* serverPool;
public:
    HttpServer(const std::string& host, int port);
    httplib::Server* getHttplibServer();
    void listenInThread();
    void listenInLocal();
};

Usage example:

#include <HttpServer.h>

int main() {
    HttpServer* hs = new HttpServer("0.0.0.0", 8080);
    httplib::Server* svr = hs->getHttplibServer();
    
    svr->Get("/hi", [](const httplib::Request&, httplib::Response& res) {
        res.set_content("Response Text", "text/plain");
    });
    
    hs->listenInThread(); // Non-blocking execution
    
    while(true) {
        // Main thread continues execution
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    
    delete hs;
    return 0;
}

Controller-based Architecture

Extend BaseController to organize request handlers:

class CustomController : public httplib::BaseController {
protected:
    void bind() override {
        server->Get("/data", BindController(&CustomController::getData, this));
        server->Post("/submit", BindController(&CustomController::postData, this));
    }
    
    void getData(const httplib::Request& req, httplib::Response& resp) {
        resp.set_content("{ \"status\": \"success\" }", "application/json");
    }
    
    void postData(const httplib::Request& req, httplib::Response& resp) {
        // Process request body
        resp.set_content("{ \"result\": \"processed\" }", "application/json");
    }
};

Integration with main application:

int main() {
    HttpServer* hs = new HttpServer(8080);
    hs->addController(new CustomController());
    hs->listenInThread();
    
    // Application logic continues
    return 0;
}

Macro-based Request Binding

For concise syntax, enable macro bindings by setting HTTPSERVER_AUTO_CONTROLLER to 1:

#define HTTPSERVER_AUTO_CONTROLLER 1

class AutoController : public httplib::AutoBashController {
    GetRequestBind(handleGet, "/auto/get");
    void handleGet(const httplib::Request& req, httplib::Response& resp) {
        resp.body = "{ \"type\": \"auto_response\" }";
    }
    
    PostRequestBind(handlePost, "/auto/post");
    void handlePost(const httplib::Request& req, httplib::Response& resp) {
        std::string requestBody = req.body;
        resp.set_content("{ \"received\": true }", "application/json");
    }
};

Thread Pool Implementation

A custom thread pool manages concurrent request processing:

class MyHttpServerThreadPool {
    std::vector<std::thread> workers;
    std::list<std::function<void()>> tasks;
    std::mutex taskMutex;
    std::condition_variable taskCondition;
    bool shutdownFlag = false;
    
    void workerFunction() {
        while(!shutdownFlag) {
            std::function<void()> task;
            {
                std::unique_lock<std::mutex> lock(taskMutex);
                taskCondition.wait(lock, [this] { 
                    return !tasks.empty() || shutdownFlag; 
                });
                if(shutdownFlag && tasks.empty()) break;
                task = std::move(tasks.front());
                tasks.pop_front();
            }
            task();
        }
    }
public:
    MyHttpServerThreadPool(size_t threadCount) {
        for(size_t i = 0; i < threadCount; ++i) {
            workers.emplace_back(&MyHttpServerThreadPool::workerFunction, this);
        }
    }
    
    void enqueue(std::function<void()> task) {
        {
            std::unique_lock<std::mutex> lock(taskMutex);
            tasks.push_back(std::move(task));
        }
        taskCondition.notify_one();
    }
};

Request Filtering Mechanism

Global request/response filters can be implemented:

class ControllerWithFilters : public httplib::AutoBashController {
    BeforeFilter(requestLogger);
    void requestLogger(const httplib::Request& req, httplib::Response& resp) {
        std::cout << "Request: " << req.method << " " << req.path << std::endl;
    }
    
    AfterFilter(responseLogger);
    void responseLogger(const httplib::Request& req, httplib::Response& resp) {
        std::cout << "Response status: " << resp.status << std::endl;
    }
};

Error Handling Configuration

Custom error handlers improve robustness:

HttpServer* serverInstance = new HttpServer("localhost", 8080);
httplib::Server* httpCore = serverInstance->getHttplibServer();

httpCore->set_error_handler([](const httplib::Request&, httplib::Response& res) {
    res.set_content("{ \"error\": \"Request failed\" }", "application/json");
});

serverInstance->listenInThread();

Resource Management

Proper cleanup prevents memory leaks:

HttpServer::~HttpServer() {
    if(server != nullptr) {
        server->stop();
        delete server;
    }
    
    for(auto controller : controllerVec) {
        delete controller;
    }
    controllerVec.clear();
}

Tags: cpp-httplib http-server cplusplus threading web-framework

Posted on Wed, 07 Oct 2026 16:12:31 +0000 by Muncey