Coder Social home page Coder Social logo

yhirose / cpp-httplib Goto Github PK

View Code? Open in Web Editor NEW
12.0K 183.0 2.2K 3.19 MB

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

License: MIT License

Makefile 0.48% C++ 96.97% HTML 0.02% Python 0.13% Dockerfile 0.01% Shell 0.01% CMake 1.79% Meson 0.59%
cpp cpp11 http https header-only

cpp-httplib's Introduction

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!

Important

This library uses 'blocking' socket I/O. If you are looking for a library with 'non-blocking' socket I/O, this is not the one that you want.

Simple examples

Server (Multi-threaded)

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

SSL Support

SSL support is available with CPPHTTPLIB_OPENSSL_SUPPORT. libssl and libcrypto should be linked.

Note

cpp-httplib currently supports only version 3.0 or later. Please see this page to get more information.

Tip

For macOS: cpp-httplib now can use system certs with CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN. CoreFoundation and Security should be linked with -framework.

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

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

// Client
httplib::Client cli("https://localhost:1234"); // scheme + host
httplib::SSLClient cli("localhost:1234"); // host
httplib::SSLClient cli("localhost", 1234); // host, port

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

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

  // Match the request path against a regular expression
  // and extract its captures
  svr.Get(R"(/numbers/(\d+))", [&](const Request& req, Response& res) {
    auto numbers = req.matches[1];
    res.set_content(numbers, "text/plain");
  });

  // Capture the second segment of the request path as "id" path param
  svr.Get("/users/:id", [&](const Request& req, Response& res) {
    auto user_id = req.path_params.at("id");
    res.set_content(user_id, "text/plain");
  });

  // Extract values from HTTP headers and URL query params
  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

Warning

These static file server methods are not thread-safe.

File request handler

// The handler is called right before the response is sent to a client
svr.set_file_request_handler([](const Request &req, Response &res) {
  ...
});

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_ptr ep) {
  auto fmt = "<h1>Error 500</h1><p>%s</p>";
  char buf[BUFSIZ];
  try {
    std::rethrow_exception(ep);
  } catch (std::exception &e) {
    snprintf(buf, sizeof(buf), fmt, e.what());
  } catch (...) { // See the following NOTE
    snprintf(buf, sizeof(buf), fmt, "Unknown Exception");
  }
  res.set_content(buf, "text/html");
  res.status = StatusCode::InternalServerError_500;
});

Caution

if you don't provide the catch (...) block for a rethrown exception pointer, an uncaught exception will end up causing the server crash. Be careful!

Pre routing handler

svr.set_pre_routing_handler([](const auto& req, auto& res) {
  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()) {
      // NOTE: `content_reader` is blocking until every form data field is read
      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;
      });
    }
  });

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](bool success) { delete data; });
});

Without content length:

svr.Get("/stream", [&](const Request &req, Response &res) {
  res.set_content_provider(
    "text/plain", // Content type
    [&](size_t offset, 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.
    }
  );
});

With trailer:

svr.Get("/chunked", [&](const Request& req, Response& res) {
  res.set_header("Trailer", "Dummy1, Dummy2");
  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_with_trailer({
        {"Dummy1", "DummyVal1"},
        {"Dummy2", "DummyVal2"}
      });
      return true;
    }
  );
});

'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 StatusCode::ExpectationFailed_417;
});
// Send a final status without reading the message body.
svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
  return res.status = StatusCode::Unauthorized_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

Note

When the request body content type is 'www-form-urlencoded', the actual payload length shouldn't exceed CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH.

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

You can also provide an optional parameter to limit the maximum number of pending requests, i.e. requests accept()ed by the listener but still waiting to be serviced by worker threads.

svr.new_task_queue = [] { return new ThreadPool(/*num_threads=*/12, /*max_queued_requests=*/18); };

