{"id":13720948,"url":"https://github.com/sprinfall/webcc","last_synced_at":"2025-09-10T18:06:36.558Z","repository":{"id":47602482,"uuid":"92490850","full_name":"sprinfall/webcc","owner":"sprinfall","description":"Lightweight C++ HTTP client and server library based on Asio for embedding purpose.","archived":false,"fork":false,"pushed_at":"2024-06-04T13:22:52.000Z","size":7861,"stargazers_count":276,"open_issues_count":11,"forks_count":63,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-06-01T16:20:08.763Z","etag":null,"topics":["asio","beast","boost","cplusplus","cpp","http","requests","rest","webcc","webservice"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sprinfall.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}},"created_at":"2017-05-26T08:43:48.000Z","updated_at":"2025-02-11T06:31:31.000Z","dependencies_parsed_at":"2023-11-25T04:21:10.832Z","dependency_job_id":"99dd690d-66f9-4637-802c-0390b30863ef","html_url":"https://github.com/sprinfall/webcc","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/sprinfall/webcc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sprinfall%2Fwebcc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sprinfall%2Fwebcc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sprinfall%2Fwebcc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sprinfall%2Fwebcc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sprinfall","download_url":"https://codeload.github.com/sprinfall/webcc/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sprinfall%2Fwebcc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274501680,"owners_count":25297439,"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","status":"online","status_checked_at":"2025-09-10T02:00:12.551Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["asio","beast","boost","cplusplus","cpp","http","requests","rest","webcc","webservice"],"created_at":"2024-08-03T01:01:10.272Z","updated_at":"2025-09-10T18:06:36.449Z","avatar_url":"https://github.com/sprinfall.png","language":"C++","readme":"# Webcc - C++ HTTP Library\n\n[__中文版 README__](README_zh_CN.md)\n\nLightweight C++ HTTP __client and server__ library based on [Boost Asio](https://www.boost.org/doc/libs/release/libs/asio/) for __embedding__ purpose.\n\n=\u003e [Build Instructions](doc/Build-Instructions.md) ([__中文版__](doc/Build-Instructions_zh_CN.md))\n\nGit repo: https://github.com/sprinfall/webcc. Please check this one instead of the forked for the latest features.\n\n**Contents**\n* [Overview](#overview)\n* [Client API](#client-api)\n    * [A Complete Example](#a-complete-example)\n    * [Request Builder](#request-builder)\n    * [HTTPS](#https)\n    * [Invoking GitHub REST API](#invoking-github-rest-api)\n    * [Authorization](#authorization)\n    * [Keep-Alive (Persistent Connection)](#keep-alive)\n    * [POST Request](#post-request)\n    * [Downloading Files](#downloading-files)\n    * [Uploading Files](#uploading-files)\n    * [Client API and Threads](#client-api-and-threads)\n* [Server API](#server-api)\n    * [A Minimal Server](#a-minimal-server)\n    * [URL Route](#url-route)\n    * [Running A Server](#running-a-server)\n    * [Response Builder](#response-builder)\n    * [REST Book Server](#rest-book-server)\n* [IPv6 Support](#ipv6-support)\n    * [IPv6 Server](#ipv6-server)\n    * [IPv6 Client](#ipv6-client)\n    \n## Overview\n\n- Cross-platform: Windows, Linux and MacOS\n- Easy-to-use client API inspired by Python [requests](https://github.com/psf/requests)\n- IPv6 support\n- SSL/HTTPS support with OpenSSL\n- GZip compression support with Zlib (optional)\n- Persistent (Keep-Alive) connections\n- Data streaming\n    - for uploading and downloading large files on client\n    - for serving and receiving large files on server\n- Basic \u0026 Token authorization\n- Timeout control\n- Source code follows [Google C++ Style](https://google.github.io/styleguide/cppguide.html)\n- Automation tests and unit tests included\n\n## Client API\n\n### A Complete Example\n\nLet's start from a complete client example:\n\n```cpp\n#include \u003ciostream\u003e\n\n#include \"webcc/client_session.h\"\n#include \"webcc/logger.h\"\n\nint main() {\n  // Configure logger to print only to console.\n  WEBCC_LOG_INIT(\"\", webcc::LOG_CONSOLE);\n  \n  // Session provides convenient request APIs, stores request configurations\n  // and manages persistent connenctions.\n  webcc::ClientSession session;\n\n  // Catch exceptions for error handling.\n  try {\n    // Send a HTTP GET request.\n    auto r =\n        session.Send(webcc::RequestBuilder{}.Get(\"http://httpbin.org/get\")());\n\n    // Print the response data.\n    std::cout \u003c\u003c r-\u003edata() \u003c\u003c std::endl;\n\n  } catch (const webcc::Error\u0026 error) {\n    std::cerr \u003c\u003c error \u003c\u003c std::endl;\n  }\n\n  return 0;\n}\n```\n\n### Request Builder\n\nAs you can see, a helper class named `RequestBuilder` is used to chain the parameters and finally build a request object. Please pay attention to the `()` operator.\n\nURL query parameters can be easily added through `Query()` method:\n\n```cpp\nsession.Send(webcc::RequestBuilder{}\n                 .Get(\"http://httpbin.org/get\")\n                 .Query(\"key1\", \"value1\")\n                 .Query(\"key2\", \"value2\")());\n```\n\nAdding additional headers is also easy:\n\n```cpp\nsession.Send(webcc::RequestBuilder{}\n                 .Get(\"http://httpbin.org/get\")\n                 .Header(\"Accept\", \"application/json\")());\n```\n\n### HTTPS\n\nAccessing HTTPS has no difference from HTTP:\n\n```cpp\nsession.Send(webcc::RequestBuilder{}.Get(\"https://httpbin.org/get\")());\n```\n\n*NOTE: The HTTPS/SSL support requires the build option `WEBCC_ENABLE_SSL` to be enabled.*\n\n### Invoking GitHub REST API\n\nListing GitHub public events is not a big deal:\n\n```cpp\nauto r = session.Send(\n    webcc::RequestBuilder{}.Get(\"https://api.github.com/events\")());\n```\n\nYou can then parse `r-\u003edata()` to JSON object with your favorite JSON library. My choice for the examples is [jsoncpp](https://github.com/open-source-parsers/jsoncpp). But Webcc itself doesn't understand JSON nor require one. It's up to you to choose the most appropriate JSON library.\n\n`RequestBuilder` provides a lot of functions for you to customize the request. Let's see more examples.\n\n### Authorization\n\nIn order to list the followers of an authorized GitHub user, you need either **Basic Authorization**:\n\n```cpp\nsession.Send(webcc::RequestBuilder{}.\n             Get(\"https://api.github.com/user/followers\").\n             AuthBasic(\u003clogin\u003e, \u003cpassword\u003e)\n             ());\n```\n\nOr **Token Authorization**:\n\n```cpp\nsession.Send(webcc::RequestBuilder{}\n                 .Get(\"https://api.github.com/user/followers\")\n                 .AuthToken(token)());\n```\n\n### Keep-Alive\n\nThough **Keep-Alive** (i.e., Persistent Connection) is a good feature and enabled by default, you can turn it off:\n\n```cpp\nauto r = session.Send(\n    webcc::RequestBuilder{}.Get(\"http://httpbin.org/get\").KeepAlive(false)());\n```\n\nThe API for other HTTP requests is no different from GET.\n\n### POST Request\n\nA POST request needs a body which is normally a JSON string for REST API. Let's post a small UTF-8 encoded JSON string:\n\n```cpp\nsession.Send(webcc::RequestBuilder{}\n                 .Post(\"http://httpbin.org/post\")\n                 .Body(\"{'name'='Adam', 'age'=20}\")\n                 .Json()\n                 .Utf8()());\n```\n\nThe body of a POST request could be any content other than JSON string.\n\nIt could be the binary data of a file. See [Uploading Files](#uploading-files).\n\nIt could be a form urlencoded string:\n\n```cpp\nwebcc::ClientSession session;\nsession.SetContentType(\"application/x-www-form-urlencoded\", \"utf8\");\n\n// Use UrlQuery to compose the urlencoded string.\n// Don't use RequestBuilder::Query() which is dedicated to GET.\nwebcc::UrlQuery query;\nquery.Add(\"key1\", \"value1\");\nquery.Add(\"key2\", \"value2\");\n// ...\n\nauto r = session.Send(webcc::RequestBuilder{}\n                          .Post(\"http://httpbin.org/post\")\n                          .Body(query.ToString())());\n```\n\nPlease see [examples/form_urlencoded_client.cc](examples/form_urlencoded_client.cc) for more details.\n\n### Downloading Files\n\nWebcc has the ability to stream large response data to a file. This is especially useful when downloading files.\n\n```cpp\n// stream = true\nauto r = session.Send(\n    webcc::RequestBuilder{}.Get(\"http://httpbin.org/image/jpeg\")(), true);\n\n// Move the streamed file to your destination.\nr-\u003efile_body()-\u003eMove(\"./wolf.jpeg\");\n```\n\n### Uploading Files\n\nStreaming is also available for uploading:\n\n```cpp\nauto r = session.Send(\n    webcc::RequestBuilder{}.Post(\"http://httpbin.org/post\").File(path)());\n```\n\nThe file will not be loaded into the memory all at once, instead, it will be read and sent piece by piece.\n\nPlease note that `Content-Length` header will still be set to the true size of the file, this is different from the handling of chunked data (`Transfer-Encoding: chunked`).\n\n### Client API and Threads\n\nA `ClientSession` shouldn't be used by multiple threads to send requests.\n\nThe state functions, `Start()`, `Stop()` and `Cancel()`, are thread safe. E.g., you can call `Send()` in thread A and call `Stop()` in thread B. Please see [examples/heartbeat_client](examples/heartbeat_client.cc) for more details.\n\nExample:\n\n```cpp\nvoid ThreadedClient() {\n  std::vector\u003cstd::thread\u003e threads;\n\n  for (int i = 0; i \u003c 3; ++i) {\n    threads.emplace_back([]() {\n      webcc::ClientSession session;\n\n      try {\n        auto r = session.Send(\n            webcc::RequestBuilder{}.Get(\"http://httpbin.org/get\")());\n\n        std::cout \u003c\u003c r-\u003edata() \u003c\u003c std::endl;\n\n      } catch (const webcc::Error\u0026) {\n      }\n    });\n  }\n\n  for (auto\u0026 t : threads) {\n    t.join();\n  }\n}\n```\n\n## Server API\n\n### A Minimal Server\n\nThe following example is a minimal yet complete HTTP server.\n\nStart it, open a browser with `localhost:8080`, you will see `Hello, World!` as response.\n\n```cpp\n#include \"webcc/logger.h\"\n#include \"webcc/response_builder.h\"\n#include \"webcc/server.h\"\n\nclass HelloView : public webcc::View {\npublic:\n  webcc::ResponsePtr Handle(webcc::RequestPtr request) override {\n    if (request-\u003emethod() == \"GET\") {\n      return webcc::ResponseBuilder{}.OK().Body(\"Hello, World!\")();\n    }\n\n    return {};\n  }\n};\n\nint main() {\n  try {\n    webcc::Server server{ boost::asio::ip::tcp::v4(), 8080 };\n\n    server.Route(\"/\", std::make_shared\u003cHelloView\u003e());\n\n    server.Run();\n\n  } catch (const std::exception\u0026) {\n    return 1;\n  }\n\n  return 0;\n}\n```\n\n### URL Route\n\nThe `Route()` method routes different URLs to different `views`.\n\nYou can route different URLs to the same view:\n\n```cpp\nserver.Route(\"/\", std::make_shared\u003cHelloView\u003e());\nserver.Route(\"/hello\", std::make_shared\u003cHelloView\u003e());\n```\n\nOr even the same view object:\n\n```cpp\nauto view = std::make_shared\u003cHelloView\u003e();\nserver.Route(\"/\", view);\nserver.Route(\"/hello\", view);\n```\n\nBut normally a view only handles a specific URL (see the Book Server example). \n\nThe URL could be regular expressions. The Book Server example uses a regex URL to match against book IDs.\n\nFinally, it's always suggested to explicitly specify the HTTP methods allowed for a route:\n\n```cpp\nserver.Route(\"/\", std::make_shared\u003cHelloView\u003e(), { \"GET\" });\n```\n\n### Running A Server\n\nThe last thing about server is `Run()`:\n\n```cpp\nvoid Run(std::size_t workers = 1, std::size_t loops = 1);\n```\n\nWorkers are threads which will be waken to process the HTTP requests once they arrive. Theoretically, the more `workers` you have, the more concurrency you gain. In practice, you have to take the number of CPU cores into account and allocate a reasonable number for it.\n\nThe `loops` means the number of threads running the IO Context of Asio. Normally, one thread is good enough, but it could be more than that.\n\n### Response Builder\n\nThe server API provides a helper class `ResponseBuilder` for the views to chain the parameters and finally build a response object. This is exactly the same strategy as `RequestBuilder`.\n\n### REST Book Server\n\nSuppose you want to create a book server and provide the following operations with RESTful API:\n\n- Query books based on some criterias.\n- Add a new book.\n- Get the detailed information of a book.\n- Update the information of a book.\n- Delete a book.\n\nThe first two operations are implemented by `BookListView` deriving from `webcc::View`:\n\n```cpp\nclass BookListView : public webcc::View {\npublic:\n  webcc::ResponsePtr Handle(webcc::RequestPtr request) override {\n    if (request-\u003emethod() == \"GET\") {\n      return Get(request);\n    }\n    if (request-\u003emethod() == \"POST\") {\n      return Post(request);\n    }\n    return {};\n  }\n  \nprivate:\n  // Get a list of books based on query parameters.\n  webcc::ResponsePtr Get(webcc::RequestPtr request);\n\n  // Create a new book.\n  // The new book's data is attached as request data in JSON format.\n  webcc::ResponsePtr Post(webcc::RequestPtr request);\n};\n```\n\nOther operations are implemented by `BookDetailView`:\n\n```cpp\nclass BookDetailView : public webcc::View {\npublic:\n  webcc::ResponsePtr Handle(webcc::RequestPtr request) override {\n    if (request-\u003emethod() == \"GET\") {\n      return Get(request);\n    }\n    if (request-\u003emethod() == \"PUT\") {\n      return Put(request);\n    }\n    if (request-\u003emethod() == \"DELETE\") {\n      return Delete(request);\n    }\n    return {};\n  }\n  \nprotected:\n  // Get the detailed information of a book.\n  webcc::ResponsePtr Get(webcc::RequestPtr request);\n\n  // Update a book.\n  webcc::ResponsePtr Put(webcc::RequestPtr request);\n\n  // Delete a book.\n  webcc::ResponsePtr Delete(webcc::RequestPtr request);\n};\n```\n\nThe detailed implementation is out of the scope of this README, but here is an example:\n\n```cpp\nwebcc::ResponsePtr BookDetailView::Get(webcc::RequestPtr request) {\n  if (request-\u003eargs().size() != 1) {\n    // NotFound means the resource specified by the URL cannot be found.\n    // BadRequest could be another choice.\n    return webcc::ResponseBuilder{}.NotFound()();\n  }\n\n  const std::string\u0026 book_id = request-\u003eargs()[0];\n\n  // Get the book by ID from, e.g., the database.\n  // ...\n\n  if (\u003cNotFound\u003e) {\n    // There's no such book with the given ID. \n    return webcc::ResponseBuilder{}.NotFound()();\n  }\n\n  // Convert the book to JSON string and set as response data.\n  return webcc::ResponseBuilder{}.OK().Data(\u003cJsonStringOfTheBook\u003e).\n      Json().Utf8()();\n}\n```\n\nLast step, route URLs to the proper views and run the server:\n\n```cpp\nint main(int argc, char* argv[]) {\n  // ...\n\n  try {\n    webcc::Server server{ boost::asio::ip::tcp::v4(), 8080 };\n\n    server.Route(\"/books\",\n                 std::make_shared\u003cBookListView\u003e(),\n                 { \"GET\", \"POST\" });\n\n    server.Route(webcc::R(\"/books/(\\\\d+)\"),\n                 std::make_shared\u003cBookDetailView\u003e(),\n                 { \"GET\", \"PUT\", \"DELETE\" });\n\n    server.Run();\n\n  } catch (const std::exception\u0026 e) {\n    std::cerr \u003c\u003c e.what() \u003c\u003c std::endl;\n    return 1;\n  }\n\n  return 0;\n```\n\nPlease see [examples/book_server](examples/book_server) for more details.\n\n## IPv6 Support\n\n### IPv6 Server\n\nOnly need to change the protocol to `boost::asio::ip::tcp::v6()`:\n\n```cpp\nwebcc::Server server{ boost::asio::ip::tcp::v6(), 8080 };\n```\n\n### IPv6 Client\n\nOnly need to specify an IPv6 address:\n\n```cpp\nsession.Send(webcc::RequestBuilder{}.Get(\"http://[::1]:8080/books\")());\n```\n","funding_links":[],"categories":["Programming Languages","C++"],"sub_categories":["C++"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsprinfall%2Fwebcc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsprinfall%2Fwebcc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsprinfall%2Fwebcc/lists"}