1cpp-httplib 2=========== 3 4[![](https://github.com/yhirose/cpp-httplib/workflows/test/badge.svg)](https://github.com/yhirose/cpp-httplib/actions) 5 6A C++11 single-file header-only cross platform HTTP/HTTPS library. 7 8It's extremely easy to setup. Just include the **httplib.h** file in your code! 9 10NOTE: 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. 11 12Simple examples 13--------------- 14 15#### Server 16 17```c++ 18#define CPPHTTPLIB_OPENSSL_SUPPORT 19#include "path/to/httplib.h" 20 21// HTTP 22httplib::Server svr; 23 24// HTTPS 25httplib::SSLServer svr; 26 27svr.Get("/hi", [](const httplib::Request &, httplib::Response &res) { 28 res.set_content("Hello World!", "text/plain"); 29}); 30 31svr.listen("0.0.0.0", 8080); 32``` 33 34#### Client 35 36```c++ 37#define CPPHTTPLIB_OPENSSL_SUPPORT 38#include "path/to/httplib.h" 39 40// HTTP 41httplib::Client cli("http://cpp-httplib-server.yhirose.repl.co"); 42 43// HTTPS 44httplib::Client cli("https://cpp-httplib-server.yhirose.repl.co"); 45 46auto res = cli.Get("/hi"); 47res->status; 48res->body; 49``` 50 51### Try out the examples on Repl.it! 52 531. Run server at https://repl.it/@yhirose/cpp-httplib-server 542. Run client at https://repl.it/@yhirose/cpp-httplib-client 55 56SSL Support 57----------- 58 59SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked. 60 61NOTE: cpp-httplib currently supports only version 1.1.1. 62 63```c++ 64#define CPPHTTPLIB_OPENSSL_SUPPORT 65#include "path/to/httplib.h" 66 67// Server 68httplib::SSLServer svr("./cert.pem", "./key.pem"); 69 70// Client 71httplib::Client cli("https://localhost:1234"); // scheme + host 72httplib::SSLClient cli("localhost:1234"); // host 73 74// Use your CA bundle 75cli.set_ca_cert_path("./ca-bundle.crt"); 76 77// Disable cert verification 78cli.enable_server_certificate_verification(false); 79``` 80 81Note: 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. 82 83Server 84------ 85 86```c++ 87#include <httplib.h> 88 89int main(void) 90{ 91 using namespace httplib; 92 93 Server svr; 94 95 svr.Get("/hi", [](const Request& req, Response& res) { 96 res.set_content("Hello World!", "text/plain"); 97 }); 98 99 svr.Get(R"(/numbers/(\d+))", [&](const Request& req, Response& res) { 100 auto numbers = req.matches[1]; 101 res.set_content(numbers, "text/plain"); 102 }); 103 104 svr.Get("/body-header-param", [](const Request& req, Response& res) { 105 if (req.has_header("Content-Length")) { 106 auto val = req.get_header_value("Content-Length"); 107 } 108 if (req.has_param("key")) { 109 auto val = req.get_param_value("key"); 110 } 111 res.set_content(req.body, "text/plain"); 112 }); 113 114 svr.Get("/stop", [&](const Request& req, Response& res) { 115 svr.stop(); 116 }); 117 118 svr.listen("localhost", 1234); 119} 120``` 121 122`Post`, `Put`, `Delete` and `Options` methods are also supported. 123 124### Bind a socket to multiple interfaces and any available port 125 126```cpp 127int port = svr.bind_to_any_port("0.0.0.0"); 128svr.listen_after_bind(); 129``` 130 131### Static File Server 132 133```cpp 134// Mount / to ./www directory 135auto ret = svr.set_mount_point("/", "./www"); 136if (!ret) { 137 // The specified base directory doesn't exist... 138} 139 140// Mount /public to ./www directory 141ret = svr.set_mount_point("/public", "./www"); 142 143// Mount /public to ./www1 and ./www2 directories 144ret = svr.set_mount_point("/public", "./www1"); // 1st order to search 145ret = svr.set_mount_point("/public", "./www2"); // 2nd order to search 146 147// Remove mount / 148ret = svr.remove_mount_point("/"); 149 150// Remove mount /public 151ret = svr.remove_mount_point("/public"); 152``` 153 154```cpp 155// User defined file extension and MIME type mappings 156svr.set_file_extension_and_mimetype_mapping("cc", "text/x-c"); 157svr.set_file_extension_and_mimetype_mapping("cpp", "text/x-c"); 158svr.set_file_extension_and_mimetype_mapping("hh", "text/x-h"); 159``` 160 161The followings are built-in mappings: 162 163| Extension | MIME Type | Extension | MIME Type | 164| :--------- | :-------------------------- | :--------- | :-------------------------- | 165| css | text/css | mpga | audio/mpeg | 166| csv | text/csv | weba | audio/webm | 167| txt | text/plain | wav | audio/wave | 168| vtt | text/vtt | otf | font/otf | 169| html, htm | text/html | ttf | font/ttf | 170| apng | image/apng | woff | font/woff | 171| avif | image/avif | woff2 | font/woff2 | 172| bmp | image/bmp | 7z | application/x-7z-compressed | 173| gif | image/gif | atom | application/atom+xml | 174| png | image/png | pdf | application/pdf | 175| svg | image/svg+xml | mjs, js | application/javascript | 176| webp | image/webp | json | application/json | 177| ico | image/x-icon | rss | application/rss+xml | 178| tif | image/tiff | tar | application/x-tar | 179| tiff | image/tiff | xhtml, xht | application/xhtml+xml | 180| jpeg, jpg | image/jpeg | xslt | application/xslt+xml | 181| mp4 | video/mp4 | xml | application/xml | 182| mpeg | video/mpeg | gz | application/gzip | 183| webm | video/webm | zip | application/zip | 184| mp3 | audio/mp3 | wasm | application/wasm | 185 186NOTE: These static file server methods are not thread-safe. 187 188### Logging 189 190```cpp 191svr.set_logger([](const auto& req, const auto& res) { 192 your_logger(req, res); 193}); 194``` 195 196### Error handler 197 198```cpp 199svr.set_error_handler([](const auto& req, auto& res) { 200 auto fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>"; 201 char buf[BUFSIZ]; 202 snprintf(buf, sizeof(buf), fmt, res.status); 203 res.set_content(buf, "text/html"); 204}); 205``` 206 207### Exception handler 208The exception handler gets called if a user routing handler throws an error. 209 210```cpp 211svr.set_exception_handler([](const auto& req, auto& res, std::exception &e) { 212 res.status = 500; 213 auto fmt = "<h1>Error 500</h1><p>%s</p>"; 214 char buf[BUFSIZ]; 215 snprintf(buf, sizeof(buf), fmt, e.what()); 216 res.set_content(buf, "text/html"); 217}); 218``` 219 220### Pre routing handler 221 222```cpp 223svr.set_pre_routing_handler([](const auto& req, auto& res) { 224 if (req.path == "/hello") { 225 res.set_content("world", "text/html"); 226 return Server::HandlerResponse::Handled; 227 } 228 return Server::HandlerResponse::Unhandled; 229}); 230``` 231 232### Post routing handler 233 234```cpp 235svr.set_post_routing_handler([](const auto& req, auto& res) { 236 res.set_header("ADDITIONAL_HEADER", "value"); 237}); 238``` 239 240### 'multipart/form-data' POST data 241 242```cpp 243svr.Post("/multipart", [&](const auto& req, auto& res) { 244 auto size = req.files.size(); 245 auto ret = req.has_file("name1"); 246 const auto& file = req.get_file_value("name1"); 247 // file.filename; 248 // file.content_type; 249 // file.content; 250}); 251``` 252 253### Receive content with a content receiver 254 255```cpp 256svr.Post("/content_receiver", 257 [&](const Request &req, Response &res, const ContentReader &content_reader) { 258 if (req.is_multipart_form_data()) { 259 // NOTE: `content_reader` is blocking until every form data field is read 260 MultipartFormDataItems files; 261 content_reader( 262 [&](const MultipartFormData &file) { 263 files.push_back(file); 264 return true; 265 }, 266 [&](const char *data, size_t data_length) { 267 files.back().content.append(data, data_length); 268 return true; 269 }); 270 } else { 271 std::string body; 272 content_reader([&](const char *data, size_t data_length) { 273 body.append(data, data_length); 274 return true; 275 }); 276 } 277 }); 278``` 279 280### Send content with the content provider 281 282```cpp 283const size_t DATA_CHUNK_SIZE = 4; 284 285svr.Get("/stream", [&](const Request &req, Response &res) { 286 auto data = new std::string("abcdefg"); 287 288 res.set_content_provider( 289 data->size(), // Content length 290 "text/plain", // Content type 291 [data](size_t offset, size_t length, DataSink &sink) { 292 const auto &d = *data; 293 sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE)); 294 return true; // return 'false' if you want to cancel the process. 295 }, 296 [data](bool success) { delete data; }); 297}); 298``` 299 300Without content length: 301 302```cpp 303svr.Get("/stream", [&](const Request &req, Response &res) { 304 res.set_content_provider( 305 "text/plain", // Content type 306 [&](size_t offset, size_t length, DataSink &sink) { 307 if (/* there is still data */) { 308 std::vector<char> data; 309 // prepare data... 310 sink.write(data.data(), data.size()); 311 } else { 312 sink.done(); // No more data 313 } 314 return true; // return 'false' if you want to cancel the process. 315 }); 316}); 317``` 318 319### Chunked transfer encoding 320 321```cpp 322svr.Get("/chunked", [&](const Request& req, Response& res) { 323 res.set_chunked_content_provider( 324 "text/plain", 325 [](size_t offset, DataSink &sink) { 326 sink.write("123", 3); 327 sink.write("345", 3); 328 sink.write("789", 3); 329 sink.done(); // No more data 330 return true; // return 'false' if you want to cancel the process. 331 } 332 ); 333}); 334``` 335 336### 'Expect: 100-continue' handler 337 338By default, the server sends a `100 Continue` response for an `Expect: 100-continue` header. 339 340```cpp 341// Send a '417 Expectation Failed' response. 342svr.set_expect_100_continue_handler([](const Request &req, Response &res) { 343 return 417; 344}); 345``` 346 347```cpp 348// Send a final status without reading the message body. 349svr.set_expect_100_continue_handler([](const Request &req, Response &res) { 350 return res.status = 401; 351}); 352``` 353 354### Keep-Alive connection 355 356```cpp 357svr.set_keep_alive_max_count(2); // Default is 5 358svr.set_keep_alive_timeout(10); // Default is 5 359``` 360 361### Timeout 362 363```c++ 364svr.set_read_timeout(5, 0); // 5 seconds 365svr.set_write_timeout(5, 0); // 5 seconds 366svr.set_idle_interval(0, 100000); // 100 milliseconds 367``` 368 369### Set maximum payload length for reading a request body 370 371```c++ 372svr.set_payload_max_length(1024 * 1024 * 512); // 512MB 373``` 374 375### Server-Sent Events 376 377Please see [Server example](https://github.com/yhirose/cpp-httplib/blob/master/example/ssesvr.cc) and [Client example](https://github.com/yhirose/cpp-httplib/blob/master/example/ssecli.cc). 378 379### Default thread pool support 380 381`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`. 382 383If you want to set the thread count at runtime, there is no convenient way... But here is how. 384 385```cpp 386svr.new_task_queue = [] { return new ThreadPool(12); }; 387``` 388 389### Override the default thread pool with yours 390 391You can supply your own thread pool implementation according to your need. 392 393```cpp 394class YourThreadPoolTaskQueue : public TaskQueue { 395public: 396 YourThreadPoolTaskQueue(size_t n) { 397 pool_.start_with_thread_count(n); 398 } 399 400 virtual void enqueue(std::function<void()> fn) override { 401 pool_.enqueue(fn); 402 } 403 404 virtual void shutdown() override { 405 pool_.shutdown_gracefully(); 406 } 407 408private: 409 YourThreadPool pool_; 410}; 411 412svr.new_task_queue = [] { 413 return new YourThreadPoolTaskQueue(12); 414}; 415``` 416 417Client 418------ 419 420```c++ 421#include <httplib.h> 422#include <iostream> 423 424int main(void) 425{ 426 httplib::Client cli("localhost", 1234); 427 428 if (auto res = cli.Get("/hi")) { 429 if (res->status == 200) { 430 std::cout << res->body << std::endl; 431 } 432 } else { 433 auto err = res.error(); 434 ... 435 } 436} 437``` 438 439NOTE: Constructor with scheme-host-port string is now supported! 440 441```c++ 442httplib::Client cli("localhost"); 443httplib::Client cli("localhost:8080"); 444httplib::Client cli("http://localhost"); 445httplib::Client cli("http://localhost:8080"); 446httplib::Client cli("https://localhost"); 447httplib::SSLClient cli("localhost"); 448``` 449 450### Error code 451 452Here is the list of errors from `Result::error()`. 453 454```c++ 455enum Error { 456 Success = 0, 457 Unknown, 458 Connection, 459 BindIPAddress, 460 Read, 461 Write, 462 ExceedRedirectCount, 463 Canceled, 464 SSLConnection, 465 SSLLoadingCerts, 466 SSLServerVerification, 467 UnsupportedMultipartBoundaryChars 468}; 469``` 470 471### GET with HTTP headers 472 473```c++ 474httplib::Headers headers = { 475 { "Accept-Encoding", "gzip, deflate" } 476}; 477auto res = cli.Get("/hi", headers); 478``` 479or 480```c++ 481cli.set_default_headers({ 482 { "Accept-Encoding", "gzip, deflate" } 483}); 484auto res = cli.Get("/hi"); 485``` 486 487### POST 488 489```c++ 490res = cli.Post("/post", "text", "text/plain"); 491res = cli.Post("/person", "name=john1¬e=coder", "application/x-www-form-urlencoded"); 492``` 493 494### POST with parameters 495 496```c++ 497httplib::Params params; 498params.emplace("name", "john"); 499params.emplace("note", "coder"); 500 501auto res = cli.Post("/post", params); 502``` 503 or 504 505```c++ 506httplib::Params params{ 507 { "name", "john" }, 508 { "note", "coder" } 509}; 510 511auto res = cli.Post("/post", params); 512``` 513 514### POST with Multipart Form Data 515 516```c++ 517httplib::MultipartFormDataItems items = { 518 { "text1", "text default", "", "" }, 519 { "text2", "aωb", "", "" }, 520 { "file1", "h\ne\n\nl\nl\no\n", "hello.txt", "text/plain" }, 521 { "file2", "{\n \"world\", true\n}\n", "world.json", "application/json" }, 522 { "file3", "", "", "application/octet-stream" }, 523}; 524 525auto res = cli.Post("/multipart", items); 526``` 527 528### PUT 529 530```c++ 531res = cli.Put("/resource/foo", "text", "text/plain"); 532``` 533 534### DELETE 535 536```c++ 537res = cli.Delete("/resource/foo"); 538``` 539 540### OPTIONS 541 542```c++ 543res = cli.Options("*"); 544res = cli.Options("/resource/foo"); 545``` 546 547### Timeout 548 549```c++ 550cli.set_connection_timeout(0, 300000); // 300 milliseconds 551cli.set_read_timeout(5, 0); // 5 seconds 552cli.set_write_timeout(5, 0); // 5 seconds 553``` 554 555### Receive content with a content receiver 556 557```c++ 558std::string body; 559 560auto res = cli.Get("/large-data", 561 [&](const char *data, size_t data_length) { 562 body.append(data, data_length); 563 return true; 564 }); 565``` 566 567```cpp 568std::string body; 569 570auto res = cli.Get( 571 "/stream", Headers(), 572 [&](const Response &response) { 573 EXPECT_EQ(200, response.status); 574 return true; // return 'false' if you want to cancel the request. 575 }, 576 [&](const char *data, size_t data_length) { 577 body.append(data, data_length); 578 return true; // return 'false' if you want to cancel the request. 579 }); 580``` 581 582### Send content with a content provider 583 584```cpp 585std::string body = ...; 586 587auto res = cli.Post( 588 "/stream", body.size(), 589 [](size_t offset, size_t length, DataSink &sink) { 590 sink.write(body.data() + offset, length); 591 return true; // return 'false' if you want to cancel the request. 592 }, 593 "text/plain"); 594``` 595 596### Chunked transfer encoding 597 598```cpp 599auto res = cli.Post( 600 "/stream", 601 [](size_t offset, DataSink &sink) { 602 sink.os << "chunked data 1"; 603 sink.os << "chunked data 2"; 604 sink.os << "chunked data 3"; 605 sink.done(); 606 return true; // return 'false' if you want to cancel the request. 607 }, 608 "text/plain"); 609``` 610 611### With Progress Callback 612 613```cpp 614httplib::Client client(url, port); 615 616// prints: 0 / 000 bytes => 50% complete 617auto res = cli.Get("/", [](uint64_t len, uint64_t total) { 618 printf("%lld / %lld bytes => %d%% complete\n", 619 len, total, 620 (int)(len*100/total)); 621 return true; // return 'false' if you want to cancel the request. 622} 623); 624``` 625 626![progress](https://user-images.githubusercontent.com/236374/33138910-495c4ecc-cf86-11e7-8693-2fc6d09615c4.gif) 627 628### Authentication 629 630```cpp 631// Basic Authentication 632cli.set_basic_auth("user", "pass"); 633 634// Digest Authentication 635cli.set_digest_auth("user", "pass"); 636 637// Bearer Token Authentication 638cli.set_bearer_token_auth("token"); 639``` 640 641NOTE: OpenSSL is required for Digest Authentication. 642 643### Proxy server support 644 645```cpp 646cli.set_proxy("host", port); 647 648// Basic Authentication 649cli.set_proxy_basic_auth("user", "pass"); 650 651// Digest Authentication 652cli.set_proxy_digest_auth("user", "pass"); 653 654// Bearer Token Authentication 655cli.set_proxy_bearer_token_auth("pass"); 656``` 657 658NOTE: OpenSSL is required for Digest Authentication. 659 660### Range 661 662```cpp 663httplib::Client cli("httpbin.org"); 664 665auto res = cli.Get("/range/32", { 666 httplib::make_range_header({{1, 10}}) // 'Range: bytes=1-10' 667}); 668// res->status should be 206. 669// res->body should be "bcdefghijk". 670``` 671 672```cpp 673httplib::make_range_header({{1, 10}, {20, -1}}) // 'Range: bytes=1-10, 20-' 674httplib::make_range_header({{100, 199}, {500, 599}}) // 'Range: bytes=100-199, 500-599' 675httplib::make_range_header({{0, 0}, {-1, 1}}) // 'Range: bytes=0-0, -1' 676``` 677 678### Keep-Alive connection 679 680```cpp 681httplib::Client cli("localhost", 1234); 682 683cli.Get("/hello"); // with "Connection: close" 684 685cli.set_keep_alive(true); 686cli.Get("/world"); 687 688cli.set_keep_alive(false); 689cli.Get("/last-request"); // with "Connection: close" 690``` 691 692### Redirect 693 694```cpp 695httplib::Client cli("yahoo.com"); 696 697auto res = cli.Get("/"); 698res->status; // 301 699 700cli.set_follow_location(true); 701res = cli.Get("/"); 702res->status; // 200 703``` 704 705### Use a specific network interface 706 707NOTE: This feature is not available on Windows, yet. 708 709```cpp 710cli.set_interface("eth0"); // Interface name, IP address or host name 711``` 712 713Compression 714----------- 715 716The server can apply compression to the following MIME type contents: 717 718 * all text types except text/event-stream 719 * image/svg+xml 720 * application/javascript 721 * application/json 722 * application/xml 723 * application/xhtml+xml 724 725### Zlib Support 726 727'gzip' compression is available with `CPPHTTPLIB_ZLIB_SUPPORT`. `libz` should be linked. 728 729### Brotli Support 730 731Brotli compression is available with `CPPHTTPLIB_BROTLI_SUPPORT`. Necessary libraries should be linked. 732Please see https://github.com/google/brotli for more detail. 733 734### Compress request body on client 735 736```c++ 737cli.set_compress(true); 738res = cli.Post("/resource/foo", "...", "text/plain"); 739``` 740 741### Compress response body on client 742 743```c++ 744cli.set_decompress(false); 745res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}}); 746res->body; // Compressed data 747``` 748 749Use `poll` instead of `select` 750------------------------------ 751 752`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`. 753 754 755Split httplib.h into .h and .cc 756------------------------------- 757 758```console 759$ ./split.py -h 760usage: split.py [-h] [-e EXTENSION] [-o OUT] 761 762This script splits httplib.h into .h and .cc parts. 763 764optional arguments: 765 -h, --help show this help message and exit 766 -e EXTENSION, --extension EXTENSION 767 extension of the implementation file (default: cc) 768 -o OUT, --out OUT where to write the files (default: out) 769 770$ ./split.py 771Wrote out/httplib.h and out/httplib.cc 772``` 773 774NOTE 775---- 776 777### g++ 778 779g++ 4.8 and below cannot build this library since `<regex>` in the versions are [broken](https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions). 780 781### Windows 782 783Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32_LEAN_AND_MEAN` beforehand. 784 785```cpp 786#include <httplib.h> 787#include <Windows.h> 788``` 789 790```cpp 791#define WIN32_LEAN_AND_MEAN 792#include <Windows.h> 793#include <httplib.h> 794``` 795 796Note: 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. 797 798Note: Windows 8 or lower and Cygwin on Windows are not supported. 799 800License 801------- 802 803MIT license (© 2021 Yuji Hirose) 804 805Special Thanks To 806----------------- 807 808[These folks](https://github.com/yhirose/cpp-httplib/graphs/contributors) made great contributions to polish this library to totally another level from a simple toy! 809