{"id":13521126,"url":"https://github.com/qicosmos/cinatra","last_synced_at":"2025-05-14T04:07:34.906Z","repository":{"id":41389464,"uuid":"119245824","full_name":"qicosmos/cinatra","owner":"qicosmos","description":"modern c++(c++20), cross-platform, header-only, easy to use http framework","archived":false,"fork":false,"pushed_at":"2025-04-29T01:56:21.000Z","size":11581,"stargazers_count":2008,"open_issues_count":38,"forks_count":375,"subscribers_count":78,"default_branch":"master","last_synced_at":"2025-04-29T02:55:22.041Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/qicosmos.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-01-28T09:45:14.000Z","updated_at":"2025-04-29T01:56:25.000Z","dependencies_parsed_at":"2023-11-26T12:24:21.451Z","dependency_job_id":"34f3daa7-4d5d-4b46-9068-4f0507d57702","html_url":"https://github.com/qicosmos/cinatra","commit_stats":{"total_commits":1017,"total_committers":40,"mean_commits":25.425,"dds":"0.27040314650934116","last_synced_commit":"3dfbd3ec11034fe9c3fb388d32faca66403d5f5c"},"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qicosmos%2Fcinatra","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qicosmos%2Fcinatra/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qicosmos%2Fcinatra/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qicosmos%2Fcinatra/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qicosmos","download_url":"https://codeload.github.com/qicosmos/cinatra/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254069216,"owners_count":22009511,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T06:00:28.980Z","updated_at":"2025-05-14T04:07:34.805Z","avatar_url":"https://github.com/qicosmos.png","language":"C++","funding_links":[],"categories":["C++","HTTP and the Web","网络"],"sub_categories":[],"readme":"# cinatra--一个高效易用的c++ http框架\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/qicosmos/cinatra/tree/master/lang/english\"\u003eEnglish\u003c/a\u003e | \u003cspan\u003e中文\u003c/span\u003e\n\u003c/p\u003e\n\n| OS (Compiler Version)                          | Status                                                                                                   |\n|------------------------------------------------|----------------------------------------------------------------------------------------------------------|\n| Ubuntu 22.04 (clang 14.0.0)                    | ![win](https://github.com/qicosmos/cinatra/actions/workflows/linux_clang.yml/badge.svg?branch=master) |\n| Ubuntu 22.04 (gcc 11.2.0)                      | ![win](https://github.com/qicosmos/cinatra/actions/workflows/linux_gcc.yml/badge.svg?branch=master)   |\n| macOS Monterey 12 (AppleClang 14.0.0.14000029) | ![win](https://github.com/qicosmos/cinatra/actions/workflows/mac.yml/badge.svg?branch=master)         |\n| Windows Server 2022 (MSVC 19.33.31630.0)       | ![win](https://github.com/qicosmos/cinatra/actions/workflows/windows.yml/badge.svg?branch=master)     |\n\n# 目录\n\n## [使用cinatra常见问题汇总(FAQ)](https://github.com/qicosmos/cinatra/wiki)\n\n[基于C++20 协程的http库](lang/coroutine_based_http_lib.md)\n\n* [cinatra简介](#cinatra简介)\n* [如何使用](#如何使用)\n* [快速示例](#快速示例)\n* [性能测试](#性能测试)\n* [注意事项](#注意事项)\n* [roadmap](#roadmap)\n* [联系方式](#联系方式)\n\n# cinatra简介\n[cinatra](https://github.com/qicosmos/cinatra)是一个高性能易用的http框架，它是用modern c++(c++20)开发的，它的目标是提供一个快速开发的c++ http框架。它的主要特点如下：\n\n1. 统一而简单的接口\n2. header-only\n3. 跨平台\n4. 高效\n5. 支持面向切面编程\n\ncinatra目前支持了http1.1/1.0, ssl和websocket, 你可以用它轻易地开发一个http服务器，比如常见的数据库访问服务器、文件上传下载服务器、实时消息推送服务器，你也可以基于cinatra开发一个mqtt服务器。\ncinatra是世界上性能最好的http服务器之一，性能测试详见[性能测试](#性能测试)\n\n除此之外，cinatra 还提供了一个基于C++20 协程的http(https) client，包括普通get/post请求、文件上传下载和web socket、redirect、proxy等功能。\n\n## 谁在用cinatra\n\ncinatra目前被很多公司在使用，在这里可以看到[谁在用cinatra](https://github.com/qicosmos/cinatra/wiki/%E8%B0%81%E5%9C%A8%E7%94%A8cinatra).\n\n# 如何使用\n\n## 编译器版本\n\n1. C++20 编译器 (gcc 10.2, clang 13, Visual Studio 2022,或者更高的版本)\n\n## 使用\ncinatra是header-only的，引用include头文件目录，并设置如下编译选项：\n\n如果 linux， 设置:\n\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS}  -pthread -std=c++20\")\n\n如果 g++ 编译，再设置：\n\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -fcoroutines\")\n\nset(CMAKE_CXX_FLAGS_RELEASE \"${CMAKE_CXX_FLAGS_RELEASE} -fno-tree-slp-vectorize\")\n\n## cinatra指令集功能使用\n\ncinatra支持通过指令集优化其内部逻辑，其通过宏来控制是否使用指令集。使用之前请确保cpu支持。\n\n使用如下命令即可编译带simd优化的cinatra。注意只能开启一种simd指令集优化,开启多个会导致编译失败。\n\n```shell\ncmake -DENABLE_SIMD=SSE42 .. # 启用sse4.2指令集\ncmake -DENABLE_SIMD=AVX2 .. # 启用avx2指令集\ncmake -DENABLE_SIMD=AARCH64 .. # arm环境下,启用neon指令集\n```\n\n# 快速示例\n\n## 示例1：一个简单的hello world\n```c++\n\t#include \"include/cinatra.hpp\"\n\tusing namespace cinatra;\n\t\n\tint main() {\n\t\tint max_thread_num = std::thread::hardware_concurrency();\n\t\tcoro_http_server server(max_thread_num, 8080);\n\t\tserver.set_http_handler\u003cGET, POST\u003e(\"/\", [](coro_http_request\u0026 req, coro_http_response\u0026 res) {\n\t\t\tres.set_status_and_content(status_type::ok, \"hello world\");\n\t\t});\n\n\t\tserver.sync_start();\n\t\treturn 0;\n\t}\n```\n\n5行代码就可以实现一个简单http服务器了，用户不需要关注多少细节，直接写业务逻辑就行了。\n\n## 示例2：基本用法\n```c++\n#include \"cinatra.hpp\"\n\nstruct person_t {\n  void foo(coro_http_request \u0026, coro_http_response \u0026res) {\n    res.set_status_and_content(status_type::ok, \"ok\");\n  }\n};\n\nasync_simple::coro::Lazy\u003cvoid\u003e basic_usage() {\n  coro_http_server server(1, 9001);\n  server.set_http_handler\u003cGET\u003e(\n      \"/get\", [](coro_http_request \u0026req, coro_http_response \u0026resp) {\n        resp.set_status_and_content(status_type::ok, \"ok\");\n      });\n\n  server.set_http_handler\u003cGET\u003e(\n      \"/coro\",\n      [](coro_http_request \u0026req,\n         coro_http_response \u0026resp) -\u003e async_simple::coro::Lazy\u003cvoid\u003e {\n        resp.set_status_and_content(status_type::ok, \"ok\");\n        co_return;\n      });\n\n  server.set_http_handler\u003cGET\u003e(\n      \"/in_thread_pool\",\n      [](coro_http_request \u0026req,\n         coro_http_response \u0026resp) -\u003e async_simple::coro::Lazy\u003cvoid\u003e {\n        // will respose in another thread.\n        co_await coro_io::post([\u0026] {\n          // do your heavy work here when finished work, response.\n          resp.set_status_and_content(status_type::ok, \"ok\");\n        });\n      });\n\n  server.set_http_handler\u003cPOST, PUT\u003e(\n      \"/post\", [](coro_http_request \u0026req, coro_http_response \u0026resp) {\n        auto req_body = req.get_body();\n        resp.set_status_and_content(status_type::ok, std::string{req_body});\n      });\n\n  server.set_http_handler\u003cGET\u003e(\n      \"/headers\", [](coro_http_request \u0026req, coro_http_response \u0026resp) {\n        auto name = req.get_header_value(\"name\");\n        auto age = req.get_header_value(\"age\");\n        assert(name == \"tom\");\n        assert(age == \"20\");\n        resp.set_status_and_content(status_type::ok, \"ok\");\n      });\n\n  server.set_http_handler\u003cGET\u003e(\n      \"/query\", [](coro_http_request \u0026req, coro_http_response \u0026resp) {\n        auto name = req.get_query_value(\"name\");\n        auto age = req.get_query_value(\"age\");\n        assert(name == \"tom\");\n        assert(age == \"20\");\n        resp.set_status_and_content(status_type::ok, \"ok\");\n      });\n\n  server.set_http_handler\u003ccinatra::GET, cinatra::POST\u003e(\n      \"/users/:userid/subscriptions/:subid\",\n      [](coro_http_request \u0026req, coro_http_response \u0026response) {\n        assert(req.params_[\"userid\"] == \"ultramarines\");\n        assert(req.params_[\"subid\"] == \"guilliman\");\n        response.set_status_and_content(status_type::ok, \"ok\");\n      });\n\n  person_t person{};\n  server.set_http_handler\u003cGET\u003e(\"/person\", \u0026person_t::foo, person);\n\n  server.async_start();\n  std::this_thread::sleep_for(300ms);  // wait for server start\n\n  coro_http_client client{};\n  auto result = co_await client.async_get(\"http://127.0.0.1:9001/get\");\n  assert(result.status == 200);\n  assert(result.resp_body == \"ok\");\n  for (auto [key, val] : result.resp_headers) {\n    std::cout \u003c\u003c key \u003c\u003c \": \" \u003c\u003c val \u003c\u003c \"\\n\";\n  }\n\n  result = co_await client.async_get(\"/coro\");\n  assert(result.status == 200);\n\n  result = co_await client.async_get(\"/in_thread_pool\");\n  assert(result.status == 200);\n\n  result = co_await client.async_post(\"/post\", \"post string\",\n                                      req_content_type::string);\n  assert(result.status == 200);\n  assert(result.resp_body == \"post string\");\n\n  client.add_header(\"name\", \"tom\");\n  client.add_header(\"age\", \"20\");\n  result = co_await client.async_get(\"/headers\");\n  assert(result.status == 200);\n\n  result = co_await client.async_get(\"/query?name=tom\u0026age=20\");\n  assert(result.status == 200);\n\n  result = co_await client.async_get(\n      \"http://127.0.0.1:9001/users/ultramarines/subscriptions/guilliman\");\n  assert(result.status == 200);\n\n  // make sure you have installed openssl and enable CINATRA_ENABLE_SSL\n#ifdef CINATRA_ENABLE_SSL\n  coro_http_client client2{};\n  result = co_await client2.async_get(\"https://baidu.com\");\n  assert(result.status == 200);\n#endif\n}\n\nint main() {\n  async_simple::coro::syncAwait(basic_usage());\n}\n```\n\n## 示例3：面向切面的http服务器\n```c++\n\t#include \"cinatra.hpp\"\n\tusing namespace cinatra;\n\n\t//日志切面\n\tstruct log_t\n\t{\n\t\tbool before(coro_http_request\u0026 req, coro_http_response\u0026 res) {\n\t\t\tstd::cout \u003c\u003c \"before log\" \u003c\u003c std::endl;\n\t\t\treturn true;\n\t\t}\n\t\n\t\tbool after(coro_http_request\u0026 req, coro_http_response\u0026 res) {\n\t\t\tstd::cout \u003c\u003c \"after log\" \u003c\u003c std::endl;\n\t\t\treturn true;\n\t\t}\n\t};\n\t\n\t//校验的切面\n\tstruct check  {\n\t\tbool before(coro_http_request\u0026 req, coro_http_response\u0026 res) {\n\t\t\tstd::cout \u003c\u003c \"before check\" \u003c\u003c std::endl;\n\t\t\tif (req.get_header_value(\"name\").empty()) {\n\t\t\t\tres.set_status_and_content(status_type::bad_request);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\n\t\tbool after(coro_http_request\u0026 req, coro_http_response\u0026 res) {\n\t\t\tstd::cout \u003c\u003c \"after check\" \u003c\u003c std::endl;\n\t\t\treturn true;\n\t\t}\n\t};\n\n\t//将信息从中间件传输到处理程序\n\tstruct get_data  {\n\t\tbool before(coro_http_request\u0026 req, coro_http_response\u0026 res) {\n\t\t\treq.set_aspect_data(\"hello world\");\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tint main() {\n\t\tcoro_http_server server(std::thread::hardware_concurrency(), 8080);\n\t\tserver.set_http_handler\u003cGET, POST\u003e(\"/aspect\", [](coro_http_request\u0026 req, coro_http_response\u0026 res) {\n\t\t\tres.set_status_and_content(status_type::ok, \"hello world\");\n\t\t}, check{}, log_t{});\n\n\t\tserver.set_http_handler\u003cGET,POST\u003e(\"/aspect/data\", [](coro_http_request\u0026 req, coro_http_response\u0026 res) {\n\t\t\tauto\u0026 val = req.get_aspect_data();\n\t\t\tres.set_status_and_content(status_type::ok, std::move(val[0]));\n\t\t}, get_data{});\n\n\t\tserver.sync_start();\n\t\treturn 0;\n\t}\n```\n本例中有两个切面，一个校验http请求的切面，一个是日志切面，这个切面用户可以根据需求任意增加。本例会先检查http请求的合法性，如果不合法就会返回bad request，合法就会进入下一个切面，即日志切面，日志切面会打印出一个before表示进入业务逻辑之前的处理，业务逻辑完成之后会打印after表示业务逻辑结束之后的处理。\n\n## 示例4：文件上传、下载、websocket\n见[example中的例子](example/main.cpp)\n\n## 示例5：RESTful服务端路径参数设置\n本代码演示如何使用RESTful路径参数。下面设置了两个RESTful API。第一个API当访问，比如访问这样的url`http://127.0.0.1:8080/numbers/1234/test/5678`时服务器可以获取到1234和5678这两个参数，第一个RESTful API的参数是`(\\d+)`是一个正则表达式表明只能参数只能为数字。获取第一个参数的代码是`req.matches_[1]`。因为每一个req不同所以每一个匹配到的参数都放在`request`结构体中。\n\n同时还支持任意字符的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`字符串。\n\n\t#include \"cinatra.hpp\"\n\tusing namespace cinatra;\n\t\n\tint main() {\n\t\tint max_thread_num = std::thread::hardware_concurrency();\n\t\tcoro_http_server server(max_thread_num, 8080);\n\n\t\tserver.set_http_handler\u003cGET, POST\u003e(\n\t\t\tR\"(/numbers/(\\d+)/test/(\\d+))\", [](request \u0026req, response \u0026res) {\n\t\t\t\tstd::cout \u003c\u003c \" matches[1] is : \" \u003c\u003c req.matches_[1]\n\t\t\t\t\t\t\u003c\u003c \" matches[2] is: \" \u003c\u003c req.matches_[2] \u003c\u003c std::endl;\n\n\t\t\t\tres.set_status_and_content(status_type::ok, \"hello world\");\n\t\t\t});\n\n\t\tserver.set_http_handler\u003cGET, POST\u003e(\n\t\t\t\"/string/:id/test/:name\", [](request \u0026req, response \u0026res) {\n\t\t\t\tstd::string id = req.get_query_value(\"id\");\n\t\t\t\tstd::cout \u003c\u003c \"id value is: \" \u003c\u003c id \u003c\u003c std::endl;\n\t\t\t\tstd::cout \u003c\u003c \"name value is: \" \u003c\u003c std::string(req.get_query_value(\"name\")) \u003c\u003c std::endl;\n\t\t\t\tres.set_status_and_content(status_type::ok, std::string(req.get_query_value(\"name\")));\n\t\t\t});\n\n\t\tserver.sync_start();\n\t\treturn 0;\n\t}\n\n## 反向代理\ncinatra 支持反向代理也很简单，3步5行代码就可以了。\n先看一个简单的例子：\n\n```c++\n  reverse_proxy proxy_rr(10, 8091);\n  proxy_rr.add_dest_host(\"127.0.0.1:9001\");\n  proxy_rr.add_dest_host(\"127.0.0.1:9002\");\n  proxy_rr.add_dest_host(\"127.0.0.1:9003\");\n  proxy_rr.start_reverse_proxy\u003cGET, POST\u003e(\"/rr\", true,\n                                          coro_io::load_blance_algorithm::RR);\n```\n第一步创建一个代理服务器，设置其线程数和端口；\n第二步添加需要访问的服务器列表；\n第三步启动代理服务，设置loadbalance 策略，这里选择的是round robin 策略。\n\n在浏览器或者client里访问http://127.0.0.1:8091/rr 就会根据RR 策略选择三个服务器中的一个。\n\n如果要选择random 策略就设置为coro_io::load_blance_algorithm::random。\n\n如果要选择weight round robin 策略，就需要设置服务器权重。\n\n```c++\n  reverse_proxy proxy_wrr(10, 8090);\n  proxy_wrr.add_dest_host(\"127.0.0.1:9001\", 10);\n  proxy_wrr.add_dest_host(\"127.0.0.1:9002\", 5);\n  proxy_wrr.add_dest_host(\"127.0.0.1:9003\", 5);\n  proxy_wrr.start_reverse_proxy\u003cGET, POST\u003e(\"/wrr\", true,\n                                           coro_io::load_blance_algorithm::WRR);\n```\n在浏览器或者client里访问http://127.0.0.1:8090/wrr ，第一次和第二次会返回9001服务器的结果，第三次返回9002服务器的结果，第四次返回9003服务器的结果，第五次又重新返回9001服务器的结果，这就是WRR的策略。\n\n## cinatra客户端使用\n\n[使用文档](lang/coro_http_client_introduction.md)\n\n### sync_send get/post message\n\n```\nvoid test_sync_client() {\n  {\n    std::string uri = \"http://www.baidu.com\";\n    coro_http_client client{};\n    auto result = client.get(uri);\n    assert(!result.net_err);\n    print(result.resp_body);\n\n    result = client.post(uri, \"hello\", req_content_type::json);\n    print(result.resp_body);\n  }\n\n  {\n    coro_http_client client{};\n    std::string uri = \"http://cn.bing.com\";\n    auto result = client.get(uri);\n    assert(!result.net_err);\n    print(result.resp_body);\n\n    result = client.post(uri, \"hello\", req_content_type::json);\n    print(result.resp_body);\n  }\n}\n\n#ifdef CINATRA_ENABLE_SSL\nvoid test_coro_http_client() {\n  using namespace cinatra;\n  coro_http_client client{};\n  client.init_ssl(\"../../include/cinatra\", \"server.crt\"); // optinal 一般情况下可以不调用这一行\n  auto data = client.get(\"https://www.bing.com\");\n  std::cout \u003c\u003c data.resp_body \u003c\u003c \"\\n\";\n  data = client.get(\"https://www.bing.com\");\n  std::cout \u003c\u003c data.resp_body \u003c\u003c \"\\n\";\n}\n#endif\n```\n\n### async get/post message\n\n```\nasync_simple::coro::Lazy\u003cvoid\u003e test_async_client() {\n  std::string uri = \"http://www.baidu.com\";\n\n  {\n    coro_http_client client{};\n    auto data = co_await client.async_get(uri);\n    print(data.status);\n\n    data = co_await client.async_get(uri);\n    print(data.status);\n\n    data = co_await client.async_post(uri, \"hello\", req_content_type::string);\n    print(data.status);\n  }\n\n#ifdef CINATRA_ENABLE_SSL\n  std::string uri2 = \"https://www.baidu.com\";\n  std::string uri3 = \"https://cn.bing.com\";\n  coro_http_client client{};\n  client.init_ssl(\"../../include/cinatra\", \"server.crt\");\n  data = co_await client.async_get(uri2);\n  print(data.status);\n\n  data = co_await client.async_get(uri3);\n  print(data.status);\n#endif\n}\n```\n\n### upload(multipart) file\n```cpp\nvoid start_server() {\n  coro_http_server server(1, 9001);\n  server.set_http_handler\u003cPOST\u003e(\n      \"/form_data\",\n      [](coro_http_request \u0026req,\n         coro_http_response \u0026resp) -\u003e async_simple::coro::Lazy\u003cvoid\u003e {\n        assert(req.get_content_type() == content_type::multipart);\n        auto boundary = req.get_boundary();\n        multipart_reader_t multipart(req.get_conn());\n        while (true) {\n          auto part_head = co_await multipart.read_part_head(boundary);\n          if (part_head.ec) {\n            co_return;\n          }\n\n          std::cout \u003c\u003c part_head.name \u003c\u003c \"\\n\";\n          std::cout \u003c\u003c part_head.filename \u003c\u003c \"\\n\";// if form data, no filename\n\n          auto part_body = co_await multipart.read_part_body(boundary);\n          if (part_body.ec) {\n            co_return;\n          }\n\n          std::cout \u003c\u003c part_body.data \u003c\u003c \"\\n\";\n\n          if (part_body.eof) {\n            break;\n          }\n        }\n\n        resp.set_status_and_content(status_type::ok, \"multipart finished\");\n      });\n  server.start();      \n}\n```\n```\nasync_simple::coro::Lazy\u003cvoid\u003e test_upload() {\n  std::string uri = \"http://127.0.0.1:9001/form_data\";\n  coro_http_client client{};\n\n  client.add_str_part(\"hello\", \"coro_http_client\");\n  client.add_file_part(\"test\", \"yourfile.jpg\");\n  result = co_await client.async_upload_multipart(uri);\n  print(result.status);\n  std::cout \u003c\u003c \"upload finished\\n\";\n}\n```\n\n### download file(ranges and chunked)\n\n```\nasync_simple::coro::Lazy\u003cvoid\u003e test_download() {\n  coro_http_client client{};\n  std::string uri =\n      \"http://www.httpwatch.com/httpgallery/chunked/chunkedimage.aspx\";\n  std::string filename = \"test.jpg\";\n\n  std::error_code ec{};\n  std::filesystem::remove(filename, ec);\n  auto r = co_await client.async_download(uri, filename);\n  assert(!r.net_err);\n  assert(r.status == 200);\n  std::cout \u003c\u003c \"download finished\\n\";\n}\n```\n\n### web socket\n```c++\nasync_simple::coro::Lazy\u003cvoid\u003e test_websocket() {\n  coro_http_client client{};\n  auto r = co_await client.connect(\"ws://localhost:8090/ws\");\n  if (r.net_err) {\n    co_return;\n  }\n\n  co_await client.write_websocket(\"hello websocket\");\n  auto data = co_await client.read_websocket();\n  CHECK(data.resp_body == \"hello websocket\");\n  co_await client.write_websocket(\"test again\");\n  data = co_await client.read_websocket();\n  CHECK(data.resp_body == \"test again\");\n  co_await client.write_websocket(\"ws close\");\n  data = co_await client.read_websocket();\n  CHECK(data.net_err == asio::error::eof);\n  CHECK(data.resp_body == \"ws close\");\n}\n```\n\n## 基于cinatra客户端的http/https压测工具使用\n\ncinatra提供了一个高性能的http1.1 压测工具, 它是基于coro_http_client 实现的，内部通过多线程和协程实现了高效的压测，能够在单核或多核cpu上发送大量请求以此来测试服务器性能。\n\n### 基础使用\n\n```shell\n./cinatra_press_tool -t 4 -c 40 -d 30s http://127.0.0.1\n```\n\n上面的命令代表使用4个线程并且保持40个连接打开(协程)对网址`http://127.0.0.1`进行30s的基准测试。\n\n输出如下:\n```\nRunning 30s test @ http://127.0.0.1\n  4 threads and 40 connections\n  Thread Status   Avg   Max   Variation   Stdev\n    Latency   4.12ms     8.15ms     3.367ms     1.835ms\n  462716 requests in 30.001s, 592.198250MB read, total: 462716, errors: 0\nRequests/sec:     15423.86666667\nTransfer/sec:     19.739390MB\n```\n\n### 命令行参数选项\n\n```\n -c, --connections    total number of HTTP connections to keep open with \n \t\t\t\t\t  each thread handling N = connections/threads (int)\n -d, --duration       duration of the test, e.g. 2s, 2m, 2h (string [=15s])\n -t, --threads        total number of threads to use (int [=1])\n -H, --headers        HTTP headers to add to request, e.g. \"User-Agent: coro_http_press\"\n            \t\t  add multiple http headers in a request need to be separated by ' \u0026\u0026 '\n            \t\t  e.g. \"User-Agent: coro_http_press \u0026\u0026 x-frame-options: SAMEORIGIN\" (string [=])\n -r, --readfix        read fixed response (int [=0])\n -?, --help           print this message\n```\n\n这里有两个参数与wrk不同\n\n`-H`参数，它表示添加http头到http请求中，该参数不止可以添加一个http头还可以以` \u0026\u0026 `符号(4个字符)为分隔符来组装多个http头到http请求。\n比如`-H User-Agent: coro_http_press`就是添加一个http头，而`-H User-Agent: coro_http_press \u0026\u0026 x-frame-options: SAMEORIGIN`则为添加`User-Agent: coro_http_press`和`x-frame-options: SAMEORIGIN`两个http头到http请求。添加三个以及多个http头的方法和上述方法相同。\n\n\n`-r`参数，它表示是否读固定长度的response，这个参数可以避免频繁的解析response优化性能，有些服务器对于相同的请求返回的长度可能不同，这种情况下不设置这个参数或者将它设置为0。\n\n\n# 性能测试\n## 测试用例：\n\n![qps](lang/qps.png \"qps\")\n\n![qps-pipeline](lang/qps-pipeline.png \"qps-pipeline\")\n\n# 注意事项\n\nwebsocket的业务函数是会多次进入的，因此写业务逻辑的时候需要注意，推荐按照示例中的方式去做。\n\n# deps\ncinatra depends on asio and async_simple.\n\npress_tool depends on cinatra and cmdline.\n\n# submodule\n\nA submodule of cinatra is iguana.\n\nWhen you want to use this submodule, using the command `git submodule init` will pull the iguana library.\n\nIf you want to use the latest iguana, please use the command `git submodule update --remote`.\n\n# 联系方式\n\npurecpp@163.com\n\nqq群：545605838\n\n[http://purecpp.cn/](http://purecpp.cn/ \"purecpp\")\n\n[https://github.com/qicosmos/cinatra](https://github.com/qicosmos/cinatra \"cinatra\")\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqicosmos%2Fcinatra","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqicosmos%2Fcinatra","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqicosmos%2Fcinatra/lists"}