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>
int main(void)
{
  using namespace httplib;
  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 directory
auto 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([](const auto& req, const auto& res) {
  your_logger(req, res);
});svr.set_error_handler([](const auto& 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", [&](const auto& req, auto& res) {
  auto size = req.files.size();
  auto ret = req.has_file("name1");
  const auto& 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);
          return true;
        },
        [&](const char *data, size_t data_length) {
          files.back().content.append(data, data_length);
          return true;
        });
    } else {
      std::string body;
      content_reader([&](const char *data, size_t data_length) {
        body.append(data, data_length);
        return true;
      });
      res.set_content(body, "text/plain");
    }
  });const size_t DATA_CHUNK_SIZE = 4;
svr.Get("/stream", [&](const Request &req, Response &res) {
  auto data = new std::string("abcdefg");
  res.set_content_provider(
    data->size(), // Content length
    "text/plain", // Content type
    [data](size_t offset, size_t length, DataSink &sink) {
      const auto &d = *data;
      sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
      return true; // 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
      }
      return true; // 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 data
      return true; // 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) {
  return 417;
});// 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 8, or std::thread::hardware_concurrency(). You can change it with CPPHTTPLIB_THREAD_POOL_COUNT.
If you want to set the thread count at runtime, there is no convenient way... But here is how.
svr.new_task_queue = [] { return new ThreadPool(12); };You can supply your own thread pool implementation according to your need.
class YourThreadPoolTaskQueue : public TaskQueue {
public:
  YourThreadPoolTaskQueue(size_t n) {
    pool_.start_with_thread_count(n);
  }
  virtual void enqueue(std::function<void()> fn) override {
    pool_.enqueue(fn);
  }
  virtual void shutdown() override {
    pool_.shutdown_gracefully();
  }
private:
  YourThreadPool pool_;
};
svr.new_task_queue = [] {
  return new YourThreadPoolTaskQueue(12);
};#include <httplib.h>
#include <iostream>
int main(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",
  [&](const char *data, size_t data_length) {
    body.append(data, data_length);
    return true;
  });std::string body;
auto res = cli.Get(
  "/stream", Headers(),
  [&](const Response &response) {
    EXPECT_EQ(200, response.status);
    return true; // return 'false' if you want to cancel the request.
  },
  [&](const char *data, size_t data_length) {
    body.append(data, data_length);
    return true; // 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);
    return true; // return 'false' if you want to cancel the request.
  },
  "text/plain");httplib::Client client(url, port);
// prints: 0 / 000 bytes => 50% complete
auto res = cli.Get("/", [](uint64_t len, uint64_t total) {
  printf("%lld / %lld bytes => %d%% complete\n",
    len, total,
    (int)(len*100/total));
  return true; // 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.
#define CPPHTTPLIB_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>#define WIN32_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!