Compare commits

..

74 Commits

Author SHA1 Message Date
yhirose e2babf315c Fixed build error on Windows 2019-09-05 13:22:44 -04:00
yhirose c434d555bb Fixed problem caused by former changes 2019-09-03 17:19:58 -04:00
yhirose 359b06681b Fixed warnings 2019-09-03 17:16:16 -04:00
yhirose a0fe91e84d Fixed unit test problem 2019-09-03 17:16:07 -04:00
yhirose 80f040cf69 Fix #209 2019-09-03 17:10:04 -04:00
yhirose 037b4fc789 Fixed problem with keep-alive on client 2019-09-03 17:05:10 -04:00
yhirose a782d1b609 Added OpenSSL version check. (Currently commented out) 2019-08-31 20:15:50 -04:00
yhirose 6a03ddf912 Update README 2019-08-31 18:01:45 -04:00
yhirose 1e82359329 Keep-alive connection support on client (Fix #36) 2019-08-31 17:52:24 -04:00
yhirose a4160e6ac1 Fix #212 2019-08-25 17:19:20 -04:00
yhirose bdbfc6cfe7 Fix #210 2019-08-25 16:49:10 -04:00
yhirose 22615f9682 Fix #203 2019-08-07 06:34:24 -04:00
yhirose 64991a560a Fixed problems with Win32 build 2019-08-07 15:21:22 +09:00
yhirose d7bb402ca7 Format code 2019-08-06 18:12:05 +09:00
yhirose d58deddbcc Fixed #198 2019-08-06 18:10:41 +09:00
yhirose 3629f87627 Fixed thread pool problem. 2019-08-06 08:31:43 +09:00
yhirose 1b95bf8cc3 Updated README 2019-08-05 18:24:30 +09:00
yhirose e383b277a4 Updated README 2019-08-05 18:17:40 +09:00
yhirose bf541442ea Updated README 2019-08-05 10:51:27 +09:00
yhirose 2823a94fc1 Added 'resource_releaser' for content provider 2019-08-05 09:40:23 +09:00
yhirose 5a13539e57 Revert "Fixed unit test error on Windows"
This reverts commit 531f6ab84d.
2019-08-04 09:50:47 +09:00
yhirose 531f6ab84d Fixed unit test error on Windows 2019-08-03 20:43:27 -04:00
yhirose f4aaba6362 Fixed warnings on Windows 2019-08-03 20:43:15 -04:00
yhirose c899462e75 Added 'Content-Range' header for single range request 2019-08-03 22:12:24 +09:00
yhirose 9785cd47f2 Thread pool support 2019-08-03 16:39:39 +09:00
yhirose 579ff1a0a6 new_task_queue support 2019-08-03 13:15:05 +09:00
yhirose 47312e6df9 Added TaskQueue interface 2019-08-03 12:11:22 +09:00
yhirose 66b81a1497 Code cleanup 2019-08-03 10:47:48 +09:00
yhirose 9d7b717504 Range header support and redesign of content provider interface 2019-08-03 03:28:39 +09:00
yhirose 3291bdad91 Fixed a build error with example/redirect.cc 2019-08-03 03:28:38 +09:00
yhirose e8799383f3 Made unit tests stable 2019-07-30 01:51:53 +00:00
yhirose 58f72bc8b6 Code cleanup 2019-07-29 14:24:40 -04:00
yhirose 4c58b91e40 Added additonal status messages 2019-07-29 09:47:50 -04:00
yhirose 06b3dee5aa Code cleanup 2019-07-28 19:12:44 -04:00
yhirose ba7f9fe4c3 Added more unit tests 2019-07-28 19:11:58 -04:00
yhirose 8480850a3f Fixed build error 2019-07-28 19:09:24 -04:00
yhirose d15df874b3 Code cleanup 2019-07-28 07:52:57 -04:00
yhirose f40de531ab Fix #196 2019-07-26 14:39:09 -04:00
yhirose 7231f6fe00 Merge pull request #193 from omaralvarez/master
Fix small CMake error when looking for the library
2019-07-24 07:02:53 -04:00
yhirose cb11d9412e Fixed connection end problem with HTTP/1.0 client 2019-07-24 06:53:48 -04:00
Omar Alvarez 057a8a0fb9 Fix small CMake error when looking for the header 2019-07-24 10:50:52 +02:00
yhirose 2d30c59340 Update README 2019-07-24 01:22:06 -04:00
yhirose 34651ef89b Fix #121 2019-07-23 09:53:44 -04:00
yhirose 4f237af813 Update README 2019-07-23 08:20:01 -04:00
yhirose eaffe68c1a Fixed README 2019-07-23 08:11:41 -04:00
yhirose e0d327558d Updated README 2019-07-23 08:05:51 -04:00
yhirose dd20e4d418 Fixed build errors 2019-07-22 06:58:05 -04:00
yhirose 7267b3f3e2 Write error handling 2019-07-21 21:34:49 -04:00
yhirose 4c18ac2b18 Added locking_callback for OpenSSL versions prior to 1.1.0 2019-07-19 17:03:47 -04:00
yhirose b5a1d52f36 Test code cleanup 2019-07-19 11:47:08 -04:00
yhirose eaafa5d55c Fix #127 2019-07-19 11:38:06 -04:00
yhirose 3d1ae3a3af Code cleanup 2019-07-17 23:50:47 -04:00
yhirose e4d3766ef2 Updated documentation 2019-07-17 23:46:02 -04:00
yhirose 6f663028e9 Fix #139. Content receiver support 2019-07-17 22:00:18 -04:00
yhirose 31cdadc4b1 Added check code to compress and decompress 2019-07-17 19:00:35 -04:00
yhirose 849add5887 Code cleanup 2019-07-17 15:09:41 -04:00
yhirose fccb84f5e8 Refactoring for reading content 2019-07-17 08:56:49 -04:00
yhirose 3541fe8330 Use -pthread 2019-07-09 22:31:55 -04:00
yhirose 63d0056826 Merge pull request #185 from gaoyadianta/master
fix the example Makefile
2019-07-09 22:31:16 -04:00
libingjun d1080087d3 fix the example Makefile 2019-07-10 10:00:19 +08:00
yhirose cb43980a70 Merge pull request #184 from jaspervandeven/master
Made svr_sock_ and is_running_ variables atomic
2019-07-05 18:53:25 -04:00
Jasper van de Ven 2fdc41c323 Made svr_sock_ and is_running_ variables atomic 2019-07-05 11:03:23 +02:00
yhirose 74e9a0d17e Fix #182 2019-07-03 10:52:24 -04:00
yhirose 30d73051ee Update README.md 2019-07-02 08:52:50 -04:00
yhirose 6f5a3c97ec Update .gitignore 2019-06-30 08:02:22 -04:00
yhirose bb537d93fe Added CPPHTTPLIB_RECV_BUFSIZ 2019-06-29 20:39:18 -04:00
yhirose bde3fd9f78 Improved performance of read_content_without_length 2019-06-29 20:25:16 -04:00
yhirose 2aa35d5f53 Fix #179 2019-06-28 12:08:47 -04:00
yhirose 0b2c506ca4 Added redirect exampe 2019-06-27 21:49:09 -04:00
yhirose 8098f88c90 Corrected licence 2019-06-27 21:48:57 -04:00
yhirose 49f21f74cb Removed linux and gcc from .travis.yml 2019-06-26 15:24:03 -04:00
yhirose cdc45c4601 Changed to use CXX and CXXFLAGS in Makefile 2019-06-26 15:09:34 -04:00
yhirose c1ca091b44 Use gcc-4.9 in travis ci 2019-06-26 14:57:25 -04:00
yhirose 7f7d101440 Updated .travis.yml to support linux and gcc 2019-06-26 14:29:07 -04:00
14 changed files with 1893 additions and 649 deletions
+1
View File
@@ -5,6 +5,7 @@ example/client
example/hello
example/simplesvr
example/benchmark
example/redirect
example/*.pem
test/test
test/test.xcodeproj/xcuser*
+4 -3
View File
@@ -1,12 +1,13 @@
# Environment
language: cpp
os: osx
os:
- osx
# Compiler selection
compiler:
- clang
# Build/test steps
script:
script:
- cd ${TRAVIS_BUILD_DIR}/test
- make all
- make all
+1 -1
View File
@@ -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)
+155 -20
View File
@@ -4,12 +4,10 @@ cpp-httplib
[![Build Status](https://travis-ci.org/yhirose/cpp-httplib.svg?branch=master)](https://travis-ci.org/yhirose/cpp-httplib)
[![Bulid Status](https://ci.appveyor.com/api/projects/status/github/yhirose/cpp-httplib?branch=master&svg=true)](https://ci.appveyor.com/project/yhirose/cpp-httplib)
A C++11 header-only HTTP library.
A C++ single-file header-only cross platform HTTP/HTTPS library.
It's extremely easy to setup. Just include **httplib.h** file in your code!
Inspired by [Sinatra](http://www.sinatrarb.com/) and [express](https://github.com/visionmedia/express).
Server Example
--------------
@@ -31,6 +29,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,18 +46,6 @@ 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
@@ -94,6 +84,79 @@ svr.Post("/multipart", [&](const auto& req, auto& res) {
})
```
### Stream 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, Out out) {
const auto &d = *data;
out(&d[offset], std::min(length, DATA_CHUNK_SIZE));
},
[data] { delete data; });
});
```
### Chunked transfer encoding
```cpp
svr.Get("/chunked", [&](const Request& req, Response& res) {
res.set_chunked_content_provider(
[](uint64_t offset, Out out, Done done) {
out("123", 3);
out("345", 3);
out("789", 3);
done();
}
);
});
```
### Default thread pool supporet
Set thread count to 8:
```cpp
#define CPPHTTPLIB_THREAD_POOL_COUNT 8
```
Disable the default thread pool:
```cpp
#define CPPHTTPLIB_THREAD_POOL_COUNT 0
```
### 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 +177,28 @@ 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, uint64_t offset, uint64_t content_length) {
body.append(data, data_length);
});
assert(res->body.empty());
```
### POST
```c++
@@ -141,6 +226,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++
@@ -185,19 +284,55 @@ std::shared_ptr<httplib::Response> res =
This feature was contributed by [underscorediscovery](https://github.com/yhirose/cpp-httplib/pull/23).
### Basic Authentication
```cpp
httplib::Client cli("httplib.org");
auto res = cli.Get("/basic-auth/hello/world", {
httplib::make_basic_authentication_header("hello", "world")
});
// res->status should be 200
// res->body should be "{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n".
```
### 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) {
...
}
}
```
OpenSSL Support
---------------
+12 -9
View File
@@ -1,29 +1,32 @@
CC = clang++
CFLAGS = -std=c++14 -I.. -Wall -Wextra -lpthread
#CXX = clang++
CXXFLAGS = -std=c++14 -I.. -Wall -Wextra -pthread
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib -lssl -lcrypto
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
all: server client hello simplesvr benchmark
all: server client hello simplesvr redirect 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)
redirect : redirect.cc ../httplib.h Makefile
$(CXX) -o redirect $(CXXFLAGS) redirect.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 redirect *.pem
+2 -2
View File
@@ -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
View File
@@ -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>
+64
View File
@@ -0,0 +1,64 @@
//
// 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
View File
@@ -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>
+2 -2
View File
@@ -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>
+1264 -462
View File
File diff suppressed because it is too large Load Diff
+3 -5
View File
@@ -1,8 +1,6 @@
CC = clang++
#CC = g++
CFLAGS = -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits
#CXX = clang++
CXXFLAGS = -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
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
@@ -10,7 +8,7 @@ all : test
./test
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
cert.pem:
openssl genrsa 2048 > key.pem
+9
View File
@@ -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_
+372 -141
View File
@@ -95,28 +95,100 @@ TEST(GetHeaderValueTest, RegularValueInt) {
TEST(GetHeaderValueTest, Range) {
{
Headers headers = {make_range_header(1)};
Headers headers = {make_range_header({{1, -1}})};
auto val = detail::get_header_value(headers, "Range", 0, 0);
EXPECT_STREQ("bytes=1-", val);
}
{
Headers headers = {make_range_header(1, 10)};
Headers headers = {make_range_header({{-1, 1}})};
auto val = detail::get_header_value(headers, "Range", 0, 0);
EXPECT_STREQ("bytes=-1", val);
}
{
Headers headers = {make_range_header({{1, 10}})};
auto val = detail::get_header_value(headers, "Range", 0, 0);
EXPECT_STREQ("bytes=1-10", val);
}
{
Headers headers = {make_range_header(1, 10, 100)};
Headers headers = {make_range_header({{1, 10}, {100, -1}})};
auto val = detail::get_header_value(headers, "Range", 0, 0);
EXPECT_STREQ("bytes=1-10, 100-", val);
}
{
Headers headers = {make_range_header(1, 10, 100, 200)};
Headers headers = {make_range_header({{1, 10}, {100, 200}})};
auto val = detail::get_header_value(headers, "Range", 0, 0);
EXPECT_STREQ("bytes=1-10, 100-200", val);
}
{
Headers headers = {make_range_header({{0, 0}, {-1, 1}})};
auto val = detail::get_header_value(headers, "Range", 0, 0);
EXPECT_STREQ("bytes=0-0, -1", val);
}
}
TEST(ParseHeaderValueTest, Range) {
{
Ranges ranges;
auto ret = detail::parse_range_header("bytes=1-", ranges);
EXPECT_TRUE(ret);
EXPECT_EQ(1u, ranges.size());
EXPECT_EQ(1u, ranges[0].first);
EXPECT_EQ(-1, ranges[0].second);
}
{
Ranges ranges;
auto ret = detail::parse_range_header("bytes=-1", ranges);
EXPECT_TRUE(ret);
EXPECT_EQ(1u, ranges.size());
EXPECT_EQ(-1, ranges[0].first);
EXPECT_EQ(1u, ranges[0].second);
}
{
Ranges ranges;
auto ret = detail::parse_range_header("bytes=1-10", ranges);
EXPECT_TRUE(ret);
EXPECT_EQ(1u, ranges.size());
EXPECT_EQ(1u, ranges[0].first);
EXPECT_EQ(10u, ranges[0].second);
}
{
Ranges ranges;
auto ret = detail::parse_range_header("bytes=10-1", ranges);
EXPECT_FALSE(ret);
}
{
Ranges ranges;
auto ret = detail::parse_range_header("bytes=1-10, 100-", ranges);
EXPECT_TRUE(ret);
EXPECT_EQ(2u, ranges.size());
EXPECT_EQ(1u, ranges[0].first);
EXPECT_EQ(10u, ranges[0].second);
EXPECT_EQ(100u, ranges[1].first);
EXPECT_EQ(-1, ranges[1].second);
}
{
Ranges ranges;
auto ret =
detail::parse_range_header("bytes=1-10, 100-200, 300-400", ranges);
EXPECT_TRUE(ret);
EXPECT_EQ(3u, ranges.size());
EXPECT_EQ(1u, ranges[0].first);
EXPECT_EQ(10u, ranges[0].second);
EXPECT_EQ(100u, ranges[1].first);
EXPECT_EQ(200u, ranges[1].second);
EXPECT_EQ(300u, ranges[2].first);
EXPECT_EQ(400u, ranges[2].second);
}
}
TEST(ChunkedEncodingTest, FromHTTPWatch) {
@@ -142,6 +214,34 @@ TEST(ChunkedEncodingTest, FromHTTPWatch) {
EXPECT_EQ(out, res->body);
}
TEST(ChunkedEncodingTest, WithContentReceiver) {
auto host = "www.httpwatch.com";
auto sec = 2;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port, sec);
#else
auto port = 80;
httplib::Client cli(host, port, sec);
#endif
std::string body;
auto res =
cli.Get("/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137",
[&](const char *data, size_t data_length, uint64_t, uint64_t) {
body.append(data, data_length);
return true;
});
ASSERT_TRUE(res != nullptr);
std::string out;
httplib::detail::read_file("./image.jpg", out);
EXPECT_EQ(200, res->status);
EXPECT_EQ(out, body);
}
TEST(RangeTest, FromHTTPBin) {
auto host = "httpbin.org";
auto sec = 5;
@@ -163,7 +263,7 @@ TEST(RangeTest, FromHTTPBin) {
}
{
httplib::Headers headers = {httplib::make_range_header(1)};
httplib::Headers headers = {httplib::make_range_header({{1, -1}})};
auto res = cli.Get("/range/32", headers);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(res->body, "bcdefghijklmnopqrstuvwxyzabcdef");
@@ -171,12 +271,35 @@ TEST(RangeTest, FromHTTPBin) {
}
{
httplib::Headers headers = {httplib::make_range_header(1, 10)};
httplib::Headers headers = {httplib::make_range_header({{1, 10}})};
auto res = cli.Get("/range/32", headers);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(res->body, "bcdefghijk");
EXPECT_EQ(206, res->status);
}
{
httplib::Headers headers = {httplib::make_range_header({{0, 31}})};
auto res = cli.Get("/range/32", headers);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(res->body, "abcdefghijklmnopqrstuvwxyzabcdef");
EXPECT_EQ(200, res->status);
}
{
httplib::Headers headers = {httplib::make_range_header({{0, -1}})};
auto res = cli.Get("/range/32", headers);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(res->body, "abcdefghijklmnopqrstuvwxyzabcdef");
EXPECT_EQ(200, res->status);
}
{
httplib::Headers headers = {httplib::make_range_header({{0, 32}})};
auto res = cli.Get("/range/32", headers);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(416, res->status);
}
}
TEST(ConnectionErrorTest, InvalidHost) {
@@ -239,9 +362,7 @@ TEST(CancelTest, NoCancel) {
httplib::Client cli(host, port, sec);
#endif
httplib::Headers headers;
auto res =
cli.Get("/range/32", headers, [](uint64_t, uint64_t) { return true; });
auto res = cli.Get("/range/32", [](uint64_t, uint64_t) { return true; });
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(res->body, "abcdefghijklmnopqrstuvwxyzabcdef");
EXPECT_EQ(200, res->status);
@@ -259,9 +380,7 @@ TEST(CancelTest, WithCancelSmallPayload) {
httplib::Client cli(host, port, sec);
#endif
httplib::Headers headers;
auto res =
cli.Get("/range/32", headers, [](uint64_t, uint64_t) { return false; });
auto res = cli.Get("/range/32", [](uint64_t, uint64_t) { return false; });
ASSERT_TRUE(res == nullptr);
}
@@ -302,8 +421,9 @@ TEST(BaseAuthTest, FromHTTPWatch) {
}
{
httplib::Headers headers = {{"Authorization", "Basic aGVsbG86d29ybGQ="}};
auto res = cli.Get("/basic-auth/hello/world", headers);
auto res =
cli.Get("/basic-auth/hello/world",
{httplib::make_basic_authentication_header("hello", "world")});
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(res->body,
"{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n");
@@ -378,28 +498,54 @@ protected:
res.set_content(json, "appliation/json");
res.status = 200;
})
.Get("/streamedchunked",
.Get("/streamed-chunked",
[&](const Request & /*req*/, Response &res) {
res.streamcb = [](uint64_t offset) {
if (offset < 3) return "a";
if (offset < 6) return "b";
return "";
};
res.set_chunked_content_provider(
[](uint64_t /*offset*/, DataSink sink, Done done) {
sink("123", 3);
sink("456", 3);
sink("789", 3);
done();
});
})
.Get("/streamed",
[&](const Request & /*req*/, Response &res) {
res.set_header("Content-Length", "6");
res.streamcb = [](uint64_t offset) {
if (offset < 3) return "a";
if (offset < 6) return "b";
return "";
};
res.set_content_provider(
6, [](uint64_t offset, uint64_t /*length*/, DataSink sink) {
sink(offset < 3 ? "a" : "b", 1);
});
})
.Get("/streamed-with-range",
[&](const Request & /*req*/, Response &res) {
auto data = new std::string("abcdefg");
res.set_content_provider(
data->size(),
[data](uint64_t offset, uint64_t length, DataSink sink) {
size_t DATA_CHUNK_SIZE = 4;
const auto &d = *data;
auto out_len = std::min(static_cast<size_t>(length), DATA_CHUNK_SIZE);
sink(&d[offset], out_len);
},
[data] { delete data; });
})
.Get("/streamed-cancel",
[&](const Request & /*req*/, Response &res) {
res.set_content_provider(
size_t(-1),
[](uint64_t /*offset*/, uint64_t /*length*/, DataSink sink) {
std::string data = "data_chunk";
sink(data.data(), data.size());
});
})
.Get("/with-range",
[&](const Request & /*req*/, Response &res) {
res.set_content("abcdefg", "text/plain");
})
.Post("/chunked",
[&](const Request &req, Response & /*res*/) {
EXPECT_EQ(req.body, "dechunked post body");
})
.Post("/largechunked",
.Post("/large-chunked",
[&](const Request &req, Response & /*res*/) {
std::string expected(6 * 30 * 1024u, 'a');
EXPECT_EQ(req.body, expected);
@@ -555,7 +701,6 @@ TEST_F(ServerTest, GetMethod200) {
EXPECT_EQ(200, res->status);
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
EXPECT_EQ(1, res->get_header_value_count("Content-Type"));
EXPECT_EQ("close", res->get_header_value("Connection"));
EXPECT_EQ("Hello World!", res->body);
}
@@ -713,85 +858,29 @@ TEST_F(ServerTest, EmptyRequest) {
}
TEST_F(ServerTest, LongRequest) {
auto res =
cli_.Get("/TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/__ok__");
std::string request;
for (size_t i = 0; i < 545; i++) {
request += "/TooLongRequest";
}
request += "OK";
auto res = cli_.Get(request.c_str());
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(404, res->status);
}
TEST_F(ServerTest, TooLongRequest) {
auto res =
cli_.Get("/TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/TooLongRequest/"
"TooLongRequest/TooLongRequest/TooLongRequest/__ng___");
std::string request;
for (size_t i = 0; i < 545; i++) {
request += "/TooLongRequest";
}
request += "_NG";
auto res = cli_.Get(request.c_str());
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(404, res->status);
EXPECT_EQ(414, res->status);
}
TEST_F(ServerTest, LongHeader) {
@@ -938,44 +1027,17 @@ TEST_F(ServerTest, EndWithPercentCharacterInQuery) {
}
TEST_F(ServerTest, MultipartFormData) {
Request req;
req.method = "POST";
req.path = "/multipart";
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"},
};
std::string host_and_port;
host_and_port += HOST;
host_and_port += ":";
host_and_port += std::to_string(PORT);
auto res = cli_.Post("/multipart", items);
req.headers.emplace("Host", host_and_port.c_str());
req.headers.emplace("Accept", "*/*");
req.headers.emplace("User-Agent", "cpp-httplib/0.1");
req.headers.emplace(
"Content-Type",
"multipart/form-data; boundary=----WebKitFormBoundarysBREP3G013oUrLB4");
req.body =
"------WebKitFormBoundarysBREP3G013oUrLB4\r\nContent-Disposition: "
"form-data; name=\"text1\"\r\n\r\ntext "
"default\r\n------WebKitFormBoundarysBREP3G013oUrLB4\r\nContent-"
"Disposition: form-data; "
"name=\"text2\"\r\n\r\naωb\r\n------"
"WebKitFormBoundarysBREP3G013oUrLB4\r\nContent-Disposition: form-data; "
"name=\"file1\"; filename=\"hello.txt\"\r\nContent-Type: "
"text/"
"plain\r\n\r\nh\ne\n\nl\nl\no\n\r\n------"
"WebKitFormBoundarysBREP3G013oUrLB4\r\nContent-Disposition: form-data; "
"name=\"file2\"; filename=\"world.json\"\r\nContent-Type: "
"application/json\r\n\r\n{\n \"world\", "
"true\n}\n\r\n------WebKitFormBoundarysBREP3G013oUrLB4\r\ncontent-"
"disposition: form-data; name=\"file3\"; filename=\"\"\r\ncontent-type: "
"application/"
"octet-stream\r\n\r\n\r\n------WebKitFormBoundarysBREP3G013oUrLB4--\r\n";
auto res = std::make_shared<Response>();
auto ret = cli_.send(req, *res);
ASSERT_TRUE(ret);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
}
@@ -1016,25 +1078,114 @@ TEST_F(ServerTest, CaseInsensitiveTransferEncoding) {
EXPECT_EQ(200, res->status);
}
TEST_F(ServerTest, GetStreamed2) {
auto res = cli_.Get("/streamed", {{make_range_header({{2, 3}})}});
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(206, res->status);
EXPECT_EQ("2", res->get_header_value("Content-Length"));
EXPECT_EQ(std::string("ab"), res->body);
}
TEST_F(ServerTest, GetStreamed) {
auto res = cli_.Get("/streamed");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
EXPECT_EQ("6", res->get_header_value("Content-Length"));
EXPECT_TRUE(res->body == "aaabbb");
EXPECT_EQ(std::string("aaabbb"), res->body);
}
TEST_F(ServerTest, GetStreamedWithRange1) {
auto res = cli_.Get("/streamed-with-range", {{make_range_header({{3, 5}})}});
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(206, res->status);
EXPECT_EQ("3", res->get_header_value("Content-Length"));
EXPECT_EQ(true, res->has_header("Content-Range"));
EXPECT_EQ(std::string("def"), res->body);
}
TEST_F(ServerTest, GetStreamedWithRange2) {
auto res = cli_.Get("/streamed-with-range", {{make_range_header({{1, -1}})}});
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(206, res->status);
EXPECT_EQ("6", res->get_header_value("Content-Length"));
EXPECT_EQ(true, res->has_header("Content-Range"));
EXPECT_EQ(std::string("bcdefg"), res->body);
}
TEST_F(ServerTest, GetStreamedWithRangeMultipart) {
auto res =
cli_.Get("/streamed-with-range", {{make_range_header({{1, 2}, {4, 5}})}});
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(206, res->status);
EXPECT_EQ("269", res->get_header_value("Content-Length"));
EXPECT_EQ(false, res->has_header("Content-Range"));
EXPECT_EQ(269, res->body.size());
}
TEST_F(ServerTest, GetStreamedEndless) {
auto res = cli_.Get("/streamed-cancel",
[](const char * /*data*/, uint64_t /*data_length*/,
uint64_t offset,
uint64_t /*content_length*/) { return offset < 100; });
ASSERT_TRUE(res == nullptr);
}
TEST_F(ServerTest, GetWithRange1) {
auto res = cli_.Get("/with-range", {{make_range_header({{3, 5}})}});
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(206, res->status);
EXPECT_EQ("3", res->get_header_value("Content-Length"));
EXPECT_EQ(true, res->has_header("Content-Range"));
EXPECT_EQ(std::string("def"), res->body);
}
TEST_F(ServerTest, GetWithRange2) {
auto res = cli_.Get("/with-range", {{make_range_header({{1, -1}})}});
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(206, res->status);
EXPECT_EQ("6", res->get_header_value("Content-Length"));
EXPECT_EQ(true, res->has_header("Content-Range"));
EXPECT_EQ(std::string("bcdefg"), res->body);
}
TEST_F(ServerTest, GetWithRange3) {
auto res = cli_.Get("/with-range", {{make_range_header({{0, 0}})}});
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(206, res->status);
EXPECT_EQ("1", res->get_header_value("Content-Length"));
EXPECT_EQ(true, res->has_header("Content-Range"));
EXPECT_EQ(std::string("a"), res->body);
}
TEST_F(ServerTest, GetWithRange4) {
auto res = cli_.Get("/with-range", {{make_range_header({{-1, 2}})}});
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(206, res->status);
EXPECT_EQ("2", res->get_header_value("Content-Length"));
EXPECT_EQ(true, res->has_header("Content-Range"));
EXPECT_EQ(std::string("fg"), res->body);
}
TEST_F(ServerTest, GetWithRangeMultipart) {
auto res = cli_.Get("/with-range", {{make_range_header({{1, 2}, {4, 5}})}});
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(206, res->status);
EXPECT_EQ("269", res->get_header_value("Content-Length"));
EXPECT_EQ(false, res->has_header("Content-Range"));
EXPECT_EQ(269, res->body.size());
}
TEST_F(ServerTest, GetStreamedChunked) {
auto res = cli_.Get("/streamedchunked");
auto res = cli_.Get("/streamed-chunked");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
EXPECT_TRUE(res->body == "aaabbb");
EXPECT_EQ(std::string("123456789"), res->body);
}
TEST_F(ServerTest, LargeChunkedPost) {
Request req;
req.method = "POST";
req.path = "/largechunked";
req.path = "/large-chunked";
std::string host_and_port;
host_and_port += HOST;
@@ -1122,14 +1273,49 @@ TEST_F(ServerTest, ArrayParam) {
}
TEST_F(ServerTest, NoMultipleHeaders) {
Headers headers;
headers.emplace("Content-Length", "5");
Headers headers = {{"Content-Length", "5"}};
auto res = cli_.Post("/validate-no-multiple-headers", headers, "hello",
"text/plain");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
}
TEST_F(ServerTest, KeepAlive) {
cli_.set_keep_alive_max_count(4);
std::vector<Request> requests;
Get(requests, "/hi");
Get(requests, "/hi");
Get(requests, "/hi");
Get(requests, "/not-exist");
Post(requests, "/empty", "", "text/plain");
std::vector<Response> responses;
auto ret = cli_.send(requests, responses);
ASSERT_TRUE(ret == true);
ASSERT_TRUE(requests.size() == responses.size());
for (int i = 0; i < 3; i++) {
auto& res = responses[i];
EXPECT_EQ(200, res.status);
EXPECT_EQ("text/plain", res.get_header_value("Content-Type"));
EXPECT_EQ("Hello World!", res.body);
}
{
auto& res = responses[3];
EXPECT_EQ(404, res.status);
}
{
auto& res = responses[4];
EXPECT_EQ(200, res.status);
EXPECT_EQ("text/plain", res.get_header_value("Content-Type"));
EXPECT_EQ("empty", res.body);
}
}
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
TEST_F(ServerTest, Gzip) {
Headers headers;
@@ -1146,6 +1332,27 @@ TEST_F(ServerTest, Gzip) {
EXPECT_EQ(200, res->status);
}
TEST_F(ServerTest, GzipWithContentReceiver) {
Headers headers;
headers.emplace("Accept-Encoding", "gzip, deflate");
std::string body;
auto res = cli_.Get("/gzip", headers,
[&](const char *data, uint64_t data_length,
uint64_t /*offset*/, uint64_t /*content_length*/) {
body.append(data, data_length);
return true;
});
ASSERT_TRUE(res != nullptr);
EXPECT_EQ("gzip", res->get_header_value("Content-Encoding"));
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
EXPECT_EQ("33", res->get_header_value("Content-Length"));
EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456"
"7890123456789012345678901234567890",
body);
EXPECT_EQ(200, res->status);
}
TEST_F(ServerTest, NoGzip) {
Headers headers;
headers.emplace("Accept-Encoding", "gzip, deflate");
@@ -1161,6 +1368,27 @@ TEST_F(ServerTest, NoGzip) {
EXPECT_EQ(200, res->status);
}
TEST_F(ServerTest, NoGzipWithContentReceiver) {
Headers headers;
headers.emplace("Accept-Encoding", "gzip, deflate");
std::string body;
auto res = cli_.Get("/nogzip", headers,
[&](const char *data, uint64_t data_length,
uint64_t /*offset*/, uint64_t /*content_length*/) {
body.append(data, data_length);
return true;
});
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(false, res->has_header("Content-Encoding"));
EXPECT_EQ("application/octet-stream", res->get_header_value("Content-Type"));
EXPECT_EQ("100", res->get_header_value("Content-Length"));
EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456"
"7890123456789012345678901234567890",
body);
EXPECT_EQ(200, res->status);
}
TEST_F(ServerTest, MultipartFormDataGzip) {
Request req;
req.method = "POST";
@@ -1409,6 +1637,7 @@ TEST(SSLClientServerTest, ClientCertPresent) {
});
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
msleep(1);
httplib::SSLClient cli(HOST, PORT, 30, CLIENT_CERT_FILE,
CLIENT_PRIVATE_KEY_FILE);
@@ -1427,6 +1656,7 @@ TEST(SSLClientServerTest, ClientCertMissing) {
svr.Get("/test", [&](const Request &, Response &) { ASSERT_TRUE(false); });
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
msleep(1);
httplib::SSLClient cli(HOST, PORT, 30);
auto res = cli.Get("/test");
@@ -1447,6 +1677,7 @@ TEST(SSLClientServerTest, TrustDirOptional) {
});
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
msleep(1);
httplib::SSLClient cli(HOST, PORT, 30, CLIENT_CERT_FILE,
CLIENT_PRIVATE_KEY_FILE);