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&note=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