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

Overview

cpp-httplib

A C++11 single-file header-only cross platform HTTP/HTTPS library.

It's extremely easy to setup. Just include the httplib.h file in your code!

NOTE: This is a multi-threaded 'blocking' HTTP library. If you are looking for a 'non-blocking' library, this is not the one that you want.

Simple examples

Server

#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "path/to/httplib.h"

// HTTP
httplib::Server svr;

// HTTPS
httplib::SSLServer 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

#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "path/to/httplib.h"

// HTTP
httplib::Client cli("http://cpp-httplib-server.yhirose.repl.co");

// HTTPS
httplib::Client cli("https://cpp-httplib-server.yhirose.repl.co");

auto res = cli.Get("/hi");
res->status;
res->body;

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>

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.

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 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 Extension MIME Type
css text/css mpga audio/mpeg
csv text/csv weba audio/webm
txt text/plain wav audio/wave
vtt text/vtt otf font/otf
html, htm text/html ttf font/ttf
apng image/apng woff font/woff
avif image/avif woff2 font/woff2
bmp image/bmp 7z application/x-7z-compressed
gif image/gif atom application/atom+xml
png image/png pdf application/pdf
svg image/svg+xml mjs, js application/javascript
webp image/webp json application/json
ico image/x-icon rss application/rss+xml
tif image/tiff tar application/x-tar
tiff image/tiff xhtml, xht application/xhtml+xml
jpeg, jpg image/jpeg xslt application/xslt+xml
mp4 video/mp4 xml application/xml
mpeg video/mpeg gz application/gzip
webm video/webm zip application/zip
mp3 audio/mp3 wasm application/wasm

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

Logging

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

Error handler

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

Exception handler

The exception handler gets called if a user routing handler throws an error.

svr.set_exception_handler([](const auto& req, auto& res, std::exception &e) {
  res.status = 500;
  auto fmt = "<h1>Error 500</h1><p>%s</p>";
  char buf[BUFSIZ];
  snprintf(buf, sizeof(buf), fmt, e.what());
  res.set_content(buf, "text/html");
});

Pre routing handler

svr.set_pre_routing_handler([](const auto& req, auto& res) -> bool {
  if (req.path == "/hello") {
    res.set_content("world", "text/html");
    return Server::HandlerResponse::Handled;
  }
  return Server::HandlerResponse::Unhandled;
});

Post routing handler

svr.set_post_routing_handler([](const auto& req, auto& res) {
  res.set_header("ADDITIONAL_HEADER", "value");
});

'multipart/form-data' POST data

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

Receive content with a 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);
          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");
    }
  });

Send content with the content provider

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

Chunked transfer encoding

svr.Get("/chunked", [&](const Request& req, Response& res) {
  res.set_chunked_content_provider(
    "text/plain",
    [](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.
    }
  );
});

'Expect: 100-continue' handler

By default, the server sends a 100 Continue response for an 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;
});

Keep-Alive connection

svr.set_keep_alive_max_count(2); // Default is 5
svr.set_keep_alive_timeout(10);  // 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 a 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 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); };

Override the default thread pool with yours

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

Client

#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::SSLClient cli("localhost");

Error code

Here is the list of errors from Result::error().

enum Error {
  Success = 0,
  Unknown,
  Connection,
  BindIPAddress,
  Read,
  Write,
  ExceedRedirectCount,
  Canceled,
  SSLConnection,
  SSLLoadingCerts,
  SSLServerVerification,
  UnsupportedMultipartBoundaryChars
};

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 a content receiver

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

Send content with a 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);
    return true; // return 'false' if you want to cancel the request.
  },
  "text/plain");

Chunked transfer encoding

auto res = cli.Post(
  "/stream",
  [](size_t offset, DataSink &sink) {
    sink.os << "chunked data 1";
    sink.os << "chunked data 2";
    sink.os << "chunked data 3";
    sink.done();
    return true; // return 'false' if you want to cancel the request.
  },
  "text/plain");

With Progress Callback

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

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 specific network interface

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

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

Compression

The server can apply 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

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

#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "path/to/httplib.h"

