2012-09-27 18:05:36 -07:00
cpp-httplib
===========
2012-09-21 19:38:33 -07:00
2019-09-19 21:43:02 -07:00
[![ ](https://github.com/yhirose/cpp-httplib/workflows/test/badge.svg )](https://github.com/yhirose/cpp-httplib/actions)
2019-01-11 18:31:06 -07:00
2020-01-21 21:45:21 -07:00
A C++11 single-file header-only cross platform HTTP/HTTPS library.
2012-09-22 21:41:21 -07:00
2012-10-05 10:58:56 -07:00
It's extremely easy to setup. Just include **httplib.h** file in your code!
2020-08-27 11:06:57 -07:00
NOTE: This is a 'blocking' HTTP library. If you are looking for a 'non-blocking' library, this is not the one that you want.
2020-08-25 17:26:53 -07:00
2020-08-27 11:06:57 -07:00
Simple examples
---------------
2020-08-27 11:16:35 -07:00
#### Server
2020-08-27 11:06:57 -07:00
```c++
httplib::Server svr;
svr.Get("/hi", [](const httplib::Request & , httplib::Response & res) {
res.set_content("Hello World!", "text/plain");
});
svr.listen("0.0.0.0", 8080);
```
2020-08-27 11:16:35 -07:00
#### Client
2020-08-27 11:06:57 -07:00
```c++
httplib::Client cli("http://cpp-httplib-server.yhirose.repl.co");
auto res = cli.Get("/hi");
res->status; // 200
res->body; // "Hello World!"
```
2020-08-27 11:16:35 -07:00
### Try out the examples on Repl.it!
1. Run server at https://repl.it/@yhirose/cpp-httplib-server
2. Run client at https://repl.it/@yhirose/cpp-httplib-client
2020-08-27 11:06:57 -07:00
Server
------
2012-09-25 19:21:42 -07:00
2015-02-06 20:43:06 -07:00
```c++
#include <httplib.h>
2012-09-25 19:21:42 -07:00
2015-02-06 20:43:06 -07:00
int main(void)
{
2020-02-14 19:49:09 -07:00
using namespace httplib;
2012-10-05 10:58:56 -07:00
2020-02-14 19:49:09 -07:00
Server svr;
2012-09-27 18:05:36 -07:00
2020-02-14 19:49:09 -07:00
svr.Get("/hi", [](const Request& req, Response& res) {
res.set_content("Hello World!", "text/plain");
});
2012-09-27 18:05:36 -07:00
2020-02-14 19:49:09 -07:00
svr.Get(R"(/numbers/(\d+))", [& ](const Request& req, Response& res ) {
auto numbers = req.matches[1];
res.set_content(numbers, "text/plain");
});
2015-02-06 20:43:06 -07:00
2020-03-15 09:05:12 -07:00
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");
});
2020-02-14 19:49:09 -07:00
svr.Get("/stop", [& ](const Request& req, Response& res ) {
svr.stop();
});
2019-07-23 05:05:51 -07:00
2020-02-14 19:49:09 -07:00
svr.listen("localhost", 1234);
2015-02-06 20:43:06 -07:00
}
```
2012-09-25 19:21:42 -07:00
2018-05-06 18:16:35 -07:00
`Post` , `Put` , `Delete` and `Options` methods are also supported.
2018-11-07 19:46:53 -07:00
### Bind a socket to multiple interfaces and any available port
```cpp
2018-11-09 21:30:58 -07:00
int port = svr.bind_to_any_port("0.0.0.0");
2018-11-07 19:46:53 -07:00
svr.listen_after_bind();
```
2017-11-25 09:59:28 -07:00
### Static File Server
```cpp
2020-01-31 18:40:33 -07:00
// Mount / to ./www directory
2020-02-01 07:18:24 -07:00
auto ret = svr.set_mount_point("/", "./www");
2020-01-06 15:13:31 -07:00
if (!ret) {
// The specified base directory doesn't exist...
}
2019-12-26 17:48:57 -07:00
2020-01-06 15:13:31 -07:00
// Mount /public to ./www directory
2020-02-01 07:18:24 -07:00
ret = svr.set_mount_point("/public", "./www");
2019-11-29 21:32:59 -07:00
2020-01-06 15:13:31 -07:00
// Mount /public to ./www1 and ./www2 directories
2020-02-01 07:18:24 -07:00
ret = svr.set_mount_point("/public", "./www1"); // 1st order to search
ret = svr.set_mount_point("/public", "./www2"); // 2nd order to search
2020-01-31 18:40:33 -07:00
// Remove mount /
ret = svr.remove_mount_point("/");
2020-02-01 07:24:42 -07:00
// Remove mount /public
ret = svr.remove_mount_point("/public");
2019-11-29 21:32:59 -07:00
```
```cpp
2020-01-06 15:13:31 -07:00
// 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");
2017-11-25 09:59:28 -07:00
```
2019-12-26 17:48:57 -07:00
The followings are built-in mappings:
2020-01-31 18:40:33 -07:00
| Extension | MIME Type |
| :-------- | :--------------------- |
| txt | text/plain |
| html, htm | text/html |
| css | text/css |
| jpeg, jpg | image/jpg |
| png | image/png |
| gif | image/gif |
| svg | image/svg+xml |
| ico | image/x-icon |
| json | application/json |
| pdf | application/pdf |
| js | application/javascript |
| wasm | application/wasm |
| xml | application/xml |
| xhtml | application/xhtml+xml |
NOTE: These the static file server methods are not thread safe.
2019-12-26 17:48:57 -07:00
2017-11-25 09:59:28 -07:00
### Logging
```cpp
svr.set_logger([](const auto& req, const auto& res) {
2020-02-14 19:49:09 -07:00
your_logger(req, res);
2017-11-25 09:59:28 -07:00
});
```
2020-02-10 22:26:15 -07:00
### Error handler
2017-11-25 09:59:28 -07:00
```cpp
svr.set_error_handler([](const auto& req, auto& res) {
2020-02-14 19:49:09 -07:00
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");
2017-11-25 09:59:28 -07:00
});
```
2017-12-12 20:22:10 -07:00
### 'multipart/form-data' POST data
2017-12-05 17:30:13 -07:00
```cpp
2018-05-06 18:16:35 -07:00
svr.Post("/multipart", [& ](const auto& req, auto& res ) {
2020-02-14 19:49:09 -07:00
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;
2019-08-01 06:08:40 -07:00
});
```
2019-11-29 21:32:59 -07:00
### Receive content with Content receiver
```cpp
svr.Post("/content_receiver",
[& ](const Request &req, Response &res, const ContentReader &content_reader ) {
2019-11-29 05:18:35 -07:00
if (req.is_multipart_form_data()) {
2019-12-12 20:44:54 -07:00
MultipartFormDataItems files;
2019-11-29 05:18:35 -07:00
content_reader(
2019-12-12 20:44:54 -07:00
[& ](const MultipartFormData &file ) {
files.push_back(file);
2019-12-02 05:11:12 -07:00
return true;
},
2019-12-12 20:44:54 -07:00
[& ](const char *data, size_t data_length ) {
files.back().content.append(data, data_length);
2019-11-29 05:18:35 -07:00
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");
}
2019-11-29 21:32:59 -07:00
});
```
2020-02-14 19:49:09 -07:00
### Send content with Content provider
```cpp
2020-05-07 18:13:45 -07:00
const size_t DATA_CHUNK_SIZE = 4;
2020-02-14 19:49:09 -07:00
svr.Get("/stream", [& ](const Request &req, Response &res ) {
auto data = new std::string("abcdefg");
res.set_content_provider(
data->size(), // Content length
2020-08-11 09:11:05 -07:00
"text/plain", // Content type
2020-05-07 18:13:45 -07:00
[data ](size_t offset, size_t length, DataSink &sink ) {
2020-02-14 19:49:09 -07:00
const auto & d = *data;
sink.write(& d[offset], std::min(length, DATA_CHUNK_SIZE));
2020-05-13 18:48:14 -07:00
return true; // return 'false' if you want to cancel the process.
2020-02-14 19:49:09 -07:00
},
[data] { delete data; });
});
```
2020-08-16 17:49:54 -07:00
Without content length:
```cpp
svr.Get("/stream", [& ](const Request &req, Response &res ) {
res.set_content_provider(
"text/plain", // Content type
[& ](size_t offset, size_t length, DataSink &sink ) {
if (/* there is still data */) {
std::vector< char > data;
// prepare data...
sink.write(data.data(), data.size());
} else {
2020-08-25 03:38:16 -07:00
sink.done(); // No more data
2020-08-16 17:49:54 -07:00
}
return true; // return 'false' if you want to cancel the process.
});
});
```
2019-08-01 06:08:40 -07:00
### Chunked transfer encoding
```cpp
svr.Get("/chunked", [& ](const Request& req, Response& res ) {
res.set_chunked_content_provider(
2020-05-07 18:13:45 -07:00
[](size_t offset, DataSink & sink) {
2020-05-13 18:48:14 -07:00
sink.write("123", 3);
sink.write("345", 3);
sink.write("789", 3);
2020-08-16 17:49:54 -07:00
sink.done(); // No more data
2020-05-13 18:48:14 -07:00
return true; // return 'false' if you want to cancel the process.
2019-08-01 06:08:40 -07:00
}
);
});
```
2020-01-17 21:21:42 -07:00
2020-05-23 16:08:17 -07:00
### 'Expect: 100-continue' handler
As default, the server sends `100 Continue` response for `Expect: 100-continue` header.
```cpp
// Send a '417 Expectation Failed' response.
svr.set_expect_100_continue_handler([](const Request & req, Response & res) {
return 417;
});
```
```cpp
// Send a final status without reading the message body.
svr.set_expect_100_continue_handler([](const Request & req, Response & res) {
return res.status = 401;
});
```
### Keep-Alive connection
```cpp
svr.set_keep_alive_max_count(2); // Default is 5
```
### Timeout
```c++
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 request body
```c++
svr.set_payload_max_length(1024 * 1024 * 512); // 512MB
```
2020-01-17 21:21:42 -07:00
### Server-Sent Events
2020-07-25 08:24:06 -07:00
Please 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 ).
2019-08-01 06:08:40 -07:00
2019-12-02 07:50:52 -07:00
### Default thread pool support
2019-08-05 02:17:40 -07:00
2020-09-15 07:11:46 -07:00
`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` .
2019-08-05 02:17:40 -07:00
2020-09-15 07:11:46 -07:00
If you want to set the thread count at runtime, there is no convenient way... But here is how.
2019-08-05 02:17:40 -07:00
2020-09-15 07:11:46 -07:00
```cpp
svr.new_task_queue = [] { return new ThreadPool(12); };
```
2019-08-05 02:17:40 -07:00
### Override the default thread pool with yours
2020-09-15 07:11:46 -07:00
You can supply your own thread pool implementation according to your need.
2019-08-05 02:17:40 -07:00
```cpp
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);
};
```
2020-08-27 11:06:57 -07:00
Client
------
2012-10-03 22:18:18 -07:00
2015-02-06 20:43:06 -07:00
```c++
#include <httplib.h>
#include <iostream>
2012-10-03 22:18:18 -07:00
2015-02-06 20:43:06 -07:00
int main(void)
{
2020-02-14 19:49:09 -07:00
httplib::Client cli("localhost", 1234);
2012-10-03 22:18:18 -07:00
2020-08-08 17:50:24 -07:00
if (auto res = cli.Get("/hi")) {
if (res->status == 200) {
std::cout < < res- > body < < std::endl ;
}
} else {
auto err = res.error();
...
2020-02-14 19:49:09 -07:00
}
2015-02-06 20:43:06 -07:00
}
```
2012-10-03 22:18:18 -07:00
2020-07-29 19:59:26 -07:00
NOTE: Constructor with scheme-host-port string is now supported!
```c++
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");
```
2019-07-23 05:05:51 -07:00
### GET with HTTP headers
```c++
2020-02-14 19:49:09 -07:00
httplib::Headers headers = {
{ "Accept-Encoding", "gzip, deflate" }
};
auto res = cli.Get("/hi", headers);
2019-07-23 05:05:51 -07:00
```
2020-08-03 20:37:05 -07:00
or
```c++
cli.set_default_headers({
{ "Accept-Encoding", "gzip, deflate" }
});
auto res = cli.Get("/hi");
```
2019-07-23 05:05:51 -07:00
2017-12-05 17:30:13 -07:00
### POST
```c++
2018-05-06 18:16:35 -07:00
res = cli.Post("/post", "text", "text/plain");
res = cli.Post("/person", "name=john1& note=coder", "application/x-www-form-urlencoded");
2017-12-05 17:30:13 -07:00
```
### POST with parameters
```c++
2018-05-09 04:17:45 -07:00
httplib::Params params;
2018-05-18 13:43:08 -07:00
params.emplace("name", "john");
params.emplace("note", "coder");
auto res = cli.Post("/post", params);
```
or
```c++
httplib::Params params{
{ "name", "john" },
{ "note", "coder" }
};
2018-05-06 18:16:35 -07:00
auto res = cli.Post("/post", params);
```
2019-07-23 06:52:12 -07:00
### POST with Multipart Form Data
```c++
2020-02-14 19:49:09 -07:00
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);
2019-07-23 06:52:12 -07:00
```
2018-05-06 18:16:35 -07:00
### PUT
```c++
2018-05-09 04:17:45 -07:00
res = cli.Put("/resource/foo", "text", "text/plain");
2018-05-06 18:16:35 -07:00
```
### DELETE
```c++
res = cli.Delete("/resource/foo");
```
### OPTIONS
```c++
res = cli.Options("*");
res = cli.Options("/resource/foo");
2017-12-05 17:30:13 -07:00
```
2020-05-23 16:08:17 -07:00
### Timeout
2017-12-30 12:47:55 -07:00
```c++
2020-05-23 16:08:17 -07:00
cli.set_connection_timeout(0, 300000); // 300 milliseconds
cli.set_read_timeout(5, 0); // 5 seconds
cli.set_write_timeout(5, 0); // 5 seconds
2017-12-30 12:47:55 -07:00
```
2020-05-23 16:08:17 -07:00
2020-05-13 18:48:14 -07:00
### Receive content with Content receiver
2020-07-25 08:20:57 -07:00
```c++
std::string body;
auto res = cli.Get("/large-data",
[& ](const char *data, size_t data_length ) {
body.append(data, data_length);
return true;
});
```
2020-05-13 18:48:14 -07:00
```cpp
std::string body;
2020-07-25 08:24:06 -07:00
2020-05-13 18:48:14 -07:00
auto res = cli.Get(
"/stream", Headers(),
[& ](const Response &response ) {
EXPECT_EQ(200, response.status);
return true; // return 'false' if you want to cancel the request.
},
[& ](const char *data, size_t data_length ) {
body.append(data, data_length);
return true; // return 'false' if you want to cancel the request.
});
```
### Send content with Content provider
```cpp
std::string body = ...;
2020-07-25 08:24:06 -07:00
2020-05-13 18:48:14 -07:00
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");
```
2017-11-24 19:39:17 -07:00
### With Progress Callback
```cpp
httplib::Client client(url, port);
// prints: 0 / 000 bytes => 50% complete
2020-08-08 17:50:24 -07:00
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.
}
2017-11-24 19:39:17 -07:00
);
```
2017-12-12 20:22:10 -07:00
![progress ](https://user-images.githubusercontent.com/236374/33138910-495c4ecc-cf86-11e7-8693-2fc6d09615c4.gif )
2019-12-10 10:06:29 -07:00
### Authentication
2017-12-12 20:20:40 -07:00
2019-12-20 21:12:24 -07:00
```cpp
// Basic Authentication
cli.set_basic_auth("user", "pass");
// Digest Authentication
cli.set_digest_auth("user", "pass");
2020-07-31 15:54:53 -07:00
// Bearer Token Authentication
cli.set_bearer_token_auth("token");
2019-12-20 21:12:24 -07:00
```
NOTE: OpenSSL is required for Digest Authentication.
### Proxy server support
2019-12-10 11:14:23 -07:00
2017-12-12 20:20:40 -07:00
```cpp
2019-12-20 21:12:24 -07:00
cli.set_proxy("host", port);
// Basic Authentication
cli.set_proxy_basic_auth("user", "pass");
// Digest Authentication
cli.set_proxy_digest_auth("user", "pass");
2020-07-31 15:54:53 -07:00
// Bearer Token Authentication
cli.set_proxy_bearer_token_auth("pass");
2019-07-19 08:38:06 -07:00
```
2017-12-12 20:20:40 -07:00
2019-12-20 21:12:24 -07:00
NOTE: OpenSSL is required for Digest Authentication.
2019-07-19 08:38:06 -07:00
### Range
```cpp
httplib::Client cli("httpbin.org");
2017-12-12 20:20:40 -07:00
2019-07-19 08:38:06 -07:00
auto res = cli.Get("/range/32", {
2019-08-01 06:08:40 -07:00
httplib::make_range_header({{1, 10}}) // 'Range: bytes=1-10'
2019-07-19 08:38:06 -07:00
});
2017-12-12 20:20:40 -07:00
// res->status should be 206.
// res->body should be "bcdefghijk".
```
2019-08-01 06:08:40 -07:00
```cpp
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'
```
2019-08-31 15:01:45 -07:00
### Keep-Alive connection
```cpp
2020-06-16 14:46:23 -07:00
httplib::Client cli("localhost", 1234);
2019-08-31 15:01:45 -07:00
2020-06-16 14:46:23 -07:00
cli.Get("/hello"); // with "Connection: close"
2019-08-31 15:01:45 -07:00
2020-06-16 14:46:23 -07:00
cli.set_keep_alive(true);
cli.Get("/world");
2020-05-07 18:13:45 -07:00
2020-06-16 14:46:23 -07:00
cli.set_keep_alive(false);
cli.Get("/last-request"); // with "Connection: close"
2019-08-31 15:01:45 -07:00
```
2019-09-05 21:26:57 -07:00
### Redirect
```cpp
httplib::Client cli("yahoo.com");
2019-09-06 15:16:42 -07:00
2019-09-06 15:16:42 -07:00
auto res = cli.Get("/");
2019-09-06 15:16:42 -07:00
res->status; // 301
2019-12-13 04:56:00 -07:00
cli.set_follow_location(true);
2019-09-06 15:16:42 -07:00
res = cli.Get("/");
2019-09-06 15:16:42 -07:00
res->status; // 200
2019-09-05 21:26:57 -07:00
```
2019-12-15 15:55:08 -07:00
### Use a specitic network interface
2019-12-15 18:31:36 -07:00
NOTE: This feature is not available on Windows, yet.
2019-12-15 15:55:08 -07:00
```cpp
cli.set_interface("eth0"); // Interface name, IP address or host name
```
2018-06-14 09:25:55 -07:00
OpenSSL Support
---------------
2017-04-21 20:00:00 -07:00
2018-06-14 09:25:55 -07:00
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT` . `libssl` and `libcrypto` should be linked.
2017-04-21 20:00:00 -07:00
2019-12-26 18:16:07 -07:00
NOTE: cpp-httplib currently supports only version 1.1.1.
2019-11-27 20:53:06 -07:00
2017-04-21 20:00:00 -07:00
```c++
#define CPPHTTPLIB_OPENSSL_SUPPORT
2020-08-27 11:06:57 -07:00
httplib::SSLServer svr("./cert.pem", "./key.pem");
2017-04-21 20:00:00 -07:00
2020-08-27 11:06:57 -07:00
httplib::SSLClient cli("localhost", 1234); // or `httplib::Client cli("https://localhost:1234");`
2019-05-07 13:54:51 -07:00
cli.set_ca_cert_path("./ca-bundle.crt");
cli.enable_server_certificate_verification(true);
2017-04-21 20:00:00 -07:00
```
2020-07-25 17:53:38 -07:00
Compression
-----------
2017-12-28 18:47:52 -07:00
2020-07-25 17:53:38 -07:00
The server can applie compression to the following MIME type contents:
2017-12-28 18:47:52 -07:00
2020-07-25 17:53:38 -07:00
* all text types except text/event-stream
2017-12-28 18:47:52 -07:00
* image/svg+xml
* application/javascript
* application/json
* application/xml
* application/xhtml+xml
2020-07-25 17:53:38 -07:00
### 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.
2020-05-26 15:20:56 -07:00
### Compress request body on client
2019-12-12 21:09:59 -07:00
```c++
2019-12-13 04:56:00 -07:00
cli.set_compress(true);
2019-12-12 21:09:59 -07:00
res = cli.Post("/resource/foo", "...", "text/plain");
```
2020-05-26 15:20:56 -07:00
### Compress response body on client
```c++
cli.set_decompress(false);
2020-07-25 17:53:38 -07:00
res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}});
2020-05-26 15:20:56 -07:00
res->body; // Compressed data
```
2019-12-14 22:21:32 -07:00
Split httplib.h into .h and .cc
-------------------------------
```bash
> python3 split.py
> ls out
httplib.h httplib.cc
```
2019-01-15 06:24:17 -07:00
NOTE
----
2020-03-17 15:01:49 -07:00
### g++
2019-12-20 21:16:05 -07:00
g++ 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 ).
2019-01-15 06:24:17 -07:00
2020-03-17 15:01:49 -07:00
### Windows
Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32_LEAN_AND_MEAN` beforehand.
```cpp
#include <httplib.h>
#include <Windows.h>
```
```cpp
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <httplib.h>
```
2020-04-18 13:36:36 -07:00
Note: Cygwin on Windows is not supported.
2017-12-20 15:27:36 -07:00
License
-------
2020-01-26 16:33:51 -07:00
MIT license (© 2020 Yuji Hirose)
Special Thanks To
-----------------
2020-03-17 15:01:49 -07:00
[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!