Default limit is 0 (unlimited). Once the limit is reached, the listener will shutdown the client connection.

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 bool enqueue(std::function<void()> fn) override {
    /* Return true if the task was actually enqueued, or false
     * if the caller must drop the corresponding connection. */
    return 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 == StatusCode::OK_200) {
      std::cout << res->body << std::endl;
    }
  } else {
    auto err = res.error();
    std::cout << "HTTP error: " << httplib::to_string(err) << std::endl;
  }
}

Tip

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,
  Compression,
  ConnectionTimeout,
};

GET with HTTP headers

httplib::Headers headers = {
  { "Accept-Encoding", "gzip, deflate" }
};
auto res = cli.Get("/hi", headers);

or

auto res = cli.Get("/hi", {{"Accept-Encoding", "gzip, deflate"}});

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(StatusCode::OK_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 cli(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

Use poll instead of select

select system call is used as default since it's more widely supported. If you want to let cpp-httplib use poll instead, you can do so with CPPHTTPLIB_USE_POLL.

Split httplib.h into .h and .cc

$ ./split.py -h
usage: split.py [-h] [-e EXTENSION] [-o OUT]

This script splits httplib.h into .h and .cc parts.

optional arguments:
  -h, --help            show this help message and exit
  -e EXTENSION, --extension EXTENSION
                        extension of the implementation file (default: cc)
  -o OUT, --out OUT     where to write the files (default: out)

$ ./split.py
Wrote out/httplib.h and out/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

cpp-httplib officially supports only the latest Visual Studio. It might work with former versions of Visual Studio, but I can no longer verify it. Pull requests are always welcome for the older versions of Visual Studio unless they break the C++11 conformance.

Note

Windows 8 or lower, Visual Studio 2013 or lower, and Cygwin and MSYS2 including MinGW are neither supported nor tested.

License

MIT license (© 2024 Yuji Hirose)

Special Thanks To

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

cpp-httplib's People

Contributors

abouvier avatar bugdea1er avatar const-volatile avatar fefer-ivan avatar gh4ck3r avatar gjasny avatar hyperxor avatar jfsimoneau avatar jimmy-park avatar jrosdahl avatar kuguma avatar matvore avatar miketsts avatar mrexodia avatar nioncode avatar omaralvarez avatar omjego avatar ottigeda avatar pixlrainbow avatar rockybilly avatar sgraham avatar stingray-11 avatar sum01 avatar sux2mfgj avatar tachi107 avatar themaverickprogrammer avatar wanggao1990 avatar yhirose avatar yrds avatar zefz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cpp-httplib's Issues

having Linker problem ....

LNK2019 unresolved external symbol __imp_WSAStartup referenced in function "public: __cdecl httplib::detail::WSInit::WSInit(void)" (??0WSInit@detail@httplib@@qeaa@XZ)

got this error when i add <httplib.h> file in my project....
pls help...

Support Timeouts on Client

How can we define custom timeouts for requests?

My current code:

    httplib::Client cli(serverAddress.c_str(), port);
    auto res = cli.post(path, data, "text/plain");
    if (res == nullptr) {
      LOGE() << "failed to send data to " << path << ": got no response";
      return;
    }
    if (res->status != 200) {
      LOGE() << "failed to send data to " << path << ", response code: " << res->status;
      return;
    }

This just blocks when the server is not reachable. I'd like to report an error after 5 seconds.

httplib::Map does not exist

Under POST with parameters in the README it is showed how to use httplib::Map to post data. This does not exist anymore and should be updated to httplib::Params (?).

Assertion `s[i]' failed.

Every now and then my server crashes with following error.

main: ext/httplib.h:651: std::string httplib::detail::decode_url(const string&): Assertion `s[i]' failed.

Http Request Middleware -Benchmark

First of all. Contratulation on your excellent work. Iam coming from nodejs world so i found it easy to understand your vision.

  1. Does this library support some kind of middleware functions (auth or ip filter middleware etc.).

  2. Secondly, is there any benchmark that compares performance with other frameworks ? (cpprestapi
    casablanca etc.)
    Thank you.

Get full URL from Request?

First of all, great little library!

Is it possible to get the full URL as sent by the client from the Request struct? I know I can get the path and params, but sometimes it's useful to get the full URL.
So, if the browser requests: http://127.0.0.1:1234/app?var1=value1&var2=value2
With Request::path I can get e . g. /app. With Request::params I can access the variables, but seems there is no way to access the full URL (besides reconstructing it yourself)?

I think something like Request::url which would return e. g. http://127.0.0.1:1234/app?var1=value1&var2=value2 or at least app?var1=value1&var2=value2 would be quite useful.

If no "Content-Length" is sent in the response-header the body is always empty

It is allowed in HTTP 1.0 to have no "Content-Length" header field in the response-header: In this case closing of the connection indicates the end of the response-body. But in the current implementation reading the body of the response is skipped if there is no "Content-Length" field, resulting into an empty body.
The template function read_content(Stream& strm, T& x) is used for the server (handle request) and for the client (handle response), but the handling for no "Content-Length" should only be applied to the response.

An example which demonstrates the problem (openssl-1.0.2l, Windows, Visual Studio 2017):

#include <iostream>
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include <httplib.h>
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "libeay32.lib")
#pragma comment(lib, "ssleay32.lib")

int main(int argc, char* argv[]) {
  httplib::SSLClient cli("bittrex.com", 443);
  auto res = cli.get("/api/v1.1/public/getmarkets");
  if (res && res->status == 200) {
    std::cout << res->body << std::endl;
  }
  return 0;
}

I have created a template specialization for Response which will work (but is maybe not optimal, because it adds bytes to the body one-by-one, similar to socket_gets).

bool read_content(Stream& strm, Response& x)
{
  auto len = get_header_value_int(x.headers, "Content-Length", 0);
  if (len) {
    x.body.assign(len, 0);
    auto r = 0;
    while (r < len) {
      auto r_incr = strm.read(&x.body[r], len - r);
      if (r_incr <= 0) {
        return false;
      }
      r += r_incr;
    }
  } else { /* Start Added code */
    for (;;) {
      char byte;
      auto n = strm.read(&byte, 1);
      if (n < 1) {
        if (x.body.size() == 0) {
          return true; // no body
        }
        else {
          break;
        }
      }
      x.body += byte;
    }
  } /* End Added code */
  return true;
}

RESTful web server

Thank you for this project! Thank you for your effort!

Header-only, no dependencies. cross-platform, light and elegant... It's the closest thing to the solution I was looking for.

Only I miss a more complete interface (with PUT and DELETE and perhaps, others). Any reason not to implement those features?

In any case I am not an expert and I think that what your project offers is enough for my purposes. :-)