// Server
httplib::SSLServer svr("./cert.pem", "./key.pem");

// Client
httplib::Client cli("https://localhost:1234");

// Use your CA bundle
cli.set_ca_cert_path("./ca-bundle.crt");

// Disable cert verification
cli.enable_server_certificate_verification(false);

Note: When using SSL, it seems impossible to avoid SIGPIPE in all cases, since on some operating systems, SIGPIPE can only be suppressed on a per-message basis, but there is no way to make the OpenSSL library do so for its internal communications. If your program needs to avoid being terminated on SIGPIPE, the only fully general way might be to set up a signal handler for SIGPIPE to handle or ignore it yourself.

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>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <httplib.h>

Note: Windows 8 or lower and Cygwin on Windows are 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!

Comments
  • SSL_read timeout doesn't work on unstable network connection

    SSL_read timeout doesn't work on unstable network connection

    I'm new to native socket. now I try to use the http client to recv() from a low-bandwidth-server. Sometimes the recv() blocks forever, never returns, at that moment I attach debugger and see it's blocking at line:

    inline int SocketStream::read(char* ptr, size_t size)
    {
        return recv(sock_, ptr, static_cast<int>(size), 0);  <------blocking here
    }
    

    I see there is timeout implemented for connect(), but not for recv(). I tried to set the timeout with:

    int millisec = 1000; // 1 second
    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&millisec, sizeof(int));
    

    But it doesn't take effect, I set 1 second but it still can blocking for 17 seconds, sometimes forever.

    I asked this question on stackoverflow, seems it's about WSA_FLAG_OVERLAPPED, I see the SO_OPENTYPE is set:

    #ifdef _WIN32
    #define SO_SYNCHRONOUS_NONALERT 0x20
    #define SO_OPENTYPE 0x7008
    
        int opt = SO_SYNCHRONOUS_NONALERT;
        setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char*)&opt, sizeof(opt));
    #endif
    

    From msdn :

    SO_OPENTYPE Once set, affects whether subsequent sockets that are created will be non-overlapped. The possible values for this option are SO_SYNCHRONOUS_ALERT and SO_SYNCHRONOUS_NONALERT. This option should not be used. Instead use the WSASocket function and leave the WSA_FLAG_OVERLAPPED bit in the dwFlagsparameter turned off.

    So, once the SO_OPENTYPE is set, the WSA_FLAG_OVERLAPPED doesn't work, then the SO_RCVTIMEO doesn't work, is this correct?

    I tried to delete the lines about SO_OPENTYPE, but all http requests fail.

    Any way to make the timeout work for recv()

    Thanks.

    bug 
    opened by aj3423 37
  • Timeouts don't apply to SSLClient*

    Timeouts don't apply to SSLClient*

    Hi, Loving this library so far, however I seem to have found a problem in which the timeouts do not apply to at least the SSLClient.

    #define CPPHTTPLIB_OPENSSL_SUPPORT
    #define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 2
    #define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
    #define CPPHTTPLIB_READ_TIMEOUT_SECOND 2
    #define CPPHTTPLIB_READ_TIMEOUT_USECOND 0
    #include <httplib.h>
    

    For the following configuration, the ssl implementation waits for about 30s or so....

    httplib::SSLClient cli(C_ServerRemote, 443);
    cli.enable_server_certificate_verification(false);
    cli.set_follow_location(true);
    std::string sBuffer;
    httplib::Headers headers;
    headers.emplace("Accept-Encoding", "gzip, deflate");
    headers.emplace("Accept", "text/plain");
    auto res = cli.Get("/cli/latest", headers);
    

    The following returns in 2 seconds as expected.

    httplib::Client cli(C_ServerRemote, 80);
    cli.set_follow_location(true);
    std::tring sBuffer;
    httplib::Headers headers;
    headers.emplace("Accept-Encoding", "gzip, deflate");
    headers.emplace("Accept", "text/plain");
    auto res = cli.Get("/cli/latest", headers);
    

    Wonder if you know a fix for this?

    Sincerely, FrozenSource

    need more information to verify 
    opened by FrozenSource 36
  • Httpclient calls with OpenSSL 1.0.1e crash

    Httpclient calls with OpenSSL 1.0.1e crash

    Crash core:

    #0  0x00007fa48b4d3277 in raise () from /lib64/libc.so.6
    #1  0x00007fa48b4d4968 in abort () from /lib64/libc.so.6
    #2  0x00007fa48b515d37 in __libc_message () from /lib64/libc.so.6
    #3  0x00007fa48b51c5e4 in malloc_printerr () from /lib64/libc.so.6
    #4  0x00007fa48b521c39 in _int_realloc () from /lib64/libc.so.6
    #5  0x00007fa48b522eb2 in realloc () from /lib64/libc.so.6
    #6  0x00007fa48d2b5649 in CRYPTO_realloc () from ../lib/libcrypto.so.10
    #7  0x00007fa48d330b11 in lh_insert () from ../lib/libcrypto.so.10
    #8  0x00007fa48d3331e4 in int_thread_set_item () from ../lib/libcrypto.so.10
    #9  0x00007fa48d333d6e in ERR_get_state () from ../lib/libcrypto.so.10
    #10 0x00007fa48d333ef5 in ERR_clear_error () from ../lib/libcrypto.so.10
    #11 0x00007fa48d6665b5 in ssl23_connect () from ../lib/libssl.so.10
    #12 0x000000000049add6 in httplib::SSLClient::read_and_close_socket(int, httplib::Request&, httplib::Response&)::{lambda(ssl_st*)#1}::operator()(ssl_st*) const ()
    #13 0x000000000049ed1b in bool httplib::detail::read_and_close_socket_ssl<httplib::SSLClient::read_and_close_socket(int, httplib::Request&, httplib::Response&)::{lambda(ssl_st*)#1}, httplib::SSLClient::read_and_close_socket(int, httplib::Request&, httplib::Response&)::{lambda(ssl_st*)#2}, httplib::SSLClient::read_and_close_socket(int, httplib::Request&, httplib::Response&)::{lambda(ssl_st*, httplib::Stream&, bool, bool&)#3}>(int, unsigned long, ssl_ctx_st*, std::mutex&, httplib::SSLClient::read_and_close_socket(int, httplib::Request&, httplib::Response&)::{lambda(ssl_st*)#1}, httplib::SSLClient::read_and_close_socket(int, httplib::Request&, httplib::Response&)::{lambda(ssl_st*)#2}, httplib::SSLClient::read_and_close_socket(int, httplib::Request&, httplib::Response&)::{lambda(ssl_st*, httplib::Stream&, bool, bool&)#3}) ()
    #14 0x000000000049afb0 in httplib::SSLClient::read_and_close_socket(int, httplib::Request&, httplib::Response&) ()
    #15 0x0000000000499c88 in httplib::Client::send(httplib::Request&, httplib::Response&) ()
    #16 0x000000000049a57e in httplib::Client::Post(char const*, std::multimap<std::string, std::string, httplib::detail::ci, std::allocator<std::pair<std::string const, std::string> > > const&, std::string const&, char const*) ()
    
    bug 
    opened by y1015860449 28
  • Client keep-alive support is broken

    Client keep-alive support is broken

    Hello,

    I recently added this http library to server HTTP requests within the ccache compiler cache. Because a compilation requires up four HTTP requests I enabled keep-alive support in the Client.

    But we observed a race condition on Linux:

    1. Connection is opened and a GET request is issued. Turns out to be a cache miss
    2. Compilation starts and takes 90s, meanwhile the server closes the connection
    3. A PUT is issued on the now closed connection
    4. The ccache process receives SIGPIPE.

    Somehow the is_alive detection does not work as expected. A rough test case looks like this:

    TEST(KeepAlive, SimpleInterface_Online) {
    
      const auto host = "127.0.0.1";
      const auto port = 8080;
      const auto resourcePath = "/hi";
    
      Server svr;
    
      svr.set_keep_alive_timeout(3);
    
      svr.Get(resourcePath, [](const httplib::Request &, httplib::Response &res) {
        res.set_content("Hello World!", "text/plain");
      });
    
      auto a2 = std::async(std::launch::async, [&svr, host, port]{ svr.listen(host, port); });
    
      std::this_thread::sleep_for(std::chrono::milliseconds(200));
    
      Client cli(host, port);
      cli.set_keep_alive(true);
    
      auto result = cli.Get(resourcePath);
      ASSERT_TRUE(result);
      EXPECT_EQ(200, result->status);
    
      std::this_thread::sleep_for(std::chrono::seconds(5));
    
      result = cli.Get(resourcePath);
      ASSERT_TRUE(result);
      EXPECT_EQ(200, result->status);
    
      svr.stop();
      a2.wait();
    }
    

    Besides the non-working is_alive detection the library should set the MSG_NOSIGNAL on Linux and SO_NOSIGPIPE on Apple platforms to not badly interfere with the library host process.

    Thanks, Gregor

    bug need help 
    opened by gjasny 27
  • SSLSocketStream::read Problem

    SSLSocketStream::read Problem

    I found SSLSocketStream::read whill cause many dead block thread. I saw SSLSocketStream::read,this function call select function to check is the data is avaliable.I found the reason maybe is when the socket comming bytes is not enough to produce a ssl record, and this time server or proxy 's connection is lost.

    I don't make sure my this post is correct,i am not familiar with openssl.

    I found some info aboud SSL_read and select below: http://openssl.6102.n7.nabble.com/Graceful-shutdown-of-TLS-connection-for-blocking-sockets-tp72626p72662.html https://blog.csdn.net/dog250/article/details/5456022 https://baijiahao.baidu.com/s?id=1592012048270657934

    thanks

    need more information to verify 
    opened by xtayaitak 21
  • Middleware-like usage?

    Middleware-like usage?

    Is there any way that I could set up a handler that just intercepts all requests, sends some headers or something, and returns, moving on to the handler for that specific URL?

    Or will I have to set up the function calls myself?

    opened by ghost 20
  • Requests from browsers seem to freeze server on Windows

    Requests from browsers seem to freeze server on Windows

    I'm seeing a 5-10 second freeze in the handling of requests for the example test server whenever I make a request from a browser with the server running on a windows machine.

    This is my set-up:

    I'm running the example server on a windows machine. I've got a couple of scripts calling the /dump endpoint as fast as they can using curl. This is fine and I can have multiple clients calling dump without any issues (response times in the order of milliseconds).

    However, as soon as I make one request from a browser all other clients freeze for 5-10 seconds before continuing. The response to the browser is also fairly slow.

    This is the dump from the request from the browser that is causing the issue:

    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    Accept-Encoding: gzip, deflate, br
    Accept-Language: en-US,en;q=0.9,nl;q=0.8
    Connection: keep-alive
    Host: 127.0.0.1:8080
    REMOTE_ADDR: 127.0.0.1
    REMOTE_PORT: 59759
    Sec-Fetch-Dest: document
    Sec-Fetch-Mode: navigate
    Sec-Fetch-Site: none
    Sec-Fetch-User: ?1
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36
    
    bug 
    opened by xy4455 19
  • Versioning

    Versioning

    Would it be possible for you to release a initial version (even if it is 0.1)? Using tags and the github release system. I would like to create a conan package that points to your repo for easy compilation using CMake.

    I have made a pull request to add CMake support. Once you are satisfied with it, it would be nice if you made a release so I can properly add Conan support for master and the first version number.

    enhancement 
    opened by omaralvarez 19
  • Memory leak in the upload example

    Memory leak in the upload example

    When using the upload example and you upload a larger jpeg image e.g. 5MB, several times, the memory size of the upload process will start increase each time you upload the image. This does not happen the first time you upload a image.

    need help need more information to verify 
    opened by tietzedk 18
  • DoS/Segfault when request path contains more than 387 characters

    DoS/Segfault when request path contains more than 387 characters

    Due to that someone reported a crash on my app when passing very long parameters to the server i did some deeper investigation.

    It turns out that this is happening in httplib.

    So i tried a bit and was able to verify my finding even with your example server.cc code.

    This is the env im using:

    alpine64:~# g++ --version
    g++ (Alpine 10.2.1_pre1) 10.2.1 20201203
    

    With a not to long path everyting works as expected.

    alpine64:~# g++ server.cc -I. -g -o server
    alpine64:~# ./server
    ================================
    GET HTTP/1.1 /tinypath
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    Accept-Encoding: gzip, deflate
    Accept-Language: de,en-US;q=0.9,en;q=0.8,es;q=0.7
    Connection: keep-alive
    DNT: 1
    Host: 192.168.0.36:8080
    REMOTE_ADDR: 192.168.0.55
    REMOTE_PORT: 49466
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36
    --------------------------------
    404 HTTP/1.1
    Content-Length: 56
    Content-Type: text/html
    Keep-Alive: timeout=5, max=5
    
    <p>Error Status: <span style='color:red;'>404</span></p>
    

    But using this request it segfaults.

    http://192.168.0.36:8080/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccddddddddddddddddddddddeeeeeeeeeeeeeeeeeeefffffffffffffffffffffffffffffffffffggggggggggggggggggggggggggggg
    
    alpine64:~# ./server
    Segmentation fault (core dumped)
    

    Even without printing the headers.

    The segfault is happening somewhere here:

    #2023 0x00005610ffe76a3c in std::regex_match<char, std::allocator<std::__cxx11::sub_match<char const*> >, std::__cxx11::regex_traits<char> > (__s=0x7f98b1e29e70 "GET /", 'a' <repeats 195 times>..., __m=..., __re=..., __f=0) at /usr/include/c++/10.2.1/bits/regex.h:2229
    #2024 0x00005610ffe6d552 in httplib::Server::parse_request_line (this=0x7fff357a30e0, s=0x7f98b1e29e70 "GET /", 'a' <repeats 195 times>..., req=...) at ./httplib.h:4528
    

    CoreDump is attached.

    core.log

    I'm somehow not able to reproduce this on my Arch, Debian and Ubuntu machine. It might be GCC version related or that alpine is using musl instead of libc. But the binary produced on alpine executed on Debian/Ubuntu/Arch crashs there too with the large request.

    Thanks in advance

    bug 
    opened by Bendr0id 17
  • [Bug] Can't upload file to VirusTotal

    [Bug] Can't upload file to VirusTotal

    Hi, i'm using VirusTotal API in order to check is file malware, so I need to upload file. I tried to do it in that way, but it have not succeeded:

    httplib::Client cli("https://www.virustotal.com");
    
    httplib::Headers headers{
    	{ "Accept", "text/plain" },
    	{ "Content-Type", "application/x-www-form-urlencoded" }
    };
    httplib::MultipartFormDataItems items{
    	{ "apikey", api_key, "", "" },
    	{ "file", std::string(buffer.begin(), buffer.end()), file_name, ""}
    };
    
    auto res = cli.Post("/vtapi/v2/file/scan", headers, items);
    

    The error that I get is: "Write (5)" I tried to do it also in another way and it have not worked too:

    httplib::Client cli("https://www.virustotal.com");
    
    httplib::Headers headers{
    	{ "Accept", "text/plain" },
    	{ "Content-Type", "application/x-www-form-urlencoded" }
    };
    httplib::Params params{
    	{ "apikey", std::string(api_key) },
    	{ "file", "data:name=" + std::string(file_path) + ";base64," + encoded }
    };
    
    auto res = cli.Post("/vtapi/v2/file/scan", headers, params);
    

    Although, I tried to use curl lib and it works great, there is a working code:

    auto params = "apikey=" + std::string(api_key) + "&file=data:name=" + std::string(file_path) + ";base64," + encoded;
    
    CURL* hnd = curl_easy_init();
    curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
    curl_easy_setopt(hnd, CURLOPT_URL, "https://www.virustotal.com/vtapi/v2/file/scan");
    struct curl_slist* headers = NULL;
    headers = curl_slist_append(headers, "Accept: text/plain");
    headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
    curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, params.c_str());
    std::string readBuffer;
    curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, WriteCallback);
    curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &readBuffer);
    CURLcode ret = curl_easy_perform(hnd);
    curl_easy_reset(hnd);
    

    Am I doing something wrong?

    need more information to verify 
    opened by sccoouut 16
  • Fix

    Fix "Location" header url corrupted by percent-unescaping in redirect…

    Redirected GET request like https://sphinx.acast.com/p/acast/s/a-bientot-de-te-revoir/e/63a4721c69c77e001126ad39/media.mp3 are failing and while it looks like an agent setup problem it is actually the processing of the Location response header.

    httplib is percent-un-escaping every response header with decode_url in parse_header, giving this address https://stitcher2.acast.com/livestitches/4d4cc4fe72c9452bd0b0992a5c89e434.mp3?aid=63a4721c69c77e001126ad39&chid=a8879bdf-de58-4537-8dab-a3bb13948786&ci=oFpQlSRp3GFDZwrcZw5e3SEuFWtGfBXjcj6-mtxC8TJYKWNWTP3KWg==&pf=rss&[email protected]&uid=6ec01abdba610f88f88e42ff560ecdef&Expires=1672100680731&Key-Pair-Id=K38CTQXUSD0VVB&Signature=XoIMT7YfpbpOerJXwA4JVT-zat8V2flxU5AKtwr8LEGegGAu6hNSgeyLgq7gQmpv6pv6im2hKSyfUUqQmBEW8MCFLUYiUXuSVEcuVZ3BAT8u0gzcSdTFC1wOGhZTAExH15vei9-UAOVMj7Mq-jP-8hd-H~Atrj2YKI9krbWoslScK4yepWvpzwvBWP8-58NPIy6FaSfMHWwODigNCrJudiR0DPrr6x-HVSiwB~q5aTNVvlABQqGxNkpWtnAie8TuYKEvmioTlEL1aFj8RxMWke7yRc4uOchJtak5COoej4x780f0mepp-eh0OGtsB1izB7hGsyob0c8DwCYoVGTsRg__ in the Location result header and the redirected request fails.

    However Firefox (for instance) successfully redirects to https://stitcher2.acast.com/livestitches/4d4cc4fe72c9452bd0b0992a5c89e434.mp3?aid=63a4721c69c77e001126ad39&chid=a8879bdf-de58-4537-8dab-a3bb13948786&ci=oFpQlSRp3GFDZwrcZw5e3SEuFWtGfBXjcj6-mtxC8TJYKWNWTP3KWg%3D%3D&pf=rss&sv=sphinx%401.134.1&uid=6ec01abdba610f88f88e42ff560ecdef&Expires=1672109987714&Key-Pair-Id=K38CTQXUSD0VVB&Signature=TQXsBs7XluU~YRtPTcYe1EtVuvnkf542tbp1p7KUnvn24rm-tQjO8dYgLSbXlJCBwsiPtbnJc-YjLbGlaVLKDzzfABj2lCldE-KoeUSdnEQPWXdPK6FK5BR7kuN-CuY1MfQ-0sDa4MTGAErHZZB1p3~jiiZbbP7fYd9ttBfXwlZgjv5BtHOL4KQs7QY7q-~ZP5tXoGhtufPMruWRYOptrves991ax5lgKPwTvzhXSL6CEKpHWoAMi88shXnBBC~f2iOropB-yzcj5K-uaK6LPcObfHh9Akgl~uIAqbLka2Nrq-HQ-7QrMIUmFcA2nTEaAF66dGRj7AGtEkS2m2hB4A__, with escaped parts here %3D%3D&pf=rss&sv=sphinx%40

    This fix skips decoding for Location (case sensitive) only, it may be too restrictive.

    opened by maudoin 4
  • add support for requests with both MultipartFormDataItems and Content Providers

    add support for requests with both MultipartFormDataItems and Content Providers

    I had a situation where I needed to send a POST request with some multipart parameters, some of which needed to be fed by content providers (chunked transfer) rather than just a string. I implemented just that.

    I tried to get it working with the most idiomatic code for this library but there are still a few combinations missing, e.g. right now it only works with ContentProviderWithoutLength not just ContentProvider. Maybe it would be a better idea to just use ContentProvider and then users can use the ContentProviderAdapter? Only problem is this adapter class doesn't seem to be accessible outside httplib.

    opened by stingray-11 5
  • URL query string encode not percent-encoding some characters

    URL query string encode not percent-encoding some characters

    According to rfc3986, only unreserved characters (ALPHA / DIGIT / "-" / "." / "_" / "~") can remain unencoded. However, the function that encodes a query string encode_query_param() is failing to encode some characters, specifically, the sub-delims group ("!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=").

    URI producing applications should percent-encode data octets that correspond to characters in the reserved set unless these characters are specifically allowed by the URI scheme to represent data in that component.
    

    To comply with the standard, this function should encode those characters as well, so I propose the following implementation.

    inline std::string encode_query_param(const std::string &value) {
      std::ostringstream escaped;
      escaped.fill('0');
      escaped << std::hex;
    
      for (auto c : value) {
        if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '.' ||
            c == '_' || c == '~') {
          escaped << c;
        } else {
          escaped << std::uppercase;
          escaped << '%' << std::setw(2)
                  << static_cast<int>(static_cast<unsigned char>(c));
          escaped << std::nouppercase;
        }
      }
    
      return escaped.str();
    }
    

    This probably conflicts with the interests of @Yrds in #788, and the specifications allows the use of un-encoded reserved characters if the specific implementation allows them, so maybe the best way to fix it would be to add an extra parameter to the function with the white-listed characters.

    opened by PabloMK7 0
  • add API support for verify certificate manually

    add API support for verify certificate manually

    Thanks for this great library! 👍

    Would it be possible to extend the API like SSLClient::set_server_certificate_verification(std::function<bool (SSL_CTX *ctx)>) to allow verify certificate manually? just like asio::ssl::context::set_verify_callback.

    I think it would be very useful when verify static self-signed certificate.

    enhancement 
    opened by h20282 1
  • noexcept annotations?

    noexcept annotations?

    It would be good to annotate methods where we should not throw or do not call any throwing operations with noexcept. This helps ensure adherence to C++ guidelines, such as E.12 and F.6.

    suggestion 
    opened by bobbymcr 1
  • Cannot access SSL Error

    Cannot access SSL Error

    Is not possibile to access to the SSL error in Connect or in Accept, the error is local to template bool ssl_connect_or_accept_nonblocking. Solutions could be have a "last_res" in client/server class so will be possible to retrieve the ssl error with SSL_get_error(ssl, res), or save a last_ssl_err or expand enum class Error to every possible SSL connect/accept error

    enhancement 
    opened by bartols 1
