mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
Compare commits
249 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 89740a808d | |||
| 2377a20e0b | |||
| 5e43680486 | |||
| 79df842cc5 | |||
| 48a4a5812e | |||
| a7a6df49a4 | |||
| e932c7e1ec | |||
| f94c3f1dc9 | |||
| 4d545cb932 | |||
| f5e19faae7 | |||
| 2e360f9dd6 | |||
| e5ca863de5 | |||
| 126f1d177e | |||
| b557ac9328 | |||
| f6db19959f | |||
| 6b4df41b30 | |||
| 0d81e20129 | |||
| 26cb83ed6c | |||
| b4f808da74 | |||
| 7fbb8bb3fd | |||
| db27812198 | |||
| 6fe6fd5dbe | |||
| 0ee9660f3d | |||
| a15d16a9de | |||
| dfebfd9033 | |||
| fcdaa24fc5 | |||
| 7f749740f7 | |||
| 7b4494748e | |||
| 7e1c107029 | |||
| db7ae0ec21 | |||
| b16905ec8b | |||
| 2f72845008 | |||
| 68aeb4a06a | |||
| 568fda62b4 | |||
| 04957c0f08 | |||
| 96e9ec0663 | |||
| c58fca5dba | |||
| d2fae4031c | |||
| de844e67ef | |||
| 6c0e021554 | |||
| e8d33a77e0 | |||
| aa630e3062 | |||
| 301a419c02 | |||
| fcbcbd53bd | |||
| 1bf616d653 | |||
| ba7c7dc4a3 | |||
| aa543240db | |||
| 5675cad407 | |||
| 079d3605ea | |||
| 2c6da365d9 | |||
| 38adeaf02c | |||
| b3814b2b80 | |||
| a444b612af | |||
| ed6d949f42 | |||
| d28cd3f937 | |||
| 8cc3e6c434 | |||
| 26fbc1b7c0 | |||
| 0dc653f45a | |||
| 7a58c0a430 | |||
| dabaa51a7d | |||
| a1cfc0f377 | |||
| eb4fcb5003 | |||
| ae43c96984 | |||
| 9c81693801 | |||
| 80202c9f62 | |||
| 094a6a614a | |||
| 39c7bba7b9 | |||
| f2476f21fc | |||
| c776454c84 | |||
| 82a5ac735f | |||
| 08bf806e92 | |||
| 9a41b16cbb | |||
| 10759f0a38 | |||
| 58b2814fda | |||
| 260422b7d7 | |||
| d2c7b447d5 | |||
| 72b20c08da | |||
| afd6d5f9dc | |||
| e5827ad16f | |||
| 5324b3d661 | |||
| 151ccba57e | |||
| 69a28d50f6 | |||
| 048f31109f | |||
| d064fb7ff2 | |||
| 3c2736bb2a | |||
| fd4e1b4112 | |||
| f6a2365ca5 | |||
| df1ff7510b | |||
| 379905bd34 | |||
| 66719ae3d4 | |||
| bc9251ea49 | |||
| a9e942d755 | |||
| e1785d6723 | |||
| b9539b8921 | |||
| 4c93b973ff | |||
| 033bc35723 | |||
| d910bfc303 | |||
| b69c0a1dcb | |||
| 5e37e38398 | |||
| 295e4d58aa | |||
| 448de6a9c6 | |||
| 6f58dc728f | |||
| 905f2d84f4 | |||
| 880f7fa62b | |||
| 8f3dbf7f21 | |||
| 924a557fa3 | |||
| d8da740597 | |||
| d45676b064 | |||
| 94d13e88a5 | |||
| 4f9d04cb8e | |||
| 9fb11986a5 | |||
| 55d04ee354 | |||
| a62a48a7b5 | |||
| c652919954 | |||
| 58753ba33c | |||
| 5706828d2c | |||
| e743b8cd57 | |||
| 9d57899352 | |||
| d03937e144 | |||
| 8fb37a449d | |||
| f0b1b5dbfd | |||
| 5f32c424c2 | |||
| f0683f2301 | |||
| 0d527e2b83 | |||
| bea3ebd7af | |||
| 380f725713 | |||
| a106bd314c | |||
| e4fd9f19ca | |||
| dfc01338eb | |||
| c4ebc31345 | |||
| d1abf96581 | |||
| 001b8a5529 | |||
| 7a3abd2768 | |||
| 4a52524f47 | |||
| 89e1e9b8fe | |||
| 98d16eb836 | |||
| bcf0c32245 | |||
| dcdb0d047b | |||
| 1f86e41d97 | |||
| 6d8302313c | |||
| 89440ec322 | |||
| 5edf455d72 | |||
| 5f49c13f95 | |||
| 760bccc3ad | |||
| a99e02aeb3 | |||
| 4aae1dcc42 | |||
| f23f9a06a9 | |||
| 46466b1e28 | |||
| 224119a60a | |||
| c02849e269 | |||
| 71979b1e88 | |||
| a62d1f79f4 | |||
| b14b7b0f8f | |||
| 9dbe0d855c | |||
| 2cef8df6ae | |||
| 94fc229c44 | |||
| a7052cba22 | |||
| c47c6b3910 | |||
| c946eb7699 | |||
| 1f99ad5d6e | |||
| 96e372bad2 | |||
| fc56f39d17 | |||
| 5844432a7b | |||
| b97420f363 | |||
| 81610ee080 | |||
| c7f8561472 | |||
| 47bc7456d2 | |||
| 4ab9270660 | |||
| d599a36c2a | |||
| 6f8f51496d | |||
| 0c293887d0 | |||
| 7e92ffec48 | |||
| 531708816a | |||
| bfec81998b | |||
| c9238434e1 | |||
| e2babf315c | |||
| c434d555bb | |||
| 359b06681b | |||
| a0fe91e84d | |||
| 80f040cf69 | |||
| 037b4fc789 | |||
| a782d1b609 | |||
| 6a03ddf912 | |||
| 1e82359329 | |||
| a4160e6ac1 | |||
| bdbfc6cfe7 | |||
| 22615f9682 | |||
| 64991a560a | |||
| d7bb402ca7 | |||
| d58deddbcc | |||
| 3629f87627 | |||
| 1b95bf8cc3 | |||
| e383b277a4 | |||
| bf541442ea | |||
| 2823a94fc1 | |||
| 5a13539e57 | |||
| 531f6ab84d | |||
| f4aaba6362 | |||
| c899462e75 | |||
| 9785cd47f2 | |||
| 579ff1a0a6 | |||
| 47312e6df9 | |||
| 66b81a1497 | |||
| 9d7b717504 | |||
| 3291bdad91 | |||
| e8799383f3 | |||
| 58f72bc8b6 | |||
| 4c58b91e40 | |||
| 06b3dee5aa | |||
| ba7f9fe4c3 | |||
| 8480850a3f | |||
| d15df874b3 | |||
| f40de531ab | |||
| 7231f6fe00 | |||
| cb11d9412e | |||
| 057a8a0fb9 | |||
| 2d30c59340 | |||
| 34651ef89b | |||
| 4f237af813 | |||
| eaffe68c1a | |||
| e0d327558d | |||
| dd20e4d418 | |||
| 7267b3f3e2 | |||
| 4c18ac2b18 | |||
| b5a1d52f36 | |||
| eaafa5d55c | |||
| 3d1ae3a3af | |||
| e4d3766ef2 | |||
| 6f663028e9 | |||
| 31cdadc4b1 | |||
| 849add5887 | |||
| fccb84f5e8 | |||
| 3541fe8330 | |||
| 63d0056826 | |||
| d1080087d3 | |||
| cb43980a70 | |||
| 2fdc41c323 | |||
| 74e9a0d17e | |||
| 30d73051ee | |||
| 6f5a3c97ec | |||
| bb537d93fe | |||
| bde3fd9f78 | |||
| 2aa35d5f53 | |||
| 0b2c506ca4 | |||
| 8098f88c90 | |||
| 49f21f74cb | |||
| cdc45c4601 | |||
| c1ca091b44 | |||
| 7f7d101440 |
@@ -0,0 +1,5 @@
|
||||
BasedOnStyle: LLVM
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
Cpp11BracedListStyle: true
|
||||
@@ -0,0 +1,17 @@
|
||||
name: test
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macOS-latest, ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: cd test && make
|
||||
@@ -5,8 +5,12 @@ example/client
|
||||
example/hello
|
||||
example/simplesvr
|
||||
example/benchmark
|
||||
example/redirect
|
||||
example/sse
|
||||
example/upload
|
||||
example/*.pem
|
||||
test/test
|
||||
test/test_proxy
|
||||
test/test.xcodeproj/xcuser*
|
||||
test/test.xcodeproj/*/xcuser*
|
||||
test/*.pem
|
||||
|
||||
+5
-3
@@ -1,12 +1,14 @@
|
||||
# Environment
|
||||
language: cpp
|
||||
os: osx
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
# Compiler selection
|
||||
compiler:
|
||||
- clang
|
||||
|
||||
# Build/test steps
|
||||
script:
|
||||
script:
|
||||
- cd ${TRAVIS_BUILD_DIR}/test
|
||||
- make all
|
||||
- make all
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ install(TARGETS ${PROJECT_NAME} EXPORT httplibConfig
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
|
||||
install(FILES httplib.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
|
||||
|
||||
install(EXPORT httplibConfig DESTINATION share/httplib/cmake)
|
||||
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
cpp-httplib
|
||||
===========
|
||||
|
||||
[](https://github.com/yhirose/cpp-httplib/actions)
|
||||
[](https://travis-ci.org/yhirose/cpp-httplib)
|
||||
[](https://ci.appveyor.com/project/yhirose/cpp-httplib)
|
||||
|
||||
A C++11 header-only HTTP library.
|
||||
A C++11 single-file header-only cross platform HTTP/HTTPS library.
|
||||
|
||||
It's extremely easy to setup. Just include **httplib.h** file in your code!
|
||||
|
||||
Inspired by [Sinatra](http://www.sinatrarb.com/) and [express](https://github.com/visionmedia/express).
|
||||
|
||||
Server Example
|
||||
--------------
|
||||
|
||||
@@ -31,6 +30,10 @@ int main(void)
|
||||
res.set_content(numbers, "text/plain");
|
||||
});
|
||||
|
||||
svr.Get("/stop", [&](const Request& req, Response& res) {
|
||||
svr.stop();
|
||||
});
|
||||
|
||||
svr.listen("localhost", 1234);
|
||||
}
|
||||
```
|
||||
@@ -44,24 +47,48 @@ int port = svr.bind_to_any_port("0.0.0.0");
|
||||
svr.listen_after_bind();
|
||||
```
|
||||
|
||||
### Method Chain
|
||||
|
||||
```cpp
|
||||
svr.Get("/get", [](const auto& req, auto& res) {
|
||||
res.set_content("get", "text/plain");
|
||||
})
|
||||
.Post("/post", [](const auto& req, auto& res) {
|
||||
res.set_content(req.body(), "text/plain");
|
||||
})
|
||||
.listen("localhost", 1234);
|
||||
```
|
||||
|
||||
### Static File Server
|
||||
|
||||
```cpp
|
||||
svr.set_base_dir("./www");
|
||||
auto ret = svr.set_base_dir("./www"); // This is same as `svr.set_base_dir("./www", "/")`;
|
||||
if (!ret) {
|
||||
// The specified base directory doesn't exist...
|
||||
}
|
||||
|
||||
// Mount /public to ./www directory
|
||||
ret = svr.set_base_dir("./www", "/public");
|
||||
|
||||
// Mount /public to ./www1 and ./www2 directories
|
||||
ret = svr.set_base_dir("./www1", "/public"); // 1st order to search
|
||||
ret = svr.set_base_dir("./www2", "/public"); // 2nd order to search
|
||||
```
|
||||
|
||||
```cpp
|
||||
// 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 |
|
||||
| :--------- | :--------------------- |
|
||||
| .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 |
|
||||
|
||||
### Logging
|
||||
|
||||
```cpp
|
||||
@@ -74,7 +101,7 @@ svr.set_logger([](const auto& req, const auto& res) {
|
||||
|
||||
```cpp
|
||||
svr.set_error_handler([](const auto& req, auto& res) {
|
||||
const char* fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
|
||||
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");
|
||||
@@ -86,12 +113,114 @@ svr.set_error_handler([](const auto& req, auto& res) {
|
||||
```cpp
|
||||
svr.Post("/multipart", [&](const auto& req, auto& res) {
|
||||
auto size = req.files.size();
|
||||
auto ret = req.has_file("name1"));
|
||||
auto ret = req.has_file("name1");
|
||||
const auto& file = req.get_file_value("name1");
|
||||
// file.filename;
|
||||
// file.content_type;
|
||||
auto body = req.body.substr(file.offset, file.length));
|
||||
})
|
||||
// file.content;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Send content with Content provider
|
||||
|
||||
```cpp
|
||||
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.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
|
||||
},
|
||||
[data] { delete data; });
|
||||
});
|
||||
```
|
||||
|
||||
### Receive content with Content receiver
|
||||
|
||||
```cpp
|
||||
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");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Chunked transfer encoding
|
||||
|
||||
```cpp
|
||||
svr.Get("/chunked", [&](const Request& req, Response& res) {
|
||||
res.set_chunked_content_provider(
|
||||
[](uint64_t offset, DataSink &sink) {
|
||||
sink.write("123", 3);
|
||||
sink.write("345", 3);
|
||||
sink.write("789", 3);
|
||||
sink.done();
|
||||
}
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### Server-Sent Events
|
||||
|
||||
Please check [here](https://github.com/yhirose/cpp-httplib/blob/master/example/sse.cc).
|
||||
|
||||
### Default thread pool support
|
||||
|
||||
|
||||
`ThreadPool` is used as a default task queue, and the default thread count is set to value from `std::thread::hardware_concurrency()`.
|
||||
|
||||
Set thread count to 8:
|
||||
|
||||
```cpp
|
||||
#define CPPHTTPLIB_THREAD_POOL_COUNT 8
|
||||
```
|
||||
|
||||
### Override the default thread pool with yours
|
||||
|
||||
```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);
|
||||
};
|
||||
```
|
||||
|
||||
Client Example
|
||||
@@ -114,6 +243,29 @@ int main(void)
|
||||
}
|
||||
```
|
||||
|
||||
### GET with HTTP headers
|
||||
|
||||
```c++
|
||||
httplib::Headers headers = {
|
||||
{ "Accept-Encoding", "gzip, deflate" }
|
||||
};
|
||||
auto res = cli.Get("/hi", headers);
|
||||
```
|
||||
|
||||
### GET with Content Receiver
|
||||
|
||||
```c++
|
||||
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());
|
||||
```
|
||||
|
||||
### POST
|
||||
|
||||
```c++
|
||||
@@ -141,6 +293,20 @@ httplib::Params params{
|
||||
auto res = cli.Post("/post", params);
|
||||
```
|
||||
|
||||
### POST with Multipart Form Data
|
||||
|
||||
```c++
|
||||
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
|
||||
|
||||
```c++
|
||||
@@ -163,7 +329,7 @@ res = cli.Options("/resource/foo");
|
||||
### Connection Timeout
|
||||
|
||||
```c++
|
||||
httplib::Client cli("localhost", 8080, 5); // timeouts in 5 seconds
|
||||
cli.set_timeout_sec(5); // timeouts in 5 seconds
|
||||
```
|
||||
### With Progress Callback
|
||||
|
||||
@@ -183,26 +349,97 @@ std::shared_ptr<httplib::Response> res =
|
||||
|
||||

|
||||
|
||||
This feature was contributed by [underscorediscovery](https://github.com/yhirose/cpp-httplib/pull/23).
|
||||
### Authentication
|
||||
|
||||
```cpp
|
||||
// Basic Authentication
|
||||
cli.set_basic_auth("user", "pass");
|
||||
|
||||
// Digest Authentication
|
||||
cli.set_digest_auth("user", "pass");
|
||||
```
|
||||
|
||||
NOTE: OpenSSL is required for Digest Authentication.
|
||||
|
||||
### Proxy server support
|
||||
|
||||
```cpp
|
||||
cli.set_proxy("host", port);
|
||||
|
||||
// Basic Authentication
|
||||
cli.set_proxy_basic_auth("user", "pass");
|
||||
|
||||
// Digest Authentication
|
||||
cli.set_proxy_digest_auth("user", "pass");
|
||||
```
|
||||
|
||||
NOTE: OpenSSL is required for Digest Authentication.
|
||||
|
||||
### Range
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("httpbin.org", 80);
|
||||
httplib::Client cli("httpbin.org");
|
||||
|
||||
// 'Range: bytes=1-10'
|
||||
httplib::Headers headers = { httplib::make_range_header(1, 10) };
|
||||
|
||||
auto res = cli.Get("/range/32", headers);
|
||||
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".
|
||||
```
|
||||
|
||||
```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'
|
||||
```
|
||||
|
||||
### Keep-Alive connection
|
||||
|
||||
```cpp
|
||||
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) {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Redirect
|
||||
|
||||
```cpp
|
||||
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 specitic network interface
|
||||
|
||||
NOTE: This feature is not available on Windows, yet.
|
||||
|
||||
```cpp
|
||||
cli.set_interface("eth0"); // Interface name, IP address or host name
|
||||
```
|
||||
|
||||
OpenSSL Support
|
||||
---------------
|
||||
|
||||
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).
|
||||
|
||||
```c++
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
|
||||
@@ -227,12 +464,71 @@ The server applies gzip compression to the following MIME type contents:
|
||||
* application/xml
|
||||
* application/xhtml+xml
|
||||
|
||||
### Compress content on client
|
||||
|
||||
```c++
|
||||
cli.set_compress(true);
|
||||
res = cli.Post("/resource/foo", "...", "text/plain");
|
||||
```
|
||||
|
||||
Split httplib.h into .h and .cc
|
||||
-------------------------------
|
||||
|
||||
```bash
|
||||
> python3 split.py
|
||||
> ls out
|
||||
httplib.h httplib.cc
|
||||
```
|
||||
|
||||
NOTE
|
||||
----
|
||||
|
||||
g++ 4.8 cannot build this library since `<regex>` in g++4.8 is [broken](https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions).
|
||||
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).
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
MIT license (© 2019 Yuji Hirose)
|
||||
MIT license (© 2020 Yuji Hirose)
|
||||
|
||||
Special Thanks To
|
||||
-----------------
|
||||
|
||||
The following folks made great contributions to polish this library to totally another level from a simple toy!
|
||||
|
||||
* [Zefz](https://github.com/Zefz)
|
||||
* [PixlRainbow](https://github.com/PixlRainbow)
|
||||
* [sgraham](https://github.com/sgraham)
|
||||
* [mrexodia](https://github.com/mrexodia)
|
||||
* [hyperxor](https://github.com/hyperxor)
|
||||
* [omaralvarez](https://github.com/omaralvarez)
|
||||
* [vvanelslande](https://github.com/vvanelslande)
|
||||
* [underscorediscovery](https://github.com/underscorediscovery)
|
||||
* [sux2mfgj](https://github.com/sux2mfgj)
|
||||
* [matvore](https://github.com/matvore)
|
||||
* [intmain-io](https://github.com/intmain)
|
||||
* [davidgfnet](https://github.com/davidgfnet)
|
||||
* [crtxcr](https://github.com/crtxcr)
|
||||
* [const-volatile](https://github.com/const)
|
||||
* [aguadoenzo](https://github.com/aguadoenzo)
|
||||
* [TheMaverickProgrammer](https://github.com/TheMaverickProgrammer)
|
||||
* [vdudouyt](https://github.com/vdudouyt)
|
||||
* [stupedama](https://github.com/stupedama)
|
||||
* [rockwotj](https://github.com/rockwotj)
|
||||
* [marknelson](https://github.com/marknelson)
|
||||
* [jaspervandeven](https://github.com/jaspervandeven)
|
||||
* [hans-erickson](https://github.com/hans)
|
||||
* [ha11owed](https://github.com/ha11owed)
|
||||
* [gulrak](https://github.com/gulrak)
|
||||
* [dolphineye](https://github.com/dolphineye)
|
||||
* [danielzehe](https://github.com/danielzehe)
|
||||
* [batist73](https://github.com/batist73)
|
||||
* [barryam3](https://github.com/barryam3)
|
||||
* [adikabintang](https://github.com/adikabintang)
|
||||
* [aaronalbers](https://github.com/aaronalbers)
|
||||
* [Whitetigerswt](https://github.com/Whitetigerswt)
|
||||
* [TangHuaiZhe](https://github.com/TangHuaiZhe)
|
||||
* [Sil3ntStorm](https://github.com/Sil3ntStorm)
|
||||
* [MannyClicks](https://github.com/MannyClicks)
|
||||
* [DraTeots](https://github.com/DraTeots)
|
||||
* [BastienDurel](https://github.com/BastienDurel)
|
||||
* [vitalyster](https://github.com/vitalyster)
|
||||
|
||||
+20
-10
@@ -1,29 +1,39 @@
|
||||
|
||||
CC = clang++
|
||||
CFLAGS = -std=c++14 -I.. -Wall -Wextra -lpthread
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib -lssl -lcrypto
|
||||
#CXX = clang++
|
||||
CXXFLAGS = -std=c++14 -I.. -Wall -Wextra -pthread
|
||||
OPENSSL_DIR = /usr/local/opt/openssl
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||
|
||||
all: server client hello simplesvr benchmark
|
||||
all: server client hello simplesvr upload redirect sse benchmark
|
||||
|
||||
server : server.cc ../httplib.h Makefile
|
||||
$(CC) -o server $(CFLAGS) server.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
$(CXX) -o server $(CXXFLAGS) server.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
|
||||
client : client.cc ../httplib.h Makefile
|
||||
$(CC) -o client $(CFLAGS) client.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
$(CXX) -o client $(CXXFLAGS) client.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
|
||||
hello : hello.cc ../httplib.h Makefile
|
||||
$(CC) -o hello $(CFLAGS) hello.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
$(CXX) -o hello $(CXXFLAGS) hello.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
|
||||
simplesvr : simplesvr.cc ../httplib.h Makefile
|
||||
$(CC) -o simplesvr $(CFLAGS) simplesvr.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
$(CXX) -o simplesvr $(CXXFLAGS) simplesvr.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
|
||||
upload : upload.cc ../httplib.h Makefile
|
||||
$(CXX) -o upload $(CXXFLAGS) upload.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
|
||||
redirect : redirect.cc ../httplib.h Makefile
|
||||
$(CXX) -o redirect $(CXXFLAGS) redirect.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
|
||||
sse : sse.cc ../httplib.h Makefile
|
||||
$(CXX) -o sse $(CXXFLAGS) sse.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
|
||||
benchmark : benchmark.cc ../httplib.h Makefile
|
||||
$(CC) -o benchmark $(CFLAGS) benchmark.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
$(CXX) -o benchmark $(CXXFLAGS) benchmark.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
|
||||
pem:
|
||||
openssl genrsa 2048 > key.pem
|
||||
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
||||
|
||||
clean:
|
||||
rm server client hello simplesvr *.pem
|
||||
rm server client hello simplesvr upload redirect sse benchmark *.pem
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
//
|
||||
// client.cc
|
||||
//
|
||||
// Copyright (c) 2012 Yuji Hirose. All rights reserved.
|
||||
// The Boost Software License 1.0
|
||||
// Copyright (c) 2019 Yuji Hirose. All rights reserved.
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include <httplib.h>
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
//
|
||||
// hello.cc
|
||||
//
|
||||
// Copyright (c) 2012 Yuji Hirose. All rights reserved.
|
||||
// The Boost Software License 1.0
|
||||
// Copyright (c) 2019 Yuji Hirose. All rights reserved.
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include <httplib.h>
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// redirect.cc
|
||||
//
|
||||
// Copyright (c) 2019 Yuji Hirose. All rights reserved.
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include <httplib.h>
|
||||
|
||||
#define SERVER_CERT_FILE "./cert.pem"
|
||||
#define SERVER_PRIVATE_KEY_FILE "./key.pem"
|
||||
|
||||
using namespace httplib;
|
||||
|
||||
int main(void) {
|
||||
// HTTP server
|
||||
Server http;
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
SSLServer https(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
|
||||
#endif
|
||||
|
||||
http.Get("/test", [](const Request & /*req*/, Response &res) {
|
||||
res.set_content("Test\n", "text/plain");
|
||||
});
|
||||
|
||||
http.set_error_handler([](const Request & /*req*/, Response &res) {
|
||||
res.set_redirect("https://localhost:8081/");
|
||||
});
|
||||
|
||||
// HTTPS server
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
https.Get("/", [=](const Request & /*req*/, Response &res) {
|
||||
res.set_redirect("/hi");
|
||||
});
|
||||
|
||||
https.Get("/hi", [](const Request & /*req*/, Response &res) {
|
||||
res.set_content("Hello World!\n", "text/plain");
|
||||
});
|
||||
|
||||
https.Get("/stop", [&](const Request & /*req*/, Response & /*res*/) {
|
||||
https.stop();
|
||||
http.stop();
|
||||
});
|
||||
#endif
|
||||
|
||||
// Run servers
|
||||
auto httpThread = std::thread([&]() { http.listen("localhost", 8080); });
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
auto httpsThread = std::thread([&]() { https.listen("localhost", 8081); });
|
||||
#endif
|
||||
|
||||
httpThread.join();
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
httpsThread.join();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
//
|
||||
// sample.cc
|
||||
//
|
||||
// Copyright (c) 2012 Yuji Hirose. All rights reserved.
|
||||
// The Boost Software License 1.0
|
||||
// Copyright (c) 2019 Yuji Hirose. All rights reserved.
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include <chrono>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//
|
||||
// simplesvr.cc
|
||||
//
|
||||
// Copyright (c) 2013 Yuji Hirose. All rights reserved.
|
||||
// The Boost Software License 1.0
|
||||
// Copyright (c) 2019 Yuji Hirose. All rights reserved.
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include <cstdio>
|
||||
@@ -27,7 +27,7 @@ string dump_headers(const Headers &headers) {
|
||||
return s;
|
||||
}
|
||||
|
||||
string dump_multipart_files(const MultipartFiles &files) {
|
||||
string dump_multipart_files(const MultipartFormDataMap &files) {
|
||||
string s;
|
||||
char buf[BUFSIZ];
|
||||
|
||||
@@ -46,10 +46,7 @@ string dump_multipart_files(const MultipartFiles &files) {
|
||||
snprintf(buf, sizeof(buf), "content type: %s\n", file.content_type.c_str());
|
||||
s += buf;
|
||||
|
||||
snprintf(buf, sizeof(buf), "text offset: %lu\n", file.offset);
|
||||
s += buf;
|
||||
|
||||
snprintf(buf, sizeof(buf), "text length: %lu\n", file.length);
|
||||
snprintf(buf, sizeof(buf), "text length: %lu\n", file.content.size());
|
||||
s += buf;
|
||||
|
||||
s += "----------------\n";
|
||||
@@ -125,7 +122,10 @@ int main(int argc, const char **argv) {
|
||||
auto base_dir = "./";
|
||||
if (argc > 2) { base_dir = argv[2]; }
|
||||
|
||||
svr.set_base_dir(base_dir);
|
||||
if (!svr.set_base_dir(base_dir)) {
|
||||
cout << "The specified base directory doesn't exist...";
|
||||
return 1;
|
||||
}
|
||||
|
||||
cout << "The server started at port " << port << "...";
|
||||
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// sse.cc
|
||||
//
|
||||
// Copyright (c) 2020 Yuji Hirose. All rights reserved.
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <httplib.h>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
using namespace httplib;
|
||||
using namespace std;
|
||||
|
||||
class EventDispatcher {
|
||||
public:
|
||||
EventDispatcher() {
|
||||
id_ = 0;
|
||||
cid_ = -1;
|
||||
}
|
||||
|
||||
void wait_event(DataSink *sink) {
|
||||
unique_lock<mutex> lk(m_);
|
||||
int id = id_;
|
||||
cv_.wait(lk, [&] { return cid_ == id; });
|
||||
if (sink->is_writable()) { sink->write(message_.data(), message_.size()); }
|
||||
}
|
||||
|
||||
void send_event(const string &message) {
|
||||
lock_guard<mutex> lk(m_);
|
||||
cid_ = id_++;
|
||||
message_ = message;
|
||||
cv_.notify_all();
|
||||
}
|
||||
|
||||
private:
|
||||
mutex m_;
|
||||
condition_variable cv_;
|
||||
atomic_int id_;
|
||||
atomic_int cid_;
|
||||
string message_;
|
||||
};
|
||||
|
||||
const auto html = R"(
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>SSE demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const ev1 = new EventSource("event1");
|
||||
ev1.onmessage = function(e) {
|
||||
console.log('ev1', e.data);
|
||||
}
|
||||
const ev2 = new EventSource("event2");
|
||||
ev2.onmessage = function(e) {
|
||||
console.log('ev2', e.data);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
int main(void) {
|
||||
EventDispatcher ed;
|
||||
|
||||
Server svr;
|
||||
|
||||
svr.Get("/", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_content(html, "text/html");
|
||||
});
|
||||
|
||||
svr.Get("/event1", [&](const Request & /*req*/, Response &res) {
|
||||
cout << "connected to event1..." << endl;
|
||||
res.set_header("Content-Type", "text/event-stream");
|
||||
res.set_chunked_content_provider(
|
||||
[&](uint64_t /*offset*/, DataSink &sink) { ed.wait_event(&sink); });
|
||||
});
|
||||
|
||||
svr.Get("/event2", [&](const Request & /*req*/, Response &res) {
|
||||
cout << "connected to event2..." << endl;
|
||||
res.set_header("Content-Type", "text/event-stream");
|
||||
res.set_chunked_content_provider(
|
||||
[&](uint64_t /*offset*/, DataSink &sink) { ed.wait_event(&sink); });
|
||||
});
|
||||
|
||||
thread t([&] {
|
||||
int id = 0;
|
||||
while (true) {
|
||||
this_thread::sleep_for(chrono::seconds(1));
|
||||
cout << "send event: " << id << std::endl;
|
||||
std::stringstream ss;
|
||||
ss << "data: " << id << "\n\n";
|
||||
ed.send_event(ss.str());
|
||||
id++;
|
||||
}
|
||||
});
|
||||
|
||||
svr.listen("localhost", 1234);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// upload.cc
|
||||
//
|
||||
// Copyright (c) 2019 Yuji Hirose. All rights reserved.
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include <fstream>
|
||||
#include <httplib.h>
|
||||
#include <iostream>
|
||||
using namespace httplib;
|
||||
using namespace std;
|
||||
|
||||
const char *html = R"(
|
||||
<form id="formElem">
|
||||
<input type="file" name="image_file" accept="image/*">
|
||||
<input type="file" name="text_file" accept="text/*">
|
||||
<input type="submit">
|
||||
</form>
|
||||
<script>
|
||||
formElem.onsubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
let res = await fetch('/post', {
|
||||
method: 'POST',
|
||||
body: new FormData(formElem)
|
||||
});
|
||||
console.log(await res.text());
|
||||
};
|
||||
</script>
|
||||
)";
|
||||
|
||||
int main(void) {
|
||||
Server svr;
|
||||
|
||||
svr.Get("/", [](const Request & /*req*/, Response &res) {
|
||||
res.set_content(html, "text/html");
|
||||
});
|
||||
|
||||
svr.Post("/post", [](const Request &req, Response &res) {
|
||||
auto image_file = req.get_file_value("image_file");
|
||||
auto text_file = req.get_file_value("text_file");
|
||||
|
||||
cout << "image file length: " << image_file.content.length() << endl
|
||||
<< "image file name: " << image_file.filename << endl
|
||||
<< "text file length: " << text_file.content.length() << endl
|
||||
<< "text file name: " << text_file.filename << endl;
|
||||
|
||||
{
|
||||
ofstream ofs(image_file.filename, ios::binary);
|
||||
ofs << image_file.content;
|
||||
}
|
||||
{
|
||||
ofstream ofs(text_file.filename);
|
||||
ofs << text_file.content;
|
||||
}
|
||||
|
||||
res.set_content("done", "text/plain");
|
||||
});
|
||||
|
||||
svr.listen("localhost", 1234);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
border = '// ----------------------------------------------------------------------------'
|
||||
|
||||
PythonVersion = sys.version_info[0];
|
||||
|
||||
with open('httplib.h') as f:
|
||||
lines = f.readlines()
|
||||
inImplementation = False
|
||||
|
||||
if PythonVersion < 3:
|
||||
os.makedirs('out')
|
||||
else:
|
||||
os.makedirs('out', exist_ok=True)
|
||||
|
||||
with open('out/httplib.h', 'w') as fh:
|
||||
with open('out/httplib.cc', 'w') as fc:
|
||||
fc.write('#include "httplib.h"\n')
|
||||
fc.write('namespace httplib {\n')
|
||||
for line in lines:
|
||||
isBorderLine = border in line
|
||||
if isBorderLine:
|
||||
inImplementation = not inImplementation
|
||||
else:
|
||||
if inImplementation:
|
||||
fc.write(line.replace('inline ', ''))
|
||||
pass
|
||||
else:
|
||||
fh.write(line)
|
||||
pass
|
||||
fc.write('} // namespace httplib\n')
|
||||
+12
-7
@@ -1,16 +1,21 @@
|
||||
|
||||
CC = clang++
|
||||
#CC = g++
|
||||
|
||||
CFLAGS = -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib -lssl -lcrypto
|
||||
#CXX = clang++
|
||||
CXXFLAGS = -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits
|
||||
OPENSSL_DIR = /usr/local/opt/openssl
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||
|
||||
all : test
|
||||
./test
|
||||
|
||||
proxy : test_proxy
|
||||
./test_proxy
|
||||
|
||||
test : test.cc ../httplib.h Makefile cert.pem
|
||||
$(CC) -o test $(CFLAGS) test.cc gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) -lpthread
|
||||
$(CXX) -o test $(CXXFLAGS) test.cc gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) -pthread
|
||||
|
||||
test_proxy : test_proxy.cc ../httplib.h Makefile cert.pem
|
||||
$(CXX) -o test_proxy $(CXXFLAGS) test_proxy.cc gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) -pthread
|
||||
|
||||
cert.pem:
|
||||
openssl genrsa 2048 > key.pem
|
||||
@@ -22,4 +27,4 @@ cert.pem:
|
||||
#c_rehash .
|
||||
|
||||
clean:
|
||||
rm -f test *.pem *.0 *.1 *.srl
|
||||
rm -f test test_proxy pem *.0 *.1 *.srl
|
||||
|
||||
@@ -54,6 +54,11 @@
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4996)
|
||||
#endif
|
||||
|
||||
// Copyright 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
@@ -19544,4 +19549,8 @@ bool StaticAssertTypeEq() {
|
||||
|
||||
} // namespace testing
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning( pop )
|
||||
#endif
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_GTEST_H_
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
FROM centos:7
|
||||
|
||||
ARG auth="basic"
|
||||
ARG port="3128"
|
||||
|
||||
RUN yum install -y squid
|
||||
|
||||
COPY ./${auth}_squid.conf /etc/squid/squid.conf
|
||||
COPY ./${auth}_passwd /etc/squid/passwd
|
||||
|
||||
EXPOSE ${port}
|
||||
|
||||
CMD ["/usr/sbin/squid", "-N"]
|
||||
@@ -0,0 +1 @@
|
||||
hello:$apr1$O6S28OBL$8dr3ixl4Mohf97hgsYvLy/
|
||||
@@ -0,0 +1,81 @@
|
||||
#
|
||||
# Recommended minimum configuration:
|
||||
#
|
||||
|
||||
# Example rule allowing access from your local networks.
|
||||
# Adapt to list your (internal) IP networks from where browsing
|
||||
# should be allowed
|
||||
acl localnet src 0.0.0.1-0.255.255.255 # RFC 1122 "this" network (LAN)
|
||||
acl localnet src 10.0.0.0/8 # RFC 1918 local private network (LAN)
|
||||
acl localnet src 100.64.0.0/10 # RFC 6598 shared address space (CGN)
|
||||
acl localnet src 169.254.0.0/16 # RFC 3927 link-local (directly plugged) machines
|
||||
acl localnet src 172.16.0.0/12 # RFC 1918 local private network (LAN)
|
||||
acl localnet src 192.168.0.0/16 # RFC 1918 local private network (LAN)
|
||||
acl localnet src fc00::/7 # RFC 4193 local private network range
|
||||
acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines
|
||||
|
||||
acl SSL_ports port 443
|
||||
acl Safe_ports port 80 # http
|
||||
acl Safe_ports port 21 # ftp
|
||||
acl Safe_ports port 443 # https
|
||||
acl Safe_ports port 70 # gopher
|
||||
acl Safe_ports port 210 # wais
|
||||
acl Safe_ports port 1025-65535 # unregistered ports
|
||||
acl Safe_ports port 280 # http-mgmt
|
||||
acl Safe_ports port 488 # gss-http
|
||||
acl Safe_ports port 591 # filemaker
|
||||
acl Safe_ports port 777 # multiling http
|
||||
acl CONNECT method CONNECT
|
||||
|
||||
auth_param basic program /usr/lib64/squid/basic_ncsa_auth /etc/squid/passwd
|
||||
auth_param basic realm proxy
|
||||
acl authenticated proxy_auth REQUIRED
|
||||
http_access allow authenticated
|
||||
|
||||
#
|
||||
# Recommended minimum Access Permission configuration:
|
||||
#
|
||||
# Deny requests to certain unsafe ports
|
||||
http_access deny !Safe_ports
|
||||
|
||||
# Deny CONNECT to other than secure SSL ports
|
||||
http_access deny CONNECT !SSL_ports
|
||||
|
||||
# Only allow cachemgr access from localhost
|
||||
http_access allow localhost manager
|
||||
http_access deny manager
|
||||
|
||||
# We strongly recommend the following be uncommented to protect innocent
|
||||
# web applications running on the proxy server who think the only
|
||||
# one who can access services on "localhost" is a local user
|
||||
#http_access deny to_localhost
|
||||
|
||||
#
|
||||
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
|
||||
#
|
||||
|
||||
# Example rule allowing access from your local networks.
|
||||
# Adapt localnet in the ACL section to list your (internal) IP networks
|
||||
# from where browsing should be allowed
|
||||
http_access allow localnet
|
||||
http_access allow localhost
|
||||
|
||||
# And finally deny all other access to this proxy
|
||||
http_access deny all
|
||||
|
||||
# Squid normally listens to port 3128
|
||||
http_port 3128
|
||||
|
||||
# Uncomment and adjust the following to add a disk cache directory.
|
||||
#cache_dir ufs /var/spool/squid 100 16 256
|
||||
|
||||
# Leave coredumps in the first cache dir
|
||||
coredump_dir /var/spool/squid
|
||||
|
||||
#
|
||||
# Add any of your own refresh_pattern entries above these.
|
||||
#
|
||||
refresh_pattern ^ftp: 1440 20% 10080
|
||||
refresh_pattern ^gopher: 1440 0% 1440
|
||||
refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
|
||||
refresh_pattern . 0 20% 4320
|
||||
@@ -0,0 +1 @@
|
||||
hello:world
|
||||
@@ -0,0 +1,81 @@
|
||||
#
|
||||
# Recommended minimum configuration:
|
||||
#
|
||||
|
||||
# Example rule allowing access from your local networks.
|
||||
# Adapt to list your (internal) IP networks from where browsing
|
||||
# should be allowed
|
||||
acl localnet src 0.0.0.1-0.255.255.255 # RFC 1122 "this" network (LAN)
|
||||
acl localnet src 10.0.0.0/8 # RFC 1918 local private network (LAN)
|
||||
acl localnet src 100.64.0.0/10 # RFC 6598 shared address space (CGN)
|
||||
acl localnet src 169.254.0.0/16 # RFC 3927 link-local (directly plugged) machines
|
||||
acl localnet src 172.16.0.0/12 # RFC 1918 local private network (LAN)
|
||||
acl localnet src 192.168.0.0/16 # RFC 1918 local private network (LAN)
|
||||
acl localnet src fc00::/7 # RFC 4193 local private network range
|
||||
acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines
|
||||
|
||||
acl SSL_ports port 443
|
||||
acl Safe_ports port 80 # http
|
||||
acl Safe_ports port 21 # ftp
|
||||
acl Safe_ports port 443 # https
|
||||
acl Safe_ports port 70 # gopher
|
||||
acl Safe_ports port 210 # wais
|
||||
acl Safe_ports port 1025-65535 # unregistered ports
|
||||
acl Safe_ports port 280 # http-mgmt
|
||||
acl Safe_ports port 488 # gss-http
|
||||
acl Safe_ports port 591 # filemaker
|
||||
acl Safe_ports port 777 # multiling http
|
||||
acl CONNECT method CONNECT
|
||||
|
||||
auth_param digest program /usr/lib64/squid/digest_file_auth /etc/squid/passwd
|
||||
auth_param digest realm proxy
|
||||
acl authenticated proxy_auth REQUIRED
|
||||
http_access allow authenticated
|
||||
|
||||
#
|
||||
# Recommended minimum Access Permission configuration:
|
||||
#
|
||||
# Deny requests to certain unsafe ports
|
||||
http_access deny !Safe_ports
|
||||
|
||||
# Deny CONNECT to other than secure SSL ports
|
||||
http_access deny CONNECT !SSL_ports
|
||||
|
||||
# Only allow cachemgr access from localhost
|
||||
http_access allow localhost manager
|
||||
http_access deny manager
|
||||
|
||||
# We strongly recommend the following be uncommented to protect innocent
|
||||
# web applications running on the proxy server who think the only
|
||||
# one who can access services on "localhost" is a local user
|
||||
#http_access deny to_localhost
|
||||
|
||||
#
|
||||
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
|
||||
#
|
||||
|
||||
# Example rule allowing access from your local networks.
|
||||
# Adapt localnet in the ACL section to list your (internal) IP networks
|
||||
# from where browsing should be allowed
|
||||
http_access allow localnet
|
||||
http_access allow localhost
|
||||
|
||||
# And finally deny all other access to this proxy
|
||||
http_access deny all
|
||||
|
||||
# Squid normally listens to port 3128
|
||||
http_port 3129
|
||||
|
||||
# Uncomment and adjust the following to add a disk cache directory.
|
||||
#cache_dir ufs /var/spool/squid 100 16 256
|
||||
|
||||
# Leave coredumps in the first cache dir
|
||||
coredump_dir /var/spool/squid
|
||||
|
||||
#
|
||||
# Add any of your own refresh_pattern entries above these.
|
||||
#
|
||||
refresh_pattern ^ftp: 1440 20% 10080
|
||||
refresh_pattern ^gopher: 1440 0% 1440
|
||||
refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
|
||||
refresh_pattern . 0 20% 4320
|
||||
@@ -0,0 +1,22 @@
|
||||
version: '2'
|
||||
|
||||
services:
|
||||
squid_basic:
|
||||
image: squid_basic
|
||||
restart: always
|
||||
ports:
|
||||
- "3128:3128"
|
||||
build:
|
||||
context: ./
|
||||
args:
|
||||
auth: basic
|
||||
|
||||
squid_digest:
|
||||
image: squid_digest
|
||||
restart: always
|
||||
ports:
|
||||
- "3129:3129"
|
||||
build:
|
||||
context: ./
|
||||
args:
|
||||
auth: digest
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
docker-compose down --rmi all
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
docker-compose up -d
|
||||
+1106
-249
File diff suppressed because it is too large
Load Diff
+20
-4
@@ -34,7 +34,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
@@ -47,7 +47,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
@@ -69,15 +69,23 @@
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>C:\Program Files\OpenSSL-Win64\lib\VC;C:\Program Files\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>C:\Program Files\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>C:\Program Files\OpenSSL-Win64\lib;$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>C:\Program Files\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>C:\Program Files\OpenSSL-Win64\lib;$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
@@ -87,6 +95,8 @@
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
||||
<AdditionalUsingDirectories>
|
||||
</AdditionalUsingDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@@ -102,11 +112,13 @@
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
||||
<AdditionalUsingDirectories>
|
||||
</AdditionalUsingDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Ws2_32.lib;libssl.lib;libcrypto.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
@@ -119,6 +131,8 @@
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
||||
<AdditionalUsingDirectories>
|
||||
</AdditionalUsingDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@@ -138,13 +152,15 @@
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
||||
<AdditionalUsingDirectories>
|
||||
</AdditionalUsingDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Ws2_32.lib;libssl.lib;libcrypto.lib;libssl.lib;libcrypto.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
#include <future>
|
||||
#include <gtest/gtest.h>
|
||||
#include <httplib.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace httplib;
|
||||
|
||||
void ProxyTest(Client& cli, bool basic) {
|
||||
cli.set_proxy("localhost", basic ? 3128 : 3129);
|
||||
auto res = cli.Get("/get");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(407, res->status);
|
||||
}
|
||||
|
||||
TEST(ProxyTest, NoSSLBasic) {
|
||||
Client cli("httpbin.org");
|
||||
ProxyTest(cli, true);
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(ProxyTest, SSLBasic) {
|
||||
SSLClient cli("httpbin.org");
|
||||
ProxyTest(cli, true);
|
||||
}
|
||||
|
||||
TEST(ProxyTest, NoSSLDigest) {
|
||||
Client cli("httpbin.org");
|
||||
ProxyTest(cli, false);
|
||||
}
|
||||
|
||||
TEST(ProxyTest, SSLDigest) {
|
||||
SSLClient cli("httpbin.org");
|
||||
ProxyTest(cli, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void RedirectProxyText(Client& cli, const char *path, bool basic) {
|
||||
cli.set_proxy("localhost", basic ? 3128 : 3129);
|
||||
if (basic) {
|
||||
cli.set_proxy_basic_auth("hello", "world");
|
||||
} else {
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
cli.set_proxy_digest_auth("hello", "world");
|
||||
#endif
|
||||
}
|
||||
cli.set_follow_location(true);
|
||||
|
||||
auto res = cli.Get(path);
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(RedirectTest, HTTPBinNoSSLBasic) {
|
||||
Client cli("httpbin.org");
|
||||
RedirectProxyText(cli, "/redirect/2", true);
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(RedirectTest, HTTPBinNoSSLDigest) {
|
||||
Client cli("httpbin.org");
|
||||
RedirectProxyText(cli, "/redirect/2", false);
|
||||
}
|
||||
|
||||
TEST(RedirectTest, HTTPBinSSLBasic) {
|
||||
SSLClient cli("httpbin.org");
|
||||
RedirectProxyText(cli, "/redirect/2", true);
|
||||
}
|
||||
|
||||
TEST(RedirectTest, HTTPBinSSLDigest) {
|
||||
SSLClient cli("httpbin.org");
|
||||
RedirectProxyText(cli, "/redirect/2", false);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(RedirectTest, YouTubeNoSSLBasic) {
|
||||
Client cli("youtube.com");
|
||||
RedirectProxyText(cli, "/", true);
|
||||
}
|
||||
|
||||
TEST(RedirectTest, YouTubeNoSSLDigest) {
|
||||
Client cli("youtube.com");
|
||||
RedirectProxyText(cli, "/", false);
|
||||
}
|
||||
|
||||
TEST(RedirectTest, YouTubeSSLBasic) {
|
||||
SSLClient cli("youtube.com");
|
||||
RedirectProxyText(cli, "/", true);
|
||||
}
|
||||
|
||||
TEST(RedirectTest, YouTubeSSLDigest) {
|
||||
SSLClient cli("youtube.com");
|
||||
RedirectProxyText(cli, "/", false);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void BaseAuthTestFromHTTPWatch(Client& cli) {
|
||||
cli.set_proxy("localhost", 3128);
|
||||
cli.set_proxy_basic_auth("hello", "world");
|
||||
|
||||
{
|
||||
auto res = cli.Get("/basic-auth/hello/world");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(401, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
auto res =
|
||||
cli.Get("/basic-auth/hello/world",
|
||||
{make_basic_authentication_header("hello", "world")});
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", res->body);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
cli.set_basic_auth("hello", "world");
|
||||
auto res = cli.Get("/basic-auth/hello/world");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", res->body);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
cli.set_basic_auth("hello", "bad");
|
||||
auto res = cli.Get("/basic-auth/hello/world");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(401, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
cli.set_basic_auth("bad", "world");
|
||||
auto res = cli.Get("/basic-auth/hello/world");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(401, res->status);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BaseAuthTest, NoSSL) {
|
||||
Client cli("httpbin.org");
|
||||
BaseAuthTestFromHTTPWatch(cli);
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(BaseAuthTest, SSL) {
|
||||
SSLClient cli("httpbin.org");
|
||||
BaseAuthTestFromHTTPWatch(cli);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
void DigestAuthTestFromHTTPWatch(Client& cli) {
|
||||
cli.set_proxy("localhost", 3129);
|
||||
cli.set_proxy_digest_auth("hello", "world");
|
||||
|
||||
{
|
||||
auto res = cli.Get("/digest-auth/auth/hello/world");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(401, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> paths = {
|
||||
"/digest-auth/auth/hello/world/MD5",
|
||||
"/digest-auth/auth/hello/world/SHA-256",
|
||||
"/digest-auth/auth/hello/world/SHA-512",
|
||||
"/digest-auth/auth-int/hello/world/MD5",
|
||||
};
|
||||
|
||||
cli.set_digest_auth("hello", "world");
|
||||
for (auto path : paths) {
|
||||
auto res = cli.Get(path.c_str());
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", res->body);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
cli.set_digest_auth("hello", "bad");
|
||||
for (auto path : paths) {
|
||||
auto res = cli.Get(path.c_str());
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(400, res->status);
|
||||
}
|
||||
|
||||
cli.set_digest_auth("bad", "world");
|
||||
for (auto path : paths) {
|
||||
auto res = cli.Get(path.c_str());
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(400, res->status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DigestAuthTest, SSL) {
|
||||
SSLClient cli("httpbin.org");
|
||||
DigestAuthTestFromHTTPWatch(cli);
|
||||
}
|
||||
|
||||
TEST(DigestAuthTest, NoSSL) {
|
||||
Client cli("httpbin.org");
|
||||
DigestAuthTestFromHTTPWatch(cli);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void KeepAliveTest(Client& cli, bool basic) {
|
||||
cli.set_proxy("localhost", basic ? 3128 : 3129);
|
||||
if (basic) {
|
||||
cli.set_proxy_basic_auth("hello", "world");
|
||||
} else {
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
cli.set_proxy_digest_auth("hello", "world");
|
||||
#endif
|
||||
}
|
||||
|
||||
cli.set_keep_alive_max_count(4);
|
||||
cli.set_follow_location(true);
|
||||
cli.set_digest_auth("hello", "world");
|
||||
|
||||
std::vector<Request> requests;
|
||||
|
||||
Get(requests, "/get");
|
||||
Get(requests, "/redirect/2");
|
||||
|
||||
std::vector<std::string> paths = {
|
||||
"/digest-auth/auth/hello/world/MD5",
|
||||
"/digest-auth/auth/hello/world/SHA-256",
|
||||
"/digest-auth/auth/hello/world/SHA-512",
|
||||
"/digest-auth/auth-int/hello/world/MD5",
|
||||
};
|
||||
|
||||
for (auto path : paths) {
|
||||
Get(requests, path.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
int count = 100;
|
||||
while (count--) {
|
||||
Get(requests, "/get");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Response> responses;
|
||||
auto ret = cli.send(requests, responses);
|
||||
ASSERT_TRUE(ret == true);
|
||||
ASSERT_TRUE(requests.size() == responses.size());
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
{
|
||||
auto &res = responses[i++];
|
||||
EXPECT_EQ(200, res.status);
|
||||
}
|
||||
|
||||
{
|
||||
auto &res = responses[i++];
|
||||
EXPECT_EQ(200, res.status);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
int count = paths.size();
|
||||
while (count--) {
|
||||
auto &res = responses[i++];
|
||||
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", res.body);
|
||||
EXPECT_EQ(200, res.status);
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < responses.size(); i++) {
|
||||
auto &res = responses[i];
|
||||
EXPECT_EQ(200, res.status);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(KeepAliveTest, NoSSLWithBasic) {
|
||||
Client cli("httpbin.org");
|
||||
KeepAliveTest(cli, true);
|
||||
}
|
||||
|
||||
TEST(KeepAliveTest, SSLWithBasic) {
|
||||
SSLClient cli("httpbin.org");
|
||||
KeepAliveTest(cli, true);
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(KeepAliveTest, NoSSLWithDigest) {
|
||||
Client cli("httpbin.org");
|
||||
KeepAliveTest(cli, false);
|
||||
}
|
||||
|
||||
TEST(KeepAliveTest, SSLWithDigest) {
|
||||
SSLClient cli("httpbin.org");
|
||||
KeepAliveTest(cli, false);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
abcde
|
||||
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/dir/test.html">Test</a>
|
||||
<a href="/hi">hi</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
test.html
|
||||
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/dir/test.html">Test</a>
|
||||
<a href="/hi">hi</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
test.html
|
||||
Reference in New Issue
Block a user