High-Performance HTTP/1.1 & HTTP/2 Client/Server for the QB Actor Framework
qbm-http
delivers production-ready HTTP/1.1 and HTTP/2 capabilities to the QB Actor Framework, enabling you to build high-performance web services and clients with minimal code complexity. Built on QB's asynchronous I/O foundation, it provides exceptional throughput while maintaining clean, expressive APIs.
Whether you're building REST APIs, serving static content, or performing concurrent HTTP requests, qbm-http
eliminates the traditional complexity of web development without sacrificing performance.
# Add the module as a submodule
git submodule add https://github.com/isndev/qbm-http qbm/http
# QB framework setup
add_subdirectory(qb)
include_directories(${QB_PATH}/include)
# Load QB modules (automatically discovers qbm-http)
qb_load_modules("${CMAKE_CURRENT_SOURCE_DIR}/qbm")
# Link against the HTTP module
target_link_libraries(your_target PRIVATE qbm::http)
#include <http/http.h> // Core HTTP components
#include <http/middleware/all.h> // For middleware (optional)
Performance First: Built on QB's non-blocking I/O engine, ensuring high throughput and scalability without the complexity of traditional async programming.
Simplicity: Clean, modern C++ APIs that get out of your way. Build complete HTTP servers in just a few lines of code.
Flexibility: Powerful routing engine and middleware system allow complete customization while maintaining simplicity for common use cases.
Cross-Platform: Same code runs on Linux, macOS, Windows (x86_64, ARM64) with identical performance characteristics.
HTTP/2 Ready: Full HTTP/2 support with automatic negotiation, while maintaining HTTP/1.1 compatibility.
#include <http/http.h>
#include <qb/main.h>
class SimpleHttpServer : public qb::Actor, public qb::http::Server<> {
public:
bool onInit() override {
// Define routes
router().get("/", [](auto ctx) {
ctx->response().body() = "Hello from QB!";
ctx->complete();
});
router().get("/api/status", [](auto ctx) {
ctx->response().body() = R"({"status": "ok", "framework": "qb-http"})";
ctx->complete();
});
// Start listening
router().compile();
if (listen({"tcp://0.0.0.0:8080"})) {
start();
std::cout << "Server running on http://localhost:8080" << std::endl;
return true;
}
return false;
}
};
int main() {
qb::Main engine;
engine.addActor<SimpleHttpServer>(0);
engine.start();
return 0;
}
That's it! No complex threading, no callback hell, no manual memory management. Just clean, actor-based HTTP serving.
Here's a more complete example showing routing, middleware, and controllers:
#include <http/http.h>
#include <http/middleware/all.h>
#include <qb/main.h>
#include <qb/json.h>
// Custom middleware for request logging
class RequestLogger : public qb::http::IMiddleware<qb::http::DefaultSession> {
public:
std::string name() const override { return "RequestLogger"; }
void process(std::shared_ptr<qb::http::Context<qb::http::DefaultSession>> ctx) override {
auto& req = ctx->request();
qb::io::cout() << "[" << qb::time::now() << "] "
<< req.method_string() << " " << req.uri().path() << std::endl;
ctx->complete(qb::http::AsyncTaskResult::CONTINUE);
}
};
// User controller with CRUD operations
class UserController : public qb::http::Controller<qb::http::DefaultSession> {
private:
qb::json _users = qb::json::array();
public:
void initialize_routes() override {
// GET /users
get("/", [this](auto ctx) {
ctx->response().body() = _users.dump();
ctx->complete();
});
// GET /users/:id
get("/:id", [this](auto ctx) {
int user_id = std::stoi(ctx->path_param("id"));
if (user_id < _users.size()) {
ctx->response().body() = _users[user_id].dump();
} else {
ctx->response().status() = qb::http::Status::NOT_FOUND;
ctx->response().body() = R"({"error": "User not found"})";
}
ctx->complete();
});
// POST /users
post("/", [this](auto ctx) {
auto user_data = qb::json::parse(ctx->request().body().as<std::string>());
user_data["id"] = _users.size();
_users.push_back(user_data);
ctx->response().status() = qb::http::Status::CREATED;
ctx->response().body() = user_data.dump();
ctx->complete();
});
}
std::string get_node_name() const override { return "UserController"; }
};
class ApiServer : public qb::Actor, public qb::http::Server<> {
public:
bool onInit() override {
// Global middleware
router().use(std::make_shared<RequestLogger>());
// API routes group
auto api = router().group("/api/v1");
api->controller<UserController>("/users");
// Static route
router().get("/health", [](auto ctx) {
ctx->response().body() = R"({"status": "healthy", "timestamp": ")" +
qb::time::now().to_string() + "\"}";
ctx->complete();
});
router().compile();
if (listen({"tcp://0.0.0.0:8080"})) {
start();
qb::io::cout() << "API Server running on http://localhost:8080" << std::endl;
qb::io::cout() << "Try: curl http://localhost:8080/health" << std::endl;
return true;
}
return false;
}
};
int main() {
qb::Main engine;
engine.addActor<ApiServer>(0);
engine.start();
return 0;
}
#include <http/http.h>
#include <qb/main.h>
#include <filesystem>
class Http2Server : public qb::Actor {
std::unique_ptr<qb::http2::Server<>> _server;
std::filesystem::path _cert_path;
std::filesystem::path _key_path;
public:
Http2Server(const std::filesystem::path& cert_path, const std::filesystem::path& key_path)
: _cert_path(cert_path), _key_path(key_path) {
_server = qb::http2::make_server();
// Configure routes
_server->router().get("/", [](auto ctx) {
ctx->response().body() = "HTTP/2 Server powered by QB!";
ctx->complete();
});
_server->router().get("/api/data", [](auto ctx) {
// Demonstrate query parameters
auto format = ctx->request().query("format", 0, "json");
if (format == "json") {
ctx->response().body() = R"({"message": "Hello HTTP/2", "protocol": "h2"})";
} else {
ctx->response().body() = "Hello HTTP/2 (text)";
}
ctx->complete();
});
_server->router().compile();
}
bool onInit() override {
// Start HTTPS server (HTTP/2 requires TLS)
if (_server->listen({"https://0.0.0.0:8443"}, _cert_path.string(), _key_path.string())) {
_server->start();
qb::io::cout() << "HTTP/2 server running on https://localhost:8443" << std::endl;
return true;
}
return false;
}
};
int main(int argc, char* argv[]) {
if (argc < 3) {
std::cerr << "Usage: " << argv[0] << " <cert.pem> <key.pem>" << std::endl;
return 1;
}
qb::Main engine;
engine.addActor<Http2Server>(0, argv[1], argv[2]);
engine.start();
return 0;
}
#include <http/http.h>
#include <qb/main.h>
class Http2ClientActor : public qb::Actor {
public:
bool onInit() override {
// Create HTTP/2 client
auto client = qb::http2::make_client("https://localhost:8443");
client->set_connect_timeout(10.0);
// Connect to the server
client->connect([this, client](bool connected, const std::string& error) {
if (connected) {
qb::io::cout() << "Connected to HTTP/2 server!" << std::endl;
make_requests(client);
} else {
qb::io::cout() << "Connection failed: " << error << std::endl;
kill();
}
});
return true;
}
private:
void make_requests(std::shared_ptr<qb::http2::Client> client) {
// Simple GET request
qb::http::Request get_request;
get_request.method() = qb::http::Method::GET;
get_request.uri() = qb::io::uri("/");
client->push_request(get_request, [this, client](qb::http::Response response) {
qb::io::cout() << "GET Response: " << response.status().code()
<< " - " << response.body().as<std::string>() << std::endl;
// Make a POST request after GET completes
make_post_request(client);
});
}
void make_post_request(std::shared_ptr<qb::http2::Client> client) {
qb::http::Request post_request;
post_request.method() = qb::http::Method::POST;
post_request.uri() = qb::io::uri("/api/data");
post_request.add_header("Content-Type", "application/json");
post_request.body() = R"({"message": "Hello HTTP/2", "version": 2})";
client->push_request(post_request, [this, client](qb::http::Response response) {
qb::io::cout() << "POST Response: " << response.status().code()
<< " - " << response.body().as<std::string>() << std::endl;
// Make concurrent requests
make_concurrent_requests(client);
});
}
void make_concurrent_requests(std::shared_ptr<qb::http2::Client> client) {
// Prepare multiple requests
std::vector<qb::http::Request> requests;
for (int i = 1; i <= 3; ++i) {
qb::http::Request request;
request.method() = qb::http::Method::GET;
request.uri() = qb::io::uri("/api/data?id=" + std::to_string(i));
requests.push_back(std::move(request));
}
// Send batch requests (HTTP/2 multiplexing)
client->push_requests(requests, [this](std::vector<qb::http::Response> responses) {
qb::io::cout() << "Received " << responses.size() << " concurrent responses:" << std::endl;
for (size_t i = 0; i < responses.size(); ++i) {
qb::io::cout() << " Request " << (i+1) << ": "
<< responses[i].status().code() << std::endl;
}
qb::io::cout() << "All HTTP/2 requests completed!" << std::endl;
kill(); // Done
});
}
};
int main() {
qb::Main engine;
engine.addActor<Http2ClientActor>(0);
engine.start();
return 0;
}
#include <http/http.h>
#include <qb/main.h>
class HttpClientActor : public qb::Actor {
public:
bool onInit() override {
// Asynchronous GET request
qb::http::Request req(qb::io::uri("https://api.github.com/repos/isndev/qb"));
req.add_header("User-Agent", "QB-HTTP-Client/1.0");
qb::http::GET(std::move(req), [this](qb::http::async::Reply&& reply) {
if (reply.response.status() == qb::http::status::OK) {
auto data = qb::json::parse(reply.response.body().as<std::string>());
qb::io::cout() << "QB Repository stars: " << data["stargazers_count"] << std::endl;
} else {
qb::io::cout() << "Request failed: " << reply.response.status().code() << std::endl;
}
kill(); // Done with request
});
return true;
}
};
int main() {
qb::Main engine;
engine.addActor<HttpClientActor>(0);
engine.start();
return 0;
}
#include <http/http.h>
int main() {
qb::io::async::init(); // Required for sync usage
// Simple synchronous GET
qb::http::Request req(qb::io::uri("https://httpbin.org/json"));
auto response = qb::http::GET(std::move(req), 5.0 /* timeout */);
if (response.status() == qb::http::status::OK) {
std::cout << "Response: " << response.body().as<std::string>() << std::endl;
}
return 0;
}
Routing Engine:
- Path parameters (
/users/:id
) - Query parameters with defaults
- Route groups for organization
- Wildcard matching
Middleware System:
- Built-in middleware (CORS, compression, security headers)
- Custom middleware with lifecycle hooks
- Per-route or global application
Controllers:
- Object-oriented route organization
- Dependency injection support
- Hierarchical route mounting
Security & Validation:
- JWT generation and verification
- Request body/header validation
- CORS with configurable policies
- Security headers middleware
Content Handling:
- Automatic compression (Gzip/Deflate)
- JSON parsing and generation
- File serving with MIME type detection
- Multipart form handling
Performance:
- Zero-copy where possible
- Connection pooling for clients
- HTTP/2 server push
- Configurable buffer sizes
- QB Framework: This module requires the QB Actor Framework as its foundation
- C++17 compatible compiler
- CMake 3.14+
- OpenSSL: For HTTPS & JWT support. Enable with
QB_IO_WITH_SSL=ON
when building QB - Zlib: For content compression. Enable with
QB_IO_WITH_ZLIB=ON
when building QB
When using the QB project template, simply add this module as shown in the integration section above. The qb_load_modules()
function will automatically handle the configuration.
# If building outside QB framework context
find_package(qb REQUIRED)
target_link_libraries(your_target PRIVATE qbm-http)
For comprehensive technical documentation, implementation details, and in-depth guides:
📖 Complete HTTP Module Documentation
This detailed documentation covers:
- Core Concepts - HTTP fundamentals, request/response lifecycle, session management
- Body Deep Dive - Request/response body handling, streaming, and memory management
- Routing Overview - URL routing principles and pattern matching
- Defining Routes - Route definition syntax, parameters, and wildcards
- Route Groups - Organizing routes with groups and nested structures
- Controllers - Object-oriented request handling and controller patterns
- Middleware - Middleware architecture and execution pipeline
- Standard Middleware - Built-in middleware for common tasks
- Custom Middleware - Creating your own middleware components
- Request Context - Context lifecycle and data sharing
- Authentication - JWT, session-based, and custom authentication
- Validation - Request validation, sanitization, and error handling
- Error Handling - Error management strategies and custom error pages
- Async HTTP Client - Making HTTP requests with the async client
- HTTP Parsing - Low-level HTTP parsing and protocol details
- Advanced Topics - Performance tuning, security, and best practices
- HTTP/2 Protocol - HTTP/2 features, server push, and optimization
- HTTPS & SSL/TLS - Secure connections, certificates, and encryption
For comprehensive examples and detailed usage patterns, explore:
- QB Examples Repository: Real-world HTTP server and client implementations
- Full Module Documentation: Complete API reference and guides
Example Categories:
- Basic servers and routing
- Middleware development and usage
- Controller patterns and REST APIs
- JWT authentication and validation
- HTTPS and HTTP/2 configuration
- Static file serving
- Performance optimization
We welcome contributions! Please see the main QB Contributing Guidelines for details.
Licensed under the Apache License, Version 2.0. See LICENSE for details.
The QB HTTP Module builds upon the excellent work of:
- llhttp - For HTTP1.1 protocol parsing structures (I/O handled by qb-io)
This library enables the module to efficiently parse HTTP protocol messages while maintaining QB's high-performance asynchronous I/O capabilities.
Part of the QB Actor Framework ecosystem - Build the future of concurrent C++ applications.