Cinatra
C++20 实现的跨平台、header only,易用的高性能http库; modern c++(c++20), cross-platform, header-only, easy to use http framework
Install / Use
/learn @qicosmos/CinatraREADME
cinatra--一个高效易用的c++ http框架
<p align="center"> <a href="https://github.com/qicosmos/cinatra/tree/master/lang/english">English</a> | <span>中文</span> </p>| OS (Compiler Version) | Status |
|------------------------------------------------|----------------------------------------------------------------------------------------------------------|
| Ubuntu 22.04 (clang 14.0.0) | |
| Ubuntu 22.04 (gcc 11.2.0) |
|
| macOS Monterey 12 (AppleClang 14.0.0.14000029) |
|
| Windows Server 2022 (MSVC 19.33.31630.0) |
|
目录
使用cinatra常见问题汇总(FAQ)
cinatra简介
cinatra是一个高性能易用的http框架,它是用modern c++(c++20)开发的,它的目标是提供一个快速开发的c++ http框架。它的主要特点如下:
- 统一而简单的接口
- header-only
- 跨平台
- 高效
- 支持面向切面编程
cinatra目前支持了http1.1/1.0, ssl和websocket, 你可以用它轻易地开发一个http服务器,比如常见的数据库访问服务器、文件上传下载服务器、实时消息推送服务器,你也可以基于cinatra开发一个mqtt服务器。 cinatra是世界上性能最好的http服务器之一,性能测试详见性能测试
除此之外,cinatra 还提供了一个基于C++20 协程的http(https) client,包括普通get/post请求、文件上传下载和web socket、redirect、proxy等功能。
谁在用cinatra
cinatra目前被很多公司在使用,在这里可以看到谁在用cinatra.
如何使用
编译器版本
- C++20 编译器 (gcc 10.2, clang 13, Visual Studio 2022,或者更高的版本)
使用
cinatra是header-only的,引用include头文件目录,并设置如下编译选项:
如果 linux, 设置:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++20")
如果 g++ 编译,再设置:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-tree-slp-vectorize")
cinatra指令集功能使用
cinatra支持通过指令集优化其内部逻辑,其通过宏来控制是否使用指令集。使用之前请确保cpu支持。
使用如下命令即可编译带simd优化的cinatra。注意只能开启一种simd指令集优化,开启多个会导致编译失败。
cmake -DENABLE_SIMD=SSE42 .. # 启用sse4.2指令集
cmake -DENABLE_SIMD=AVX2 .. # 启用avx2指令集
cmake -DENABLE_SIMD=AARCH64 .. # arm环境下,启用neon指令集
快速示例
示例1:一个简单的hello world
#include "include/cinatra.hpp"
using namespace cinatra;
int main() {
int max_thread_num = std::thread::hardware_concurrency();
coro_http_server server(max_thread_num, 8080);
server.set_http_handler<GET, POST>("/", [](coro_http_request& req, coro_http_response& res) {
res.set_status_and_content(status_type::ok, "hello world");
});
server.sync_start();
return 0;
}
5行代码就可以实现一个简单http服务器了,用户不需要关注多少细节,直接写业务逻辑就行了。
示例2:基本用法
#include "cinatra.hpp"
struct person_t {
void foo(coro_http_request &, coro_http_response &res) {
res.set_status_and_content(status_type::ok, "ok");
}
};
async_simple::coro::Lazy<void> basic_usage() {
coro_http_server server(1, 9001);
server.set_http_handler<GET>(
"/get", [](coro_http_request &req, coro_http_response &resp) {
resp.set_status_and_content(status_type::ok, "ok");
});
server.set_http_handler<GET>(
"/coro",
[](coro_http_request &req,
coro_http_response &resp) -> async_simple::coro::Lazy<void> {
resp.set_status_and_content(status_type::ok, "ok");
co_return;
});
server.set_http_handler<GET>(
"/in_thread_pool",
[](coro_http_request &req,
coro_http_response &resp) -> async_simple::coro::Lazy<void> {
// will respose in another thread.
co_await coro_io::post([&] {
// do your heavy work here when finished work, response.
resp.set_status_and_content(status_type::ok, "ok");
});
});
server.set_http_handler<POST, PUT>(
"/post", [](coro_http_request &req, coro_http_response &resp) {
auto req_body = req.get_body();
resp.set_status_and_content(status_type::ok, std::string{req_body});
});
server.set_http_handler<GET>(
"/headers", [](coro_http_request &req, coro_http_response &resp) {
auto name = req.get_header_value("name");
auto age = req.get_header_value("age");
assert(name == "tom");
assert(age == "20");
resp.set_status_and_content(status_type::ok, "ok");
});
server.set_http_handler<GET>(
"/query", [](coro_http_request &req, coro_http_response &resp) {
auto name = req.get_query_value("name");
auto age = req.get_query_value("age");
assert(name == "tom");
assert(age == "20");
resp.set_status_and_content(status_type::ok, "ok");
});
server.set_http_handler<cinatra::GET, cinatra::POST>(
"/users/:userid/subscriptions/:subid",
[](coro_http_request &req, coro_http_response &response) {
assert(req.params_["userid"] == "ultramarines");
assert(req.params_["subid"] == "guilliman");
response.set_status_and_content(status_type::ok, "ok");
});
person_t person{};
server.set_http_handler<GET>("/person", &person_t::foo, person);
server.async_start();
std::this_thread::sleep_for(300ms); // wait for server start
coro_http_client client{};
auto result = co_await client.async_get("http://127.0.0.1:9001/get");
assert(result.status == 200);
assert(result.resp_body == "ok");
for (auto [key, val] : result.resp_headers) {
std::cout << key << ": " << val << "\n";
}
result = co_await client.async_get("/coro");
assert(result.status == 200);
result = co_await client.async_get("/in_thread_pool");
assert(result.status == 200);
result = co_await client.async_post("/post", "post string",
req_content_type::string);
assert(result.status == 200);
assert(result.resp_body == "post string");
client.add_header("name", "tom");
client.add_header("age", "20");
result = co_await client.async_get("/headers");
assert(result.status == 200);
result = co_await client.async_get("/query?name=tom&age=20");
assert(result.status == 200);
result = co_await client.async_get(
"http://127.0.0.1:9001/users/ultramarines/subscriptions/guilliman");
assert(result.status == 200);
// make sure you have installed openssl and enable CINATRA_ENABLE_SSL
#ifdef CINATRA_ENABLE_SSL
coro_http_client client2{};
result = co_await client2.async_get("https://baidu.com");
assert(result.status == 200);
#endif
}
int main() {
async_simple::coro::syncAwait(basic_usage());
}
示例3:面向切面的http服务器
#include "cinatra.hpp"
using namespace cinatra;
//日志切面
struct log_t
{
bool before(coro_http_request& req, coro_http_response& res) {
std::cout << "before log" << std::endl;
return true;
}
bool after(coro_http_request& req, coro_http_response& res) {
std::cout << "after log" << std::endl;
return true;
}
};
//校验的切面
struct check {
bool before(coro_http_request& req, coro_http_response& res) {
std::cout << "before check" << std::endl;
if (req.get_header_value("name").empty()) {
res.set_status_and_content(status_type::bad_request);
return false;
}
return true;
}
bool after(coro_http_request& req, coro_http_response& res) {
std::cout << "after check" << std::endl;
return true;
}
};
//将信息从中间件传输到处理程序
struct get_data {
bool before(coro_http_request& req, coro_http_response& res) {
req.set_aspect_data("hello world");
return true;
}
}
int main() {
coro_http_server server(std::thread::hardware_concurrency(), 8080);
server.set_http_handler<GET, POST>("/aspect", [](coro_http_request& req, coro_http_response& res) {
res.set_status_and_content(status_type::ok, "hello world");
}, check{}, log_t{});
server.set_http_handler<GET,POST>("/aspect/data", [](coro_http_request& req, coro_http_response& res) {
auto& val = req.get_aspect_data();
res.set_status_and_content(status_type::ok, std::move(val[0]));
}, get_data{});
server.sync_start();
return 0;
}
本例中有两个切面,一个校验http请求的切面,一个是日志切面,这个切面用户可以根据需求任意增加。本例会先检查http请求的合法性,如果不合法就会返回bad request,合法就会进入下一个切面,即日志切面,日志切面会打印出一个before表示进入业务逻辑之前的处理,业务逻辑完成之后会打印after表示业务逻辑结束之后的处理。
示例4:文件上传、下载、websocket
示例5:RESTful服务端路径参数设置
本代码演示如何使用RESTful路径参数。下面设置了两个RESTful API。第一个API当访问,比如访问这样的urlhttp://127.0.0.1:8080/numbers/1234/test/5678时服务器可以获取到1234和5678这两个参数,第一个RESTful API的参数是(\d+)是一个正则表达式表明只能参数只能为数字。获取第一个参数的代码是req.matches_[1]。因为每一个req不同所以每一个匹配到的参数都放在request结构体中。
同时还支持任意字符的RESTful API,即示例的第二种RESTful API"/string/:id/test/:name",要获取到对应的参数使用req.get_query_value函数即可,其参数只能为注册的变量(如果不为依然运行但是有报错),例子中参数名是id和name,要获取id参数调用req.get_query_value("id")即可。示例代码运行后,当访问http://127.0.0.1:8080/string/params_1/test/api_test时,浏览器会返回api_test字符串。
#include "cinatra.hpp"
using namespace cinatra;
int main() {
int max_thread_num = std::thread::hardware_concurrency();
coro_http_server server(max_thread_num, 8080);
server.set_http_handler<GET, POST>(
R"(/numbers/(\d+)/test/(\d+))", [](request &req, response &res) {
std::cout << " matches[1] is : " << req.matches_[1]
<< " matches[2] is: " << req.matches_[2] << std::endl;
res.set_status_and_content(status_type::ok, "hello world");
});
server.set_http_handler<GET, POST>(
"/string/:id/test/:name", [](request &req, response &res) {
std::string id = req.get_query_value("id");
std::cout << "id value is: " << id << std::endl;
std::cout << "name value is: " << std::string(req.get_query_value("name")) << std::endl;
res.set_status_and_content(status_type::ok, std::string(req.get_q
Related Skills
node-connect
339.3kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.9kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
339.3kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.9kCommit, push, and open a PR