Thanks again!

DJuego

c++11 ‘auto’ in lambda parameter

Hi!
It is said that cpp-httplib is C++11 library. But when I try to build example on gcc, I get an error:

error: use of ‘auto’ in lambda parameter declaration only available with -std=c++14 or -std=gnu++14

While this relates only to examples code it... should be at least said somewhere...

extra data present in body

when writing the response body to a file, i found that it contained extra unwanted data. I was trying to make it get a file from a api, the data is in base64 and it appends value before the expected data

image

the expected result =

image

Any help would be appreciated

used code -

httplib::SSLClient client("example.com", 443);
auto logincheck = "/load?username=" + user + "&product=" + product;
const char* chr = _strdup(logincheck.c_str());
auto res = client.get(chr);
if (res && res->status == 200)
encoded << res->body;

sorry for the bad code format

edit : found that it might be caused by it displaying chunks as the server is sending it with "Transfer-Encoding: chunked"

Error in README.md sample code

Great library -- one issue is that the sample code in the README.md doesn't compile because
the parameters const Response& res are actually supposed to be non-const: Response& res

svr.get("/hi", [](const Request& req, const Response& res) {
        res.set_content("Hello World!", "text/plain");
    });

and

    svr.get(R"(/numbers/(\d+))", [&](const Request& req, const Response& res) {
        auto numbers = req.matches[1];
        res.set_content(numbers, "text/plain");
    });

