Skip to content

A C++ header-only HTTP/HTTPS server and client library

License

Notifications You must be signed in to change notification settings

NamiStudio/cpp-httplib

Repository files navigation

cpp-httplib

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.

Simple examples

Server

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);

Client

httplib::Client cli("http://cpp-httplib-server.yhirose.repl.co"); auto res = cli.Get("/hi"); res->status; // 200 res->body; // "Hello World!"

Try out the examples on Repl.it!

  1. Run server at https://repl.it/@yhirose/cpp-httplib-server
  2. Run client at https://repl.it/@yhirose/cpp-httplib-client

Server

#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.

Bind a socket to multiple interfaces and any available port

int port = svr.bind_to_any_port("0.0.0.0"); svr.listen_after_bind();

Static File Server

// 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:

ExtensionMIME Type
txttext/plain
html, htmtext/html
csstext/css
jpeg, jpgimage/jpg
pngimage/png
gifimage/gif
svgimage/svg+xml
icoimage/x-icon
jsonapplication/json
pdfapplication/pdf
jsapplication/javascript
wasmapplication/wasm
xmlapplication/xml
xhtmlapplication/xhtml+xml

NOTE: These the static file server methods are not thread safe.

Logging

svr.set_logger([](constauto& req, constauto& res){your_logger(req, res)});

Error handler

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")});

'multipart/form-data' POST data

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; });

Receive content with Content receiver

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")} });

Send content with Content provider

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. })});

Chunked transfer encoding

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. } )});

'Expect: 100-continue' handler

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});

Keep-Alive connection

svr.set_keep_alive_max_count(2); // Default is 5

Timeout

svr.set_read_timeout(5, 0); // 5 seconds svr.set_write_timeout(5, 0); // 5 seconds svr.set_idle_interval(0, 100000); // 100 milliseconds

Set maximum payload length for reading request body

svr.set_payload_max_length(1024 * 1024 * 512); // 512MB

Server-Sent Events

Please see Server example and Client example.

Default thread pool support

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.

Override the default thread pool with yours

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)};

Client

#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");

GET with HTTP headers

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");

POST

res = cli.Post("/post", "text", "text/plain"); res = cli.Post("/person", "name=john1&note=coder", "application/x-www-form-urlencoded");

POST with parameters

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);

POST with Multipart Form Data

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);

PUT

res = cli.Put("/resource/foo", "text", "text/plain");

DELETE

res = cli.Delete("/resource/foo");

OPTIONS

res = cli.Options("*"); res = cli.Options("/resource/foo");

Timeout

cli.set_connection_timeout(0, 300000); // 300 milliseconds cli.set_read_timeout(5, 0); // 5 seconds cli.set_write_timeout(5, 0); // 5 seconds

Receive content with Content receiver

std::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. });

Send content with Content provider

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");

With Progress Callback

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. } );

progress

Authentication

// 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.

Proxy server support

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.

Range

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'

Keep-Alive connection

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"

Redirect

httplib::Client cli("yahoo.com"); auto res = cli.Get("/"); res->status; // 301 cli.set_follow_location(true); res = cli.Get("/"); res->status; // 200

Use a specitic network interface

NOTE: This feature is not available on Windows, yet.

cli.set_interface("eth0"); // Interface name, IP address or host name

OpenSSL Support

SSL 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);

Compression

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

Zlib Support

'gzip' compression is available with CPPHTTPLIB_ZLIB_SUPPORT. libz should be linked.

Brotli Support

Brotli compression is available with CPPHTTPLIB_BROTLI_SUPPORT. Necessary libraries should be linked. Please see https://github.com/google/brotli for more detail.

Compress request body on client

cli.set_compress(true); res = cli.Post("/resource/foo", "...", "text/plain");

Compress response body on client

cli.set_decompress(false); res = cli.Get("/resource/foo",{{"Accept-Encoding", "gzip, deflate, br"}}); res->body; // Compressed data

Split httplib.h into .h and .cc

> python3 split.py > ls out httplib.h httplib.cc

NOTE

g++

g++ 4.8 and below cannot build this library since <regex> in the versions are broken.

Windows

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.

License

MIT license (© 2020 Yuji Hirose)

Special Thanks To

These folks made great contributions to polish this library to totally another level from a simple toy!

About

A C++ header-only HTTP/HTTPS server and client library

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • C++98.2%
  • CMake1.5%
  • Makefile0.2%
  • Python0.1%
  • HTML0.0%
  • Dockerfile0.0%