Releases(v0.11.3)
Owner
null
Ultra fast and low latency asynchronous socket server & client C++ library with support TCP, SSL, UDP, HTTP, HTTPS, WebSocket protocols and 10K connections problem solution

CppServer Ultra fast and low latency asynchronous socket server & client C++ library with support TCP, SSL, UDP, HTTP, HTTPS, WebSocket protocols and

Ivan Shynkarenka 958 Jan 3, 2023
H2O - the optimized HTTP/1, HTTP/2, HTTP/3 server

H2O - an optimized HTTP server with support for HTTP/1.x, HTTP/2 and HTTP/3 (experimental) Copyright (c) 2014-2019 DeNA Co., Ltd., Kazuho Oku, Tatsuhi

H2O 10.2k Dec 30, 2022
cuehttp is a modern c++ middleware framework for http(http/https)/websocket(ws/wss).

cuehttp 简介 cuehttp是一个使用Modern C++(C++17)编写的跨平台、高性能、易用的HTTP/WebSocket框架。基于中间件模式可以方便、高效、优雅的增加功能。cuehttp基于boost.asio开发,使用picohttpparser进行HTTP协议解析。内部依赖了nl

xcyl 29 Dec 17, 2022
Tiny cross-platform HTTP / HTTPS client library in C.