How to build with Mingw on Windows?

Hey there,

I really like the idea behind this library, but I am running into a few issues when trying to include this with my project. I am using g++ with Mingw on Windows, rather than Visual Studio.

My current command to run everything looks like this:

g++ test.cpp httplib.h -DWEBVIEW_WINAPI=1 -lole32 -lcomctl32 -loleaut32 -luuid -mwindows -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextraw

I am also using a Webview library to create a browser. The errors that I receive are related to " undefined reference to `_imp__freeaddrinfo@4'" for example, so linker errors.

Any ideas?

Case sensitivity in regex for multipart/form-data

Some browsers seem to send metadata about MultipartFiles as lower-case 'content-type' and 'content-disposition'. This seems to be consistent with the specification (RFC7578). Currently, these are missed with this http server and are therefore not handled correctly.

The simple diff below changes the regex(es) that look for this metadata to case-insensitive and fixes this problem.

$ git diff httplib.h
diff --git a/httplib.h b/httplib.h
index 1716d0e..461c1e4 100644
--- a/httplib.h
+++ b/httplib.h
@@ -1026,10 +1026,9 @@ inline bool parse_multipart_formdata(
     static std::string crlf = "\r\n";

     static std::regex re_content_type(
-        "Content-Type: (.*?)");
+        "Content-Type: (.*?)", std::regex_constants::icase);
     static std::regex re_content_disposition(
-        "Content-Disposition: form-data; name=\"(.*?)\"(?:; filename=\"(.*?)\")?");
+        "Content-Disposition: form-data; name=\"(.*?)\"(?:; filename=\"(.*?)\")?", std::regex_constants::icase);

     auto dash_boundary = dash + boundary;

HTTP POST request

POST requests are extremely slow. Each request is taking about 1 second to complete. I am using the library on windows.

Server Name Indication for httplib::SSLClient

First of all, thanks for providing such a great library!

I had a problem to get connections to certain HTTPS hosts working with using httplib::SSLClient. After investigating with openssl s_client, I found that the problem is that the host sends the wrong certificate to the client. This is because, the server doesn't know for which site the client expects the certificate if multiple sites/certificates are on the same host (i.e. on a virtual server).
The solution to this problem in TLS is the extension RFC 6066, Server Name Indication. Using this TLS extension the client will send during the handshake the servername for which he wants to receive the certificate, so that the correct certificate can be supplied to the client. The extension is already implemented in openssl and can be used with the "-servername" parameter, i.e. openssl s_client -connect httpbin.org:443 -servername httpbin.org

SNI (Server Name Indication) is widely used on the internet and I think it would be a valuable addition to cpp-httplib.

A minimal example which shows the problem using httplib::SSLClient (openssl-1.0.2l, Visual Studio 2017):

#include <iostream>

#define CPPHTTPLIB_OPENSSL_SUPPORT
#include <httplib.h>
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "libeay32.lib")
#pragma comment(lib, "ssleay32.lib")

int main(int argc, char* argv[]) {
  httplib::SSLClient cli("httpbin.org", 443);
  auto res = cli.get("/get");
  if (res && res->status == 200) {
    std::cout << res->body << std::endl;
  }
  return 0;
}

One possible workaround in read_and_close_socket_ssl is:

SSL_set_bio(ssl, bio, bio);
SSL_set_tlsext_host_name(ssl, "httpbin.org"); // workaround
SSL_connect_or_accept(ssl);

It would be more elegant, if the hostname would come from the Client::host_, but it is not accessible from there with the current interface.

SIGPIPE (signal SIGPIPE) after some time working

Having a pretty simple server, which is set up very close to the examples.

After some time of fine running, it just starts failing with SIGPIPE here:

inline int SocketStream::write(const char* ptr, size_t size) // ptr = "Connection: close\r\n"
    {
        return send(sock_, ptr, size, 0);
    }
Linux 4.4.0-53-generic #74-Ubuntu SMP Fri Dec 2 15:59:10 UTC 2016 x86_64 x86_64 
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)

