Overview
Cinatra is a modern C++ HTTP framework designed for high performance and ease of use. Built utilizing C++17 standards, it aims to streamline the development of robust web services. Key architectural features include:
- Consistent and minimalistic API design
- Header-only library structure
- Cross-platform compatibility
- High throughput efficiency
- Aspect-Oriented Programming (AOP) support
The framework supports HTTP 1.0/1.1, SSL encryption, and WebSocket protocols. It is suitable for constructing various server types, such as database proxies, file transfer services, real-time messaging systems, or MQTT brokers.
Prerequisites
Cinatra leverages Boost.Asio for asynchronous operations. However, it also supports standalone Asio, meaning a full Boost installation is optional.
Dependencies
- C++17 compliant compiler (e.g., GCC 7.2+, Clang 4.0+, MSSVC 2017 15.5+)
- Boost.Asio or Standalone Asio
- Boost.System
Since the library is header-only, integration requires only including the necessary header files.
Server Implementation Examples
Basic Hello World Service
The following example demonstrates initializing a server with a thread pool matching the hardware concurrency and setting up a basic route.
#include "cinatra.hpp"
int main() {
constexpr auto thread_count = std::thread::hardware_concurrency();
cinatra::http_server web_svc(thread_count);
web_svc.listen("0.0.0.0", "9090");
web_svc.set_http_handler<cinatra::GET, cinatra::POST>("/",
[](cinatra::request& req, cinatra::response& res) {
res.set_status_and_content(cinatra::status_type::ok, "Service Online");
});
web_svc.run();
return 0;
}
This configuration requires minimal boilerplate, allowing developers to focus primarily on business logic.
Handling Headers and Query Parameters
This example illustrates extracting data from request headers and query strings, including error handling for missing parameters.
#include "cinatra.hpp"
int main() {
cinatra::http_server web_svc(std::thread::hardware_concurrency());
web_svc.listen("0.0.0.0", "9090");
web_svc.set_http_handler<cinatra::GET, cinatra::POST>("/validate",
[](cinatra::request& req, cinatra::response& res) {
auto user_id = req.get_header_value("user-id");
if (user_id.empty()) {
res.set_status_and_content(cinatra::status_type::bad_request, "Missing User ID");
return;
}
auto token = req.get_query_value("token");
if (token.empty()) {
res.set_status_and_content(cinatra::status_type::bad_request);
return;
}
res.set_status_and_content(cinatra::status_type::ok, "Validation Passed");
});
web_svc.run();
return 0;
}
Aspect-Oriented Programming (AOP)
Cinatra supports aspects for cross-cutting concerns like logging and security validation. Aspects are executed in order before and after the main handler.
#include "cinatra.hpp"
struct AccessLogger {
bool before(cinatra::request& req, cinatra::response& res) {
std::cout << "[Log] Request received" << std::endl;
return true;
}
bool after(cinatra::request& req, cinatra::response& res) {
std::cout << "[Log] Request completed" << std::endl;
return true;
}
};
struct IdentityVerifier {
bool before(cinatra::request& req, cinatra::response& res) {
std::cout << "[Security] Checking credentials" << std::endl;
if (req.get_header_value("auth").empty()) {
res.set_status_and_content(cinatra::status_type::unauthorized);
return false;
}
return true;
}
bool after(cinatra::request& req, cinatra::response& res) {
return true;
}
};
int main() {
cinatra::http_server web_svc(std::thread::hardware_concurrency());
web_svc.listen("0.0.0.0", "9090");
web_svc.set_http_handler<cinatra::GET, cinatra::POST>("/secure",
[](cinatra::request& req, cinatra::response& res) {
res.set_status_and_content(cinatra::status_type::ok, "Secure Content");
},
IdentityVerifier{}, AccessLogger{});
web_svc.run();
return 0;
}
In this workflow, the verifier runs first. If authentication fails, the request halts. Otherwise, the logger records the entry, the business logic executes, and the logger records the exit.
File Upload Capabilities
The framework supports both multipart/form-data and application/octet-stream content types.
Multipart Upload
#include "cinatra.hpp"
int main() {
cinatra::http_server web_svc(std::thread::hardware_concurrency());
web_svc.listen("0.0.0.0", "9090");
web_svc.set_http_handler<cinatra::POST>("/upload_form",
[](cinatra::request& req, cinatra::response& res) {
assert(req.get_content_type() == cinatra::content_type::multipart);
const auto& uploaded_files = req.get_upload_files();
for (const auto& file : uploaded_files) {
std::cout << "Path: " << file.get_file_path()
<< " Size: " << file.get_file_size() << std::endl;
}
res.set_status_and_content(cinatra::status_type::ok, "Upload Complete");
});
web_svc.run();
return 0;
}
Octet-Stream Upload
#include "cinatra.hpp"
int main() {
cinatra::http_server web_svc(std::thread::hardware_concurrency());
web_svc.listen("0.0.0.0", "9090");
web_svc.set_http_handler<cinatra::POST>("/upload_raw",
[](cinatra::request& req, cinatra::response& res) {
assert(req.get_content_type() == cinatra::content_type::octet_stream);
const auto& uploaded_files = req.get_upload_files();
for (const auto& file : uploaded_files) {
std::cout << "Binary Size: " << file.get_file_size() << std::endl;
}
res.set_status_and_content(cinatra::status_type::ok, "Binary Received");
});
web_svc.run();
return 0;
}
File Download
Large files (typically exceeding 5MB) are automatically served using chunked trensfer encoding, supporting resume capabilities.
WebSocket Support
WebSocket connections are handled via event callbacks for opening, messaging, and errors.
#include "cinatra.hpp"
int main() {
cinatra::http_server web_svc(std::thread::hardware_concurrency());
web_svc.listen("0.0.0.0", "9090");
web_svc.set_http_handler<cinatra::GET>("/ws",
[](cinatra::request& req, cinatra::response& res) {
assert(req.get_content_type() == cinatra::content_type::websocket);
req.on(cinatra::ws_open, [](cinatra::request& req){
std::cout << "Connection Established" << std::endl;
});
req.on(cinatra::ws_message, [](cinatra::request& req) {
auto data = req.get_part_data();
std::string msg = std::string(data.data(), data.length());
// Transform message before echoing
std::transform(msg.begin(), msg.end(), msg.begin(), ::toupper);
req.get_conn()->send_ws_string(std::move(msg));
});
req.on(cinatra::ws_error, [](cinatra::request& req) {
std::cout << "Connection Error" << std::endl;
});
});
web_svc.run();
return 0;
}
Manual Event Loop Control
For scenarios requiring custom thread management, io_service_inplace allows manual control over the server loop.
#include "cinatra.hpp"
int main() {
bool service_active = true;
cinatra::http_server_<cinatra::io_service_inplace> web_svc;
web_svc.listen("9090");
web_svc.set_http_handler<cinatra::GET>("/",
[](cinatra::request& req, cinatra::response& res) {
res.set_status_and_content(cinatra::status_type::ok, "Active");
});
web_svc.set_http_handler<cinatra::GET>("/shutdown",
[&](cinatra::request& req, cinatra::response& res) {
res.set_status_and_content(cinatra::status_type::ok, "Shutting Down");
service_active = false;
web_svc.stop();
});
while(service_active) {
web_svc.poll_one();
}
return 0;
}
Client Implementation
Cinatra includes a HTTP client supporting both synchronous and asynchronous operations. Response data includes error codes, status, body, and headers.
Synchronous Requests
void inspect_response(const cinatra::response_data& result) {
// Process ec, status, body, headers
}
void execute_sync_calls() {
auto client = cinatra::client_factory::instance().new_client();
std::string endpoint = "http://example.com";
auto result = client->get(endpoint);
inspect_response(result);
auto post_result = client->post(endpoint, "payload");
inspect_response(post_result);
}
Asynchronous Requests
void execute_async_calls() {
std::string endpoint = "http://example.com";
auto client = cinatra::client_factory::instance().new_client();
client->async_get(endpoint, [](cinatra::response_data data) {
inspect_response(data);
});
}
Client File Operations
Asynchronous uploads and chunked downloads are suported. Downloads can be saved directly to disk or processed via callbacks.
void transfer_files() {
std::string url = "http://example.com/upload";
auto client = cinatra::client_factory::instance().new_client();
// Upload
client->upload(url, "data.zip", [](cinatra::response_data data) {
if (!data.ec) {
std::cout << "Upload Success" << std::endl;
}
});
// Download
client->download(url, "local_copy.zip", [](cinatra::response_data data) {
if (!data.ec) {
std::cout << "Download Success" << std::endl;
}
});
}
Technical Considerations
When implementing WebSocket handlers, be aware that callback functions may be invoked multiple times during the connection lifecycle. Business logic should be designed to handle stateless invocations appropriately. While the framework is optimized for performance, it is recommended to validate stability within a staging environment before deploying to critical production systems.