naett /nɛt:/ Tiny HTTP client library in C. Wraps native HTTP client functionality on macOS, Windows, Linux, iOS and Android in a single, simple non-b

Erik Agsjö 21 Nov 28, 2022
HTTP/HTTPS REST Client C Library

https_client HTTP/HTTPS REST Client C Library This library is a tiny https client library. it use only small memory(default read buffer size(H_READ_SI

HISONA 103 Dec 20, 2022
Graphical small-internet client for windows, linux, MacOS X and BSDs. Supports gemini, http, https, gopher, finger.

Graphical small-internet client for windows, linux, MacOS X and BSDs. Supports gemini, http, https, gopher, finger.

Felix Queißner 569 Dec 30, 2022
Pushpin is a reverse proxy server written in C++ that makes it easy to implement WebSocket, HTTP streaming, and HTTP long-polling services.

Pushpin is a reverse proxy server written in C++ that makes it easy to implement WebSocket, HTTP streaming, and HTTP long-polling services. The project is unique among realtime push solutions in that it is designed to address the needs of API creators. Pushpin is transparent to clients and integrates easily into an API stack.

Fanout 3.2k Jan 2, 2023
Gromox - Groupware server backend with MAPI/HTTP, RPC/HTTP, IMAP, POP3 and PHP-MAPI support for grommunio

Gromox is the central groupware server component of grommunio. It is capable of serving as a replacement for Microsoft Exchange and compatibles. Conne

grommunio 139 Dec 26, 2022
websocket and http client and server library, coming with ws, a command line swiss army knife utility

Hello world IXWebSocket is a C++ library for WebSocket client and server development. It has minimal dependencies (no boost), is very simple to use an

Machine Zone, Inc. 369 Jan 5, 2023
BingBing 60 Dec 15, 2022
A collection of C++ HTTP libraries including an easy to use HTTP server.

Proxygen: Facebook's C++ HTTP Libraries This project comprises the core C++ HTTP abstractions used at Facebook. Internally, it is used as the basis fo

Facebook 7.7k Jan 4, 2023
Micro http server and client written in C++

httpp Micro http server and client written in C++ The motivation behind this little piece of code is to provide a really simple, yet efficient HTTP se

Thomas Sanchez 158 Nov 28, 2022
A project designed for the esp8266 D1 Mini or the esp8266 D1 Mini PRO to provide a wifi http server and dns server.

PS4 Server 9.00 This is a project designed for the esp8266 D1 Mini or the esp8266 D1 Mini PRO to provide a wifi http server and dns server. this is fo

null 14 Nov 28, 2022
Asynchronous, Header-only C++ HTTP-over-(TCP|UNIX Socket|STDIO) Library

CXXHTTP A C++ library implementing an asynchronous HTTP server and client. To clone this library, make sure you also clone the submodules. The --recur

null 25 Mar 19, 2021
Tiny HTTP Server on C, using only standard libraries

hell_o Linux only. Tiny HTTP Server on C, using only standard libraries. It is unfinished yet, going to add working interface and rewrite handler late

null 3 Feb 1, 2022
single header C(99) library to implement client-server network code for games

single header C(99) library to implement client-server network code for games

Nathan 227 Jan 5, 2023
Dohd is a minimalist DNS-over-HTTPS daemon that redirects all DoH queries to a local DNS server running on localhost:53 (UDP)

dohd Dohd (pron. doh-dee) is a minimalist DNS-over-HTTPS daemon that redirects all DoH queries to a local DNS server running on localhost:53 (UDP). Fe

Dyne.org 16 Dec 1, 2022
modern c++(c++17), cross-platform, header-only, easy to use http framework

cinatra--一个高效易用的c++ http框架 English | 中文 目录 使用cinatra常见问题汇总(FAQ) cinatra简介 如何使用 快速示例 性能测试 注意事项 roadmap 联系方式 cinatra简介 cinatra是一个高性能易用的http框架,它是用modern

qicosmos 1.4k Dec 30, 2022
A Tcp/Ip stack implementation on top of Solarflare ef_vi, and a C++ headers only framework for tcp multiplexing client/server.

Efvitcp Efvitcp is a tcp library using Solarflare ef_vi interface on linux, and also a tcp multiplexing framework for both C++ client and server progr

Meng Rao 23 Nov 26, 2022