Update:
Sometimes it runs really long without it. So one can say, that sometimes it is not happening.

Receving SIGPIPE signal if the hostname / port is wrong

When using the code sample :

` ``
std::thread signaling_health_thread([] () {
printf("hey from new thread!\n");
httplib::Client cli("localhost", 1234);

auto res = cli.get("/hi");
printf("I have got a res!\n");
if (res && res->status == 200) {
  std::cout << res->body << std::endl;
}
});
signaling_health_thread.join(); 

I get a SIGPIPE signal if there is no port open on localhost:1234.

Is that expected ? Thanks.

Add multipart-formdata support

It would be really nice if we could process forms encoded multipart/form-data, including file uploads. Unfortunately this feature lacks in most lightweight C++ HTTP libraries, though it doesn't takes much slocs to implement.

client needs Host header

The client, when doing a request to a server, does not include the host in the header. This causes some failures on some sites. Something like this fixes the problem (presumably also needed for HEAD and POST methods):

@@ -1078,6 +1084,7 @@ inline std::shared_ptr<Response> Client::get(const char* path)
     Request req;
     req.method = "GET";
     req.path = path;
+    req.set_header("Host", host_.c_str());
 
     auto res = std::make_shared<Response>();

Multi-thread support

Multi-thread support to accept simultaneous connections as bellow.

ab -n 10 -c 5 http://host:port/slow_request_which_takes_10_secondes

Allow users to disable this feature with CPPHTTPLIB_NO_MULTI_THREAD_SUPPORT.

add GET with byte range

added functionality to get downloading portions of big files using range_low & range_high
can be easily achieved with following code:
( default parameter values must be defined in the class definition, they are just in the implementation for ovesight )
`
inline void Client::add_range_header (Request &req, int64_t range_low, int64_t range_high)
{
std::string range_str = "bytes=";

            if ( range_low >= 0 )
            {
                    range_str += std::to_string (range_low);
            }

            range_str += "-";

            if ( range_high >= 0 )
                    range_str += std::to_string (range_high);
            
            req.set_header ("Range", range_str.c_str ());
    }

    inline std::shared_ptr<Response> Client::get (const char* path, Progress callback, int64_t range_low=-1, int64_t range_high=-1)
    {
            Request req;
            req.method = "GET";
            req.path = path;
            req.progress = callback;
            add_default_headers (req);


            if ( range_low >= 0 )
                    add_range_header (req, range_low, range_high);

            auto res = std::make_shared<Response> ();

            return send (req, *res) ? res : nullptr;
    }

