commit | 5324b3d661caaed4c9962e3a0cb4e48bb60877bf | [log] [tgz] |
---|---|---|
author | yhirose <yuji.hirose.bug@gmail.com> | Thu Dec 12 22:44:54 2019 -0500 |
committer | yhirose <yuji.hirose.bug@gmail.com> | Thu Dec 12 22:48:09 2019 -0500 |
tree | eb6ba0fe1c804be6b47df83250f106636eb8c172 | |
parent | 151ccba57ec34f144f805664c8b7cf4d501b63d4 [diff] |
Improved multipart form data interface
A C++ single-file header-only cross platform HTTP/HTTPS library.
It's extremely easy to setup. Just include httplib.h file in your code!
#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("/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();
svr.set_base_dir("./www"); // This is same as `svr.set_base_dir("./www", "/")`;
svr.set_base_dir("./www", "/public");
svr.set_base_dir("./www1", "/public"); // 1st order svr.set_base_dir("./www2", "/public"); // 2nd order
svr.set_logger([](const auto& req, const auto& res) { your_logger(req, res); });
svr.set_error_handler([](const auto& req, auto& res) { const char* 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; });
const uint64_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 [data](uint64_t offset, uint64_t length, DataSink sink) { const auto &d = *data; sink(&d[offset], std::min(length, DATA_CHUNK_SIZE)); }, [data] { delete data; }); });
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"); } });
svr.Get("/chunked", [&](const Request& req, Response& res) { res.set_chunked_content_provider( [](uint64_t offset, DataSink sink, Done done) { sink("123", 3); sink("345", 3); sink("789", 3); done(); } ); });
Set thread count to 8:
#define CPPHTTPLIB_THREAD_POOL_COUNT 8
Disable the default thread pool:
#define CPPHTTPLIB_THREAD_POOL_COUNT 0
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); auto res = cli.Get("/hi"); if (res && res->status == 200) { std::cout << res->body << std::endl; } }
httplib::Headers headers = { { "Accept-Encoding", "gzip, deflate" } }; auto res = cli.Get("/hi", headers);
std::string body; auto res = cli.Get("/large-data", [&](const char *data, uint64_t data_length) { body.append(data, data_length); return true; }); assert(res->body.empty());
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");
httplib::Client cli("localhost", 8080, 5); // timeouts in 5 seconds
httplib::Client client(url, port); // prints: 0 / 000 bytes => 50% complete std::shared_ptr<httplib::Response> res = cli.Get("/", [](uint64_t len, uint64_t total) { printf("%lld / %lld bytes => %d%% complete\n", len, total, (int)((len/total)*100)); return true; // return 'false' if you want to cancel the request. } );
This feature was contributed by underscorediscovery.
NOTE: OpenSSL is required for Digest Authentication, since cpp-httplib uses message digest functions in OpenSSL.
httplib::Client cli("httplib.org"); cli.set_auth("user", "pass"); // Basic auto res = cli.Get("/basic-auth/user/pass"); // res->status should be 200 // res->body should be "{\n \"authenticated\": true, \n \"user\": \"user\"\n}\n". // Digest res = cli.Get("/digest-auth/auth/user/pass/SHA-256"); // res->status should be 200 // res->body should be "{\n \"authenticated\": true, \n \"user\": \"user\"\n}\n".
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'
cli.set_keep_alive_max_count(2); // Default is 5 std::vector<Request> requests; Get(requests, "/get-request1"); Get(requests, "/get-request2"); Post(requests, "/post-request1", "text", "text/plain"); Post(requests, "/post-request2", "text", "text/plain"); std::vector<Response> responses; if (cli.send(requests, responses)) { for (const auto& res: responses) { ... } }
httplib::Client cli("yahoo.com"); auto res = cli.Get("/"); res->status; // 301 cli.follow_location(true); res = cli.Get("/"); res->status; // 200
SSL support is available with CPPHTTPLIB_OPENSSL_SUPPORT
. libssl
and libcrypto
should be linked.
NOTE: cpp-httplib supports 1.1.1 (until 2023-09-11) and 1.0.2 (2019-12-31).
#define CPPHTTPLIB_OPENSSL_SUPPORT SSLServer svr("./cert.pem", "./key.pem"); SSLClient cli("localhost", 8080); cli.set_ca_cert_path("./ca-bundle.crt"); cli.enable_server_certificate_verification(true);
‘gzip’ compression is available with CPPHTTPLIB_ZLIB_SUPPORT
.
The server applies gzip compression to the following MIME type contents:
g++ 4.8 cannot build this library since <regex>
in g++4.8 is broken.
MIT license (© 2019 Yuji Hirose)