A C++11 single-file header-only cross platform HTTP/HTTPS library.
It's extremely easy to setup. Just include httplib.h file in your code!
NOTE: This is a 'blocking' HTTP library. If you are looking for a 'non-blocking' library, this is not the one that you want.
httplib::Server svr; svr.Get("/hi", [](const httplib::Request &, httplib::Response &res){res.set_content("Hello World!", "text/plain")}); svr.listen("0.0.0.0", 8080);httplib::Client cli("http://cpp-httplib-server.yhirose.repl.co"); auto res = cli.Get("/hi"); res->status; // 200 res->body; // "Hello World!"- Run server at https://repl.it/@yhirose/cpp-httplib-server
- Run client at https://repl.it/@yhirose/cpp-httplib-client
#include<httplib.h>intmain(void){usingnamespacehttplib; Server svr; svr.Get("/hi", [](const Request& req, Response& res){res.set_content("Hello World!", "text/plain")}); svr.Get(R"(/numbers/(\d+))", [&](const Request& req, Response& res){auto numbers = req.matches[1]; res.set_content(numbers, "text/plain")}); svr.Get("/body-header-param", [](const Request& req, Response& res){if (req.has_header("Content-Length")){auto val = req.get_header_value("Content-Length")} if (req.has_param("key")){auto val = req.get_param_value("key")} res.set_content(req.body, "text/plain")}); svr.Get("/stop", [&](const Request& req, Response& res){svr.stop()}); svr.listen("localhost", 1234)}Post, Put, Delete and Options methods are also supported.
int port = svr.bind_to_any_port("0.0.0.0"); svr.listen_after_bind();// Mount / to ./www directoryauto ret = svr.set_mount_point("/", "./www"); if (!ret){// The specified base directory doesn't exist... } // Mount /public to ./www directory ret = svr.set_mount_point("/public", "./www"); // Mount /public to ./www1 and ./www2 directories ret = svr.set_mount_point("/public", "./www1"); // 1st order to search ret = svr.set_mount_point("/public", "./www2"); // 2nd order to search// Remove mount / ret = svr.remove_mount_point("/"); // Remove mount /public ret = svr.remove_mount_point("/public");// User defined file extension and MIME type mappings svr.set_file_extension_and_mimetype_mapping("cc", "text/x-c"); svr.set_file_extension_and_mimetype_mapping("cpp", "text/x-c"); svr.set_file_extension_and_mimetype_mapping("hh", "text/x-h");The followings are built-in mappings:
| Extension | MIME Type |
|---|---|
| txt | text/plain |
| html, htm | text/html |
| css | text/css |
| jpeg, jpg | image/jpg |
| png | image/png |
| gif | image/gif |
| svg | image/svg+xml |
| ico | image/x-icon |
| json | application/json |
| application/pdf | |
| js | application/javascript |
| wasm | application/wasm |
| xml | application/xml |
| xhtml | application/xhtml+xml |
NOTE: These the static file server methods are not thread safe.
svr.set_logger([](constauto& req, constauto& res){your_logger(req, res)});svr.set_error_handler([](constauto& req, auto& res){auto fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>"; char buf[BUFSIZ]; snprintf(buf, sizeof(buf), fmt, res.status); res.set_content(buf, "text/html")});svr.Post("/multipart", [&](constauto& req, auto& res){auto size = req.files.size(); auto ret = req.has_file("name1"); constauto& file = req.get_file_value("name1"); // file.filename;// file.content_type;// file.content; });svr.Post("/content_receiver", [&](const Request &req, Response &res, const ContentReader &content_reader){if (req.is_multipart_form_data()){MultipartFormDataItems files; content_reader( [&](const MultipartFormData &file){files.push_back(file); returntrue}, [&](constchar *data, size_t data_length){files.back().content.append(data, data_length); returntrue})} else{std::string body; content_reader([&](constchar *data, size_t data_length){body.append(data, data_length); returntrue}); res.set_content(body, "text/plain")} });constsize_t DATA_CHUNK_SIZE = 4; svr.Get("/stream", [&](const Request &req, Response &res){auto data = newstd::string("abcdefg"); res.set_content_provider( data->size(), // Content length"text/plain", // Content type [data](size_t offset, size_t length, DataSink &sink){constauto &d = *data; sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE)); returntrue; // return 'false' if you want to cancel the process. }, [data]{delete data})});Without content length:
svr.Get("/stream", [&](const Request &req, Response &res){res.set_content_provider( "text/plain", // Content type [&](size_t offset, size_t length, DataSink &sink){if (/* there is still data */){std::vector<char> data; // prepare data... sink.write(data.data(), data.size())} else{sink.done(); // No more data } returntrue; // return 'false' if you want to cancel the process. })});svr.Get("/chunked", [&](const Request& req, Response& res){res.set_chunked_content_provider( [](size_t offset, DataSink &sink){sink.write("123", 3); sink.write("345", 3); sink.write("789", 3); sink.done(); // No more datareturntrue; // return 'false' if you want to cancel the process. } )});As default, the server sends 100 Continue response for Expect: 100-continue header.
// Send a '417 Expectation Failed' response. svr.set_expect_100_continue_handler([](const Request &req, Response &res){return417});// Send a final status without reading the message body. svr.set_expect_100_continue_handler([](const Request &req, Response &res){return res.status = 401});svr.set_keep_alive_max_count(2); // Default is 5svr.set_read_timeout(5, 0); // 5 seconds svr.set_write_timeout(5, 0); // 5 seconds svr.set_idle_interval(0, 100000); // 100 millisecondssvr.set_payload_max_length(1024 * 1024 * 512); // 512MBPlease see Server example and Client example.
ThreadPool is used as a default task queue, and the default thread count is set to value from std::thread::hardware_concurrency().
You can change the thread count by setting CPPHTTPLIB_THREAD_POOL_COUNT.
classYourThreadPoolTaskQueue : publicTaskQueue{public:YourThreadPoolTaskQueue(size_t n){pool_.start_with_thread_count(n)} virtualvoidenqueue(std::function<void()> fn) override{pool_.enqueue(fn)} virtualvoidshutdown() override{pool_.shutdown_gracefully()} private: YourThreadPool pool_}; svr.new_task_queue = []{returnnewYourThreadPoolTaskQueue(12)};#include<httplib.h> #include<iostream>intmain(void){httplib::Client cli("localhost", 1234); if (auto res = cli.Get("/hi")){if (res->status == 200){std::cout << res->body << std::endl} } else{auto err = res.error(); ... } }NOTE: Constructor with scheme-host-port string is now supported!
httplib::Client cli("localhost"); httplib::Client cli("localhost:8080"); httplib::Client cli("http://localhost"); httplib::Client cli("http://localhost:8080"); httplib::Client cli("https://localhost");httplib::Headers headers ={{"Accept-Encoding", "gzip, deflate" } }; auto res = cli.Get("/hi", headers);or
cli.set_default_headers({{"Accept-Encoding", "gzip, deflate" } }); auto res = cli.Get("/hi");res = cli.Post("/post", "text", "text/plain"); res = cli.Post("/person", "name=john1¬e=coder", "application/x-www-form-urlencoded");httplib::Params params; params.emplace("name", "john"); params.emplace("note", "coder"); auto res = cli.Post("/post", params);or
httplib::Params params{{"name", "john" },{"note", "coder" } }; auto res = cli.Post("/post", params);httplib::MultipartFormDataItems items ={{"text1", "text default", "", "" },{"text2", "aωb", "", "" },{"file1", "h\ne\n\nl\nl\no\n", "hello.txt", "text/plain" },{"file2", "{\n\"world\", true\n}\n", "world.json", "application/json" },{"file3", "", "", "application/octet-stream" }, }; auto res = cli.Post("/multipart", items);res = cli.Put("/resource/foo", "text", "text/plain");res = cli.Delete("/resource/foo");res = cli.Options("*"); res = cli.Options("/resource/foo");cli.set_connection_timeout(0, 300000); // 300 milliseconds cli.set_read_timeout(5, 0); // 5 seconds cli.set_write_timeout(5, 0); // 5 secondsstd::string body; auto res = cli.Get("/large-data", [&](constchar *data, size_t data_length){body.append(data, data_length); returntrue});std::string body; auto res = cli.Get( "/stream", Headers(), [&](const Response &response){EXPECT_EQ(200, response.status); returntrue; // return 'false' if you want to cancel the request. }, [&](constchar *data, size_t data_length){body.append(data, data_length); returntrue; // return 'false' if you want to cancel the request. });std::string body = ...; auto res = cli_.Post( "/stream", body.size(), [](size_t offset, size_t length, DataSink &sink){sink.write(body.data() + offset, length); returntrue; // return 'false' if you want to cancel the request. }, "text/plain");httplib::Client client(url, port); // prints: 0 / 000 bytes => 50% completeauto res = cli.Get("/", [](uint64_t len, uint64_t total){printf("%lld / %lld bytes => %d%% complete\n", len, total, (int)(len*100/total)); returntrue; // return 'false' if you want to cancel the request. } );// Basic Authentication cli.set_basic_auth("user", "pass"); // Digest Authentication cli.set_digest_auth("user", "pass"); // Bearer Token Authentication cli.set_bearer_token_auth("token");NOTE: OpenSSL is required for Digest Authentication.
cli.set_proxy("host", port); // Basic Authentication cli.set_proxy_basic_auth("user", "pass"); // Digest Authentication cli.set_proxy_digest_auth("user", "pass"); // Bearer Token Authentication cli.set_proxy_bearer_token_auth("pass");NOTE: OpenSSL is required for Digest Authentication.
httplib::Client cli("httpbin.org"); auto res = cli.Get("/range/32",{httplib::make_range_header({{1, 10}}) // 'Range: bytes=1-10' }); // res->status should be 206.// res->body should be "bcdefghijk".httplib::make_range_header({{1, 10},{20, -1}}) // 'Range: bytes=1-10, 20-' httplib::make_range_header({{100, 199},{500, 599}}) // 'Range: bytes=100-199, 500-599' httplib::make_range_header({{0, 0},{-1, 1}}) // 'Range: bytes=0-0, -1'httplib::Client cli("localhost", 1234); cli.Get("/hello"); // with "Connection: close" cli.set_keep_alive(true); cli.Get("/world"); cli.set_keep_alive(false); cli.Get("/last-request"); // with "Connection: close"httplib::Client cli("yahoo.com"); auto res = cli.Get("/"); res->status; // 301 cli.set_follow_location(true); res = cli.Get("/"); res->status; // 200NOTE: This feature is not available on Windows, yet.
cli.set_interface("eth0"); // Interface name, IP address or host nameSSL support is available with CPPHTTPLIB_OPENSSL_SUPPORT. libssl and libcrypto should be linked.
NOTE: cpp-httplib currently supports only version 1.1.1.
#defineCPPHTTPLIB_OPENSSL_SUPPORT httplib::SSLServer svr("./cert.pem", "./key.pem"); httplib::SSLClient cli("localhost", 1234); // or `httplib::Client cli("https://localhost:1234");` cli.set_ca_cert_path("./ca-bundle.crt"); cli.enable_server_certificate_verification(true);The server can applie compression to the following MIME type contents:
- all text types except text/event-stream
- image/svg+xml
- application/javascript
- application/json
- application/xml
- application/xhtml+xml
'gzip' compression is available with CPPHTTPLIB_ZLIB_SUPPORT. libz should be linked.
Brotli compression is available with CPPHTTPLIB_BROTLI_SUPPORT. Necessary libraries should be linked. Please see https://github.com/google/brotli for more detail.
cli.set_compress(true); res = cli.Post("/resource/foo", "...", "text/plain");cli.set_decompress(false); res = cli.Get("/resource/foo",{{"Accept-Encoding", "gzip, deflate, br"}}); res->body; // Compressed data> python3 split.py > ls out httplib.h httplib.ccg++ 4.8 and below cannot build this library since <regex> in the versions are broken.
Include httplib.h before Windows.h or include Windows.h by defining WIN32_LEAN_AND_MEAN beforehand.
#include<httplib.h> #include<Windows.h>#defineWIN32_LEAN_AND_MEAN #include<Windows.h> #include<httplib.h>Note: Cygwin on Windows is not supported.
MIT license (© 2020 Yuji Hirose)
These folks made great contributions to polish this library to totally another level from a simple toy!