`

Issues with cpp-httplib and MinGW

Hi again!
I try cpp-httplib with MinGW (x86_64-7.3.0-release-win32-seh-rt_v5-rev0.7z)

I have had to add to httplib.h the lines (and #define WIN32_ and #define MINGW)

#ifdef MINGW
#include "mingw.mutex.h"
#include "mingw.thread.h"
#else
#include <mutex>
#include <thread>
#endif

NOTE: I got mingw.mutex.h and mingw.thread.h from https://github.com/meganz/mingw-std-threads

After that, i get:

..\..\inc\cpp-httplib/httplib.h: In function 'std::__cxx11::string httplib::detail::get_remote_addr(socket_t)':
..\..\inc\cpp-httplib/httplib.h:629:9: error: 'inet_ntop' was not declared in this scope
         inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof(ipstr));
         ^~~~~~~~~
..\..\inc\cpp-httplib/httplib.h:629:9: note: suggested alternative: 'inet_ntoa'
         inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof(ipstr));
         ^~~~~~~~~
         inet_ntoa
..\..\inc\cpp-httplib/httplib.h:633:9: error: 'inet_ntop' was not declared in this scope
         inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof(ipstr));
         ^~~~~~~~~
..\..\inc\cpp-httplib/httplib.h:633:9: note: suggested alternative: 'inet_ntoa'
         inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof(ipstr));
         ^~~~~~~~~
         inet_ntoa

Apparently, I have solved this problem by adding the following code to httplib.h.

#ifdef MINGW
const char* inet_ntop(int af, const void* src, char* dst, int cnt){

    struct sockaddr_in srcaddr;

    memset(&srcaddr, 0, sizeof(struct sockaddr_in));
    memcpy(&(srcaddr.sin_addr), src, sizeof(srcaddr.sin_addr));

    srcaddr.sin_family = af;
    if (WSAAddressToString((struct sockaddr*) &srcaddr, sizeof(struct sockaddr_in), 0, dst, (LPDWORD) &cnt) != 0) {
        DWORD rv = WSAGetLastError();
        printf("WSAAddressToString() : %d\n",rv);
        return NULL;
    }
    return dst;
}
#endif

I am going to test it but...Is there any better solution?

DJuego

Race in startup/shutdown test code

I think this only really affects the test code, but I thought perhaps you would want to know.

In investigating a possible patch for #46, I split listen() into helper functions locally. After I did that I was experiencing an occasional crash in the server tests. I made a repro by adding a short sleep() in the body of listen() on master like this: https://github.com/yhirose/cpp-httplib/compare/master...sgraham:start-stop-crash?expand=1

When I run this (on a Mac), ASAN reports a crash in the thread:
https://gist.github.com/sgraham/461f286144ef8c7e531dad75ec2960d1

I think this is because the svr_.is_running() in the test SetUp() passes while listen() is in the sleep, then the server is immediately stopped, then the thread continues on to detail::select_read(), but svr_sock_ is now -1, so select_read() crashes in FD_SET(-1, &fds).

(Sorry for the long explanation without a fix, I wasn't immediately sure where the correct place to synchronize would be.)

client fails to read long response

If the HTTP server the client is requesting from sends a large page, the client will not read all of the response, because there is only one call made to the socket recv() method.

Something like this will fix it:

@@ -508,10 +508,16 @@ bool read_content(Stream& strm, T& x)
 {
     auto len = get_header_value_int(x.headers, "Content-Length", 0);
     if (len) {
       x.body.assign(len, 0);
-        if (!strm.read(&x.body[0], x.body.size())) {
-            return false;
-        }
+      auto r = 0;
+      while(r < len){
+       auto r_incr = strm.read(&x.body[r], x.body.size());
+       r += r_incr;
+       if(r_incr <= 0) break;
+      }
+      if (!r) {
+       return false;
+      }
     }
     return true;
 }

Warning: bufSize is not used

Hi,

I just starting trying your library and noticed a warning:

cpp-httplib/httplib.h:256:54: warning: unused parameter ‘bufsiz’ [-Wunused-parameter]
inline bool socket_gets(Stream& strm, char* buf, int bufsiz)

Here

I could ignore it, but since it's a buffer size, wouldn't that be a bug and the bufsize should be used to prevent buffer overflow, no ?

Thanks

Baptiste

Split into multiple headers for development and merge for distribution

It would be really helpfull to split the library into multiple .hpp files for development and then merge them into single .hpp / .h for distribution :)
Examples:
https://github.com/ThePhD/sol2/blob/develop/single.py
https://github.com/oftc/jsoncpp/blob/master/amalgamate.py
https://github.com/connormanning/arbiter/blob/master/amalgamate.py

This would not only allow for easier development but also for possible PRs/contributions or simple modifications/extensions that others might need to make.
It would also improve code reuse and abstraction mechanisms.
For example sprintf_socket could be made as a method writefmt of Stream and not implementation detail.

Crashing on shutdown due to handler threads

I'm finding I have crashes on shutdown due to https://github.com/yhirose/cpp-httplib/blob/master/httplib.h#L1596.

In this case, the worker thread is copying this by value. If, while the thread is servicing the request the server is shutdown, listen() returns, and Server is destroyed, then e.g. Server::process_request can try to access freed memory running on the thread.

A simple way to fix this would be to have listen() store the std::threads instead of detach()ing them and then .join() before exiting listen(). However, that would be very inefficient for a long running server as that list would grow during each request and not be cleared until stop().

Implementing the "// TODO: Use thread pool..." near there might be a good way to fix this (assuming the pool is joined before shutdown).

A more direct slightly hacky way might be to increment an atomic when a thread is started, decrement it on thread exit, and wait until it's zero before exiting listen().

Method to retrieve port after starting with port 0?

I was trying to use cpp-httplib for running tests, so I'd like to start it on a system-selected port, and then have clients connect to that port. I don't want to pre-select a port number to avoid collisions with other things on the system, or when tests are run in parallel.

When I do the common thing of passing 0 as the server port everything seems to work, and I was able to hunt down the actual port by running netstat, and when I then connect to that port things work just fine.

Would it be possible to have a method to retrieve port that the server's running on? (Or is that already possible and I missed how to do so?)

Delay in shutdown since moving to HTTP/1.1

Hello,

I had been using the default setting for the server which was HTTP/1.0 until recently. In test situations, the client code sends one request, and after the server has sent its response, it calls Server::stop(), ending the test. This still works correctly, however, I believe because of the change to 1.1, causes the server to wait for 5s (CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND) before termination, which makes the tests take longer than necessary.

I can work around this by locally patching CPPHTTPLIB_KEEPALIVE_MAX_COUNT to 1, but I was just wondering if there's something different I should be sending from the client (I'm only using cpp-httplib for the server, not the client side). Alternatively, maybe it would be possible to have Server::stop() signal to the select() somehow that it should shut down immediately?

Please append port number to HOST header

In RFC 7230(HTTP1.1),

if the connection's incoming TCP port number
differs from the default port for the effective request URI's
scheme, then a colon (":") and the incoming port number (in
decimal form) are appended to the authority component.

While I know your library supports HTTP/1.0,
I'm glad to append port number to HOST header like following:

#-    req.set_header("Host", host_.c_str());
+    req.set_header("Host", (host_ + ":" + std::to_string(port_)).c_str());

Static compilation results in segmentation fault

I need to build a static executable using cpp-httplib.

Then I add -static to CFLAGS and -ldl -Wl,--whole-archive -lpthread -Wl,--no-whole-archive to the LDFLAGS.

After my changes the test/Makefile contains:

CFLAGS = -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -static 
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib -lssl -lcrypto
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
EXTRA_LDFLAGS = -Wl,--whole-archive -lpthread -Wl,--no-whole-archive -ldl

all : test
	./test

test : test.cc ../httplib.h Makefile
	$(CC) -o test $(CFLAGS) test.cc gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(EXTRA_LDFLAGS)

pem:
	openssl genrsa 2048 > key.pem
	openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem

clean:
	rm test *.pem

When I run the test suite its ok until

[----------] 3 tests from ConnectionErrorTest
[ RUN      ] ConnectionErrorTest.InvalidHost
Makefile:9: recipe for target 'all' failed
make: *** [all] Falha de segmentação

Use _WIN32 defintion to detect Windows instead of _MSC_VER

Currently you are assuming that windows compiler is always MSVC (by checking_MSC_VER) which might not be true (as is in my case).
You should change it to check for (_WIN32) in all places first.
Also you might consider adding autodetection for headers when c++17 compiler is found...
Example:

#if ( __cplusplus > 201703L)
#if __has_include(<openssl/ssl.h>)
#ifndef CPPHTTPLIB_OPENSSL_SUPPORT
#define CPPHTTPLIB_OPENSSL_SUPPORT
#endif
#endif
#endif

params["name"] = "john" doesn't compile

Hello
Thanks for the lib!

I got an issue with "httplib::Params".

According to readme we can fill in post parameters like this :

httplib::Params params;
params["name"] = "john";

But it doesn't compile : error C2676: binary '[': 'httplib::Params' does not define this operator or a conversion to a type acceptable to the predefined operator

Params is a std::multimap and it seems it doesn't support this way to insert element.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.