{"id":20602334,"url":"https://github.com/jbaldwin/liblifthttp","last_synced_at":"2025-08-09T07:08:11.118Z","repository":{"id":41148649,"uuid":"96650917","full_name":"jbaldwin/liblifthttp","owner":"jbaldwin","description":"Safe and easy to use C++17 HTTP client library.","archived":false,"fork":false,"pushed_at":"2025-03-11T18:40:48.000Z","size":811,"stargazers_count":66,"open_issues_count":0,"forks_count":17,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-03-31T08:12:05.556Z","etag":null,"topics":["async","asynchronous","cpp","cpp17","cpp17-library","curl","event-driven","http-client","modern-cpp","uv"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jbaldwin.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-07-09T00:05:55.000Z","updated_at":"2025-03-15T11:43:08.000Z","dependencies_parsed_at":"2024-06-21T09:46:54.731Z","dependency_job_id":"2a7f3bfa-e84c-4c53-87a9-82ee3b8bdd6b","html_url":"https://github.com/jbaldwin/liblifthttp","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jbaldwin%2Fliblifthttp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jbaldwin%2Fliblifthttp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jbaldwin%2Fliblifthttp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jbaldwin%2Fliblifthttp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jbaldwin","download_url":"https://codeload.github.com/jbaldwin/liblifthttp/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247622986,"owners_count":20968575,"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":["async","asynchronous","cpp","cpp17","cpp17-library","curl","event-driven","http-client","modern-cpp","uv"],"created_at":"2024-11-16T09:13:32.525Z","updated_at":"2025-08-09T07:08:11.089Z","avatar_url":"https://github.com/jbaldwin.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# liblifthttp - Safe Easy to use C++17 HTTP client library\n\n[![CI](https://github.com/jbaldwin/liblifthttp/workflows/build/badge.svg)](https://github.com/jbaldwin/liblifthttp/workflows/build/badge.svg)\n[![Coverage Status](https://coveralls.io/repos/github/jbaldwin/liblifthttp/badge.svg?branch=master)](https://coveralls.io/github/jbaldwin/liblifthttp?branch=master)\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/2625260f88524abfa2c2974ad9328e45)](https://www.codacy.com/gh/jbaldwin/liblifthttp/dashboard?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=jbaldwin/liblifthttp\u0026amp;utm_campaign=Badge_Grade)\n[![language][badge.language]][language]\n[![license][badge.license]][license]\n\nYou're using curl? Do you even lift?\n\n**liblifthttp** is a C++17 HTTP client library that provides an easy to use API for both synchronous _and_ asynchronous requests.  It is built upon the super heavyweight champions libcurl and libuv libraries.\n\n**liblifthttp** is licensed under the Apache 2.0 license.\n\n## Overview\n* Easy to use Synchronous and Asynchronous HTTP Request APIs.\n* Safe C++17 client library API, modern memory move semantics.\n* Background IO thread(s) for sending and receiving Async HTTP requests.\n* Request pooling for re-using HTTP requests and sharing of connection information.\n\n## Known Bugs/Issues\n*   libcurl 7.81.0 is unsupported due to a known libcurl bug in the multi handle code.  Unfortunately ubuntu 22.04 comes with this version installed by default, you will need to manually install a different version of libcurl or build libcurl from source and link to it to avoid segfaults in asynchronous http requests via liblift.  See [here](https://github.com/jbaldwin/liblifthttp/issues/142) for more information\n\n## Usage\n\n### Examples\n\nSee all of the examples under the `examples/` directory.  Below are some simple examples\nto get your started on using liblifthttp with both the synchronous and asynchronous APIs.\n\n#### Synchronous and Asynchronous Requests\n```C++\n#include \u003ciostream\u003e\n#include \u003clift/lift.hpp\u003e\n\nint main()\n{\n    const std::string url{\"http://www.example.com\"};\n\n    // Every HTTP request in this example has a 10 second timeout to complete.\n    const std::chrono::seconds timeout{10};\n\n    // Synchronous requests can be created on the stack.\n    lift::request sync_request{url, timeout};\n\n    // Debug information about any request can be added by including a callback handler for debug\n    // information.  Just pass in a lambda to capture the verbose debug information.\n    sync_request.debug_info_handler(\n        [](const lift::request\u0026 /*unused*/, lift::debug_info_type type, std::string_view data)\n        { std::cout \u003c\u003c \"sync_request (\" \u003c\u003c lift::to_string(type) \u003c\u003c \"): \" \u003c\u003c data; });\n\n    // Set the http method for this synchronous request.\n    sync_request.method(lift::http::method::post);\n\n    // Add headers to this request, they are given as a name + value pair.  Note that if the same\n    // header name is specified more than once then it will appear that many times in the request.\n    sync_request.header(\"x-lift\", \"lift-custom-header-data\");\n\n    // Add some data to the request body, note that if the request http verb is not post or put then\n    // this data call will set the http verb to post since the request now includes a body.\n    sync_request.data(\"lift-hello-world-data\");\n\n    // This is the blocking synchronous HTTP call, this thread will wait until the http request\n    // completes or times out.\n    auto sync_response = sync_request.perform();\n    std::cout \u003c\u003c \"Lift status (sync): \" \u003c\u003c lift::to_string(sync_response.lift_status()) \u003c\u003c \"\\n\";\n    std::cout \u003c\u003c sync_response \u003c\u003c \"\\n\\n\"; // Will print the raw http response.\n\n    // Asynchronous requests must be created on the heap and they also need to be executed through\n    // a lift::client instance.  Creating a lift::client automatically spawns a background event\n    // loop thread to exceute the http requests it is given.  A lift::client also maintains a set\n    // of http connections and will actively re-use available http connections when possible.\n    lift::client client{};\n\n    // Create an asynchronous request that will be fulfilled by a std::future upon its completion.\n    auto async_future_request = std::make_unique\u003clift::request\u003e(url, timeout);\n\n    // Create an asynchronous request that will be fulfilled by a callback upon its completion.\n    // It is important to note that the callback will be executed on the lift::client's background\n    // event loop thread so it is wise to avoid any heavy CPU usage within this callback otherwise\n    // other outstanding requests will be blocked from completing.\n    auto async_callback_request = std::make_unique\u003clift::request\u003e(url, timeout);\n\n    // Starting the asynchronous requests requires the request ownership to be moved to the\n    // lift::client while it is being processed.  Regardless of the on complete method, future or\n    // callback, the original request object and its response will have their ownership moved back\n    // to you upon completion.  If you hold on to any raw pointers or references to the requests\n    // while they are being processed be sure not to use them until the requests complete.  Modifying\n    // a request's state during execution is prohibited.\n\n    // Start the request that will be completed by future.\n    auto future = client.start_request(std::move(async_future_request));\n\n    // Start the request that will be completed by callback.\n    client.start_request(\n        std::move(async_callback_request),\n        [](lift::request_ptr async_callback_request_returned, lift::response async_callback_response)\n        {\n            // This on complete callback will run on the lift::client background event loop thread.\n            std::cout \u003c\u003c \"Lift status (async callback): \";\n            std::cout \u003c\u003c lift::to_string(async_callback_response.lift_status()) \u003c\u003c \"\\n\";\n            std::cout \u003c\u003c async_callback_response \u003c\u003c \"\\n\\n\";\n        });\n\n    // Block until the async future request completes, this returns the original request and the response.\n    // Note that the other callback request could complete and print to stdout before or after the future\n    // request completes since its lambda callback will be invoked on the lift::client's thread.\n    auto [async_future_request_returned, async_future_response] = future.get();\n    std::cout \u003c\u003c \"Lift status (async future): \";\n    std::cout \u003c\u003c lift::to_string(async_future_response.lift_status()) \u003c\u003c \"\\n\";\n    std::cout \u003c\u003c async_future_response \u003c\u003c \"\\n\\n\";\n\n    // The lift::client destructor will block until all outstanding requests complete or timeout.\n\n    return 0;\n}\n```\n\n### Requirements\n```bash\nC++17 compilers tested\n    g++ [9, 10, 11, 12, 13, 14]\n    clang [9, 14, 15, 16, 17, 18, 19]\nCMake\nmake or ninja\npthreads\nlibcurl-devel \u003e= 7.59\n    *UNSUPPORTED* 7.81.0 has a known libcurl multi bug that was fixed in 7.82.0.\n    ubuntu-22.04 apt pacakges have this broken version, see ci-ubuntu.yml on how to\n    manually build and link to a working version of curl.\nlibuv-devel\nzlib-devel\nopenssl-devel (or equivalent curl support ssl library)\nstdc++fs\n\nTested on:\n    ubuntu:20.04\n    ubuntu:22.04 (with custom libcurl built)\n    ubuntu:24.04\n    fedora:41\n```\n\n### Instructions\n\n#### Building\n```bash\n# This will produce a static library to link against your project.\nmkdir Release \u0026\u0026 cd Release\ncmake -DCMAKE_BUILD_TYPE=Release ..\ncmake --build .\n```\n\n#### CMake Projects\n\nCMake options:\n\n| Name                     | Default                       | Description                            |\n|:-------------------------|:------------------------------|:---------------------------------------|\n| LIFT_BUILD_EXAMPLES      | ON                            | Should the examples be built?          |\n| LIFT_BUILD_TESTS         | ON                            | Should the tests be built?             |\n| LIFT_CODE_COVERAGE       | OFF                           | Should code coverage be enabled?       |\n| LIFT_USER_LINK_LIBRARIES | curl z uv pthread dl stdc++fs | Override lift's target link libraries. |\n\nNote on `LIFT_USER_LINK_LIBRARIES`, if override the value then all of the default link libraries/targets must be\naccounted for in the override.  E.g. if you are building with a custom curl target but defaults for everything else\nthen `-DLIFT_USER_LINK_LIBRARIES=\"custom_curl_target;z;uv;pthread;dl;stdc++fs\"` would be the correct setting.\n\n##### add_subdirectory()\nTo use within your cmake project you can clone the project or use git submodules and then `add_subdirectory` in the parent project's `CMakeList.txt`,\nassuming the lift code is in a `liblifthttp/` subdirectory of the parent project:\n    add_subdirectory(liblifthttp)\nTo link to the `\u003cproject_name\u003e` then use the following:\n    add_executable(\u003cproject_name\u003e main.cpp)\n    target_link_libraries(\u003cproject_name\u003e PUBLIC lifthttp)\nInclude lift in the project's code by simply including `#include \u003clift/lift.hpp\u003e` as needed.\n\n##### FetchContent\nCMake can also include the project directly via a `FetchContent` declaration.  In your project's `CMakeLists.txt`\ninclude the following code to download the git repository and make it available to link to.\n\n```cmake\ncmake_minimum_required(VERSION 3.11)\n\n# ... cmake project stuff ...\n\ninclude(FetchContent)\nFetchContent_Declare(\n    lifthttp\n    GIT_REPOSITORY https://github.com/jbaldwin/liblifthttp.git\n    GIT_TAG        \u003cTAG_OR_GIT_HASH\u003e\n)\nFetchContent_MakeAvailable(lifthttp)\n\n# ... cmake project more stuff ...\n\ntarget_link_libraries(${PROJECT_NAME} PUBLIC lifthttp)\n```\n\n#### Running Tests\nThe tests are automatically run by GitHub Actions on all Pull Requests.  They can also be ran locally with a default\nlocalhost instance of `nginx` and `haproxy`.  To do so the CMake option `LIFT_LOCALHOST_TESTS=ON` must be set otherwise the tests\nwill use the hostname `nginx` setup in the CI settings.  After building and starting `nginx` and `haproxy` tests can be run by issuing:\n\n```bash\n# Invoke via cmake:\nctest -v\n\n# Or invoke directly to see error messages if tests are failing:\n./test/liblifthttp_tests\n```\n\nNote:\n* `nginx` should be default install/configuration running on port `80`.\n* `haproxy` should be running on port `*3128` with a backend pointing at the `nginx` instance. See `docker/build/haproxy/haproxy.cfg` to update the local configuration.\n\n\n### Benchmarks\nUsing the example benchmark code and a local `nginx` instance serving its default welcome page.  All benchmarks use `keep-alive` connections.  The benchmark is compared against `wrk` as that is basically optimal performance since\n`wrk` does zero parsing of the response whereas `lift` does.\n\nHere is the CPU the benchmarks were run on:\n\n```bash\ncat /proc/cpuinfo\n...\nIntel(R) Core(TM) i9-9980HK CPU @ 2.40GHz\n```\n\nHere is how the benchmark application is called:\n\n```bash\n$ ./examples/lift_benchmark --help\nUsage: ./examples/lift_benchmark\u003coptions\u003e \u003curl\u003e\n    -c --connections  HTTP Connections to use per thread.\n    -t --threads      Number of threads to use.\n                      evenly between each worker thread.\n    -d --duration     Duration of the test in seconds\n    -h --help         Print this help usage.\n```\n\nUsing `nginx` as the webserver with the default `fedora` configuration.\n\n| Connections | Threads | wrk Req/Sec | lift Req/Sec |\n|------------:|--------:|------------:|-------------:|\n| 1           | 1       | 31,138      | 20,785       |\n| 100         | 1       | 148,121     | 44,393       |\n| 100         | 2       | 220,670     | 81,335       |\n| 100         | 3       | 241,839     | 104,747      |\n| 100         | 4       | 275,633     | 123,481      |\n| 100         | 8       | 249,845     | 143,911      |\n\n### Support\n\nFile bug reports, feature requests and questions using [GitHub Issues](https://github.com/jbaldwin/liblifthttp/issues)\n\nCopyright © 2017-2024, Josh Baldwin\n\n[badge.language]: https://img.shields.io/badge/language-C%2B%2B17-yellow.svg\n[badge.license]: https://img.shields.io/badge/license-Apache--2.0-blue\n\n[language]: https://en.wikipedia.org/wiki/C%2B%2B17\n[license]: https://en.wikipedia.org/wiki/Apache_License\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjbaldwin%2Fliblifthttp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjbaldwin%2Fliblifthttp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjbaldwin%2Fliblifthttp/lists"}