mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 88c961f37e | |||
| b952376968 | |||
| 5dd605d3a2 | |||
| 9c0c98b1ed | |||
| 615867322d | |||
| 02d3cd5909 | |||
| 47e5af15ea | |||
| a5c239c174 | |||
| c2afc5ca44 | |||
| cee062d4c9 | |||
| b21dc8cbe0 | |||
| e1133a2dcb | |||
| e273fec93c | |||
| 95d0b073bd | |||
| 9c7d841b37 | |||
| f086bf5310 | |||
| 6613d7b7ad | |||
| 6adf130bf3 | |||
| b6b2eaf5bc | |||
| eb4b7c70a9 | |||
| 84661ea6ed | |||
| 041122908c | |||
| 401de608df | |||
| 726c64cf10 | |||
| e1f781a21a | |||
| 72b81badad | |||
| 17428a8fbf | |||
| 6e1879dfae | |||
| eb1d2e04bc |
@@ -32,7 +32,7 @@ jobs:
|
||||
run: cd test && make -f Makefile.fuzz_test
|
||||
- name: setup msbuild on windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
uses: warrenbuckley/Setup-MSBuild@v1
|
||||
uses: microsoft/setup-msbuild@v1.0.2
|
||||
- name: make-windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
|
||||
@@ -275,6 +275,7 @@ svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
|
||||
|
||||
```cpp
|
||||
svr.set_keep_alive_max_count(2); // Default is 5
|
||||
svr.set_keep_alive_timeout(10); // Default is 5
|
||||
```
|
||||
|
||||
### Timeout
|
||||
@@ -502,7 +503,7 @@ auto res = cli.Get(
|
||||
```cpp
|
||||
std::string body = ...;
|
||||
|
||||
auto res = cli_.Post(
|
||||
auto res = cli.Post(
|
||||
"/stream", body.size(),
|
||||
[](size_t offset, size_t length, DataSink &sink) {
|
||||
sink.write(body.data() + offset, length);
|
||||
@@ -511,6 +512,21 @@ auto res = cli_.Post(
|
||||
"text/plain");
|
||||
```
|
||||
|
||||
### Chunked transfer encoding
|
||||
|
||||
```cpp
|
||||
auto res = cli.Post(
|
||||
"/stream",
|
||||
[](size_t offset, DataSink &sink) {
|
||||
sink.os << "chunked data 1";
|
||||
sink.os << "chunked data 2";
|
||||
sink.os << "chunked data 3";
|
||||
sink.done();
|
||||
return true; // return 'false' if you want to cancel the request.
|
||||
},
|
||||
"text/plain");
|
||||
```
|
||||
|
||||
### With Progress Callback
|
||||
|
||||
```cpp
|
||||
@@ -630,6 +646,12 @@ cli.set_ca_cert_path("./ca-bundle.crt");
|
||||
cli.enable_server_certificate_verification(true);
|
||||
```
|
||||
|
||||
Note: When using SSL, it seems impossible to avoid SIGPIPE in all cases, since on some operating systems, SIGPIPE
|
||||
can only be suppressed on a per-message basis, but there is no way to make the OpenSSL library do so for its
|
||||
internal communications. If your program needs to avoid being terminated on SIGPIPE, the only fully general way might
|
||||
be to set up a signal handler for SIGPIPE to handle or ignore it yourself.
|
||||
|
||||
|
||||
Compression
|
||||
-----------
|
||||
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
#CXX = clang++
|
||||
CXXFLAGS = -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion #-fsanitize=address
|
||||
CXXFLAGS = -g -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion #-fsanitize=address
|
||||
|
||||
OPENSSL_DIR = /usr/local/opt/openssl@1.1
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||
@@ -24,6 +24,7 @@ test_proxy : test_proxy.cc ../httplib.h Makefile cert.pem
|
||||
cert.pem:
|
||||
openssl genrsa 2048 > key.pem
|
||||
openssl req -new -batch -config test.conf -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
||||
openssl req -x509 -config test.conf -key key.pem -sha256 -days 3650 -nodes -out cert2.pem -extensions SAN
|
||||
openssl genrsa 2048 > rootCA.key.pem
|
||||
openssl req -x509 -new -batch -config test.rootCA.conf -key rootCA.key.pem -days 1024 > rootCA.cert.pem
|
||||
openssl genrsa 2048 > client.key.pem
|
||||
|
||||
@@ -19,7 +19,8 @@ all : server_fuzzer
|
||||
|
||||
# Fuzz target, so that you can choose which $(LIB_FUZZING_ENGINE) to use.
|
||||
server_fuzzer : server_fuzzer.cc ../../httplib.h
|
||||
$(CXX) $(CXXFLAGS) -o $@ $< -Wl,-Bstatic $(OPENSSL_SUPPORT) -Wl,-Bdynamic -ldl $(ZLIB_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
|
||||
# $(CXX) $(CXXFLAGS) -o $@ $< -Wl,-Bstatic $(OPENSSL_SUPPORT) -Wl,-Bdynamic -ldl $(ZLIB_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
|
||||
$(CXX) $(CXXFLAGS) -o $@ $< $(ZLIB_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
|
||||
zip -q -r server_fuzzer_seed_corpus.zip corpus
|
||||
|
||||
clean:
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
#include <memory>
|
||||
#include <httplib.h>
|
||||
#include <memory>
|
||||
|
||||
class FuzzedStream : public httplib::Stream {
|
||||
public:
|
||||
FuzzedStream(const uint8_t* data, size_t size)
|
||||
public:
|
||||
FuzzedStream(const uint8_t *data, size_t size)
|
||||
: data_(data), size_(size), read_pos_(0) {}
|
||||
|
||||
ssize_t read(char* ptr, size_t size) override {
|
||||
if (size + read_pos_ > size_) {
|
||||
size = size_ - read_pos_;
|
||||
}
|
||||
ssize_t read(char *ptr, size_t size) override {
|
||||
if (size + read_pos_ > size_) { size = size_ - read_pos_; }
|
||||
memcpy(ptr, data_ + read_pos_, size);
|
||||
read_pos_ += size;
|
||||
return size;
|
||||
return static_cast<ssize_t>(size);
|
||||
}
|
||||
|
||||
ssize_t write(const char* ptr, size_t size) override {
|
||||
ssize_t write(const char *ptr, size_t size) override {
|
||||
response_.append(ptr, size);
|
||||
return static_cast<int>(size);
|
||||
}
|
||||
|
||||
int write(const char* ptr) { return write(ptr, strlen(ptr)); }
|
||||
ssize_t write(const char *ptr) { return write(ptr, strlen(ptr)); }
|
||||
|
||||
int write(const std::string& s) { return write(s.data(), s.size()); }
|
||||
ssize_t write(const std::string &s) { return write(s.data(), s.size()); }
|
||||
|
||||
std::string get_remote_addr() const { return ""; }
|
||||
|
||||
@@ -35,16 +33,18 @@ class FuzzedStream : public httplib::Stream {
|
||||
port = 8080;
|
||||
}
|
||||
|
||||
private:
|
||||
const uint8_t* data_;
|
||||
socket_t socket() const override { return 0; }
|
||||
|
||||
private:
|
||||
const uint8_t *data_;
|
||||
size_t size_;
|
||||
size_t read_pos_;
|
||||
std::string response_;
|
||||
};
|
||||
|
||||
class FuzzableServer : public httplib::Server {
|
||||
public:
|
||||
void ProcessFuzzedRequest(FuzzedStream& stream) {
|
||||
public:
|
||||
void ProcessFuzzedRequest(FuzzedStream &stream) {
|
||||
bool connection_close = false;
|
||||
process_request(stream, /*last_connection=*/false, connection_close,
|
||||
nullptr);
|
||||
@@ -53,36 +53,36 @@ class FuzzableServer : public httplib::Server {
|
||||
|
||||
static FuzzableServer g_server;
|
||||
|
||||
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
|
||||
extern "C" int LLVMFuzzerInitialize(int * /*argc*/, char *** /*argv*/) {
|
||||
g_server.Get(R"(.*)",
|
||||
[&](const httplib::Request& req, httplib::Response& res) {
|
||||
[&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||
res.set_content("response content", "text/plain");
|
||||
});
|
||||
g_server.Post(R"(.*)",
|
||||
[&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||
res.set_content("response content", "text/plain");
|
||||
});
|
||||
g_server.Post(R"(.*)",
|
||||
[&](const httplib::Request& req, httplib::Response& res) {
|
||||
g_server.Put(R"(.*)",
|
||||
[&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||
res.set_content("response content", "text/plain");
|
||||
});
|
||||
g_server.Patch(R"(.*)",
|
||||
[&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||
res.set_content("response content", "text/plain");
|
||||
});
|
||||
g_server.Put(R"(.*)",
|
||||
[&](const httplib::Request& req, httplib::Response& res) {
|
||||
res.set_content("response content", "text/plain");
|
||||
});
|
||||
g_server.Patch(R"(.*)",
|
||||
[&](const httplib::Request& req, httplib::Response& res) {
|
||||
res.set_content("response content", "text/plain");
|
||||
});
|
||||
g_server.Delete(R"(.*)",
|
||||
[&](const httplib::Request& req, httplib::Response& res) {
|
||||
res.set_content("response content", "text/plain");
|
||||
});
|
||||
g_server.Options(R"(.*)",
|
||||
[&](const httplib::Request& req, httplib::Response& res) {
|
||||
res.set_content("response content", "text/plain");
|
||||
});
|
||||
g_server.Delete(
|
||||
R"(.*)", [&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||
res.set_content("response content", "text/plain");
|
||||
});
|
||||
g_server.Options(
|
||||
R"(.*)", [&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||
res.set_content("response content", "text/plain");
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
FuzzedStream stream{data, size};
|
||||
g_server.ProcessFuzzedRequest(stream);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +42,9 @@
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion"
|
||||
#elif __GNUC__
|
||||
#pragma gcc diagnostic push
|
||||
#pragma gcc diagnostic ignored "-Wsign-conversion"
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wsign-conversion"
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#endif
|
||||
|
||||
// The following lines pull in the real gtest *.cc files.
|
||||
@@ -9127,5 +9128,5 @@ const char* TypedTestCasePState::VerifyRegisteredTestNames(
|
||||
#if __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#elif __GNUC__
|
||||
#pragma gcc diagnostic pop
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
+7
-6
@@ -63,8 +63,9 @@
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wsign-compare"
|
||||
#elif __GNUC__
|
||||
#pragma gcc diagnostic push
|
||||
#pragma gcc diagnostic ignored "-Wsign-compare"
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wsign-compare"
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#endif
|
||||
|
||||
// Copyright 2005, Google Inc.
|
||||
@@ -18353,8 +18354,8 @@ AssertionResult CmpHelperEQ(const char* expected_expression,
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wsign-compare"
|
||||
#elif __GNUC__
|
||||
#pragma gcc diagnostic push
|
||||
#pragma gcc diagnostic ignored "-Wsign-compare"
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wsign-compare"
|
||||
#endif
|
||||
|
||||
if (expected == actual) {
|
||||
@@ -18366,7 +18367,7 @@ AssertionResult CmpHelperEQ(const char* expected_expression,
|
||||
#elif __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#elif __GNUC__
|
||||
#pragma gcc diagnostic pop
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
return EqFailure(expected_expression,
|
||||
@@ -19564,7 +19565,7 @@ bool StaticAssertTypeEq() {
|
||||
#if __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#elif __GNUC__
|
||||
#pragma gcc diagnostic pop
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_GTEST_H_
|
||||
|
||||
+224
-30
@@ -2,11 +2,13 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <thread>
|
||||
|
||||
#define SERVER_CERT_FILE "./cert.pem"
|
||||
#define SERVER_CERT2_FILE "./cert2.pem"
|
||||
#define SERVER_PRIVATE_KEY_FILE "./key.pem"
|
||||
#define CA_CERT_FILE "./ca-bundle.crt"
|
||||
#define CLIENT_CA_CERT_FILE "./rootCA.cert.pem"
|
||||
@@ -134,6 +136,17 @@ TEST(GetHeaderValueTest, RegularValue) {
|
||||
EXPECT_STREQ("text/html", val);
|
||||
}
|
||||
|
||||
TEST(GetHeaderValueTest, SetContent) {
|
||||
Response res;
|
||||
|
||||
res.set_content("html", "text/html");
|
||||
EXPECT_EQ("text/html", res.get_header_value("Content-Type"));
|
||||
|
||||
res.set_content("text", "text/plain");
|
||||
EXPECT_EQ(1, res.get_header_value_count("Content-Type"));
|
||||
EXPECT_EQ("text/plain", res.get_header_value("Content-Type"));
|
||||
}
|
||||
|
||||
TEST(GetHeaderValueTest, RegularValueInt) {
|
||||
Headers headers = {{"Content-Length", "100"}, {"Dummy", "Dummy"}};
|
||||
auto val =
|
||||
@@ -766,6 +779,7 @@ TEST(YahooRedirectTest, Redirect) {
|
||||
res = cli.Get("/");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("https://yahoo.com/", res->location);
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -829,7 +843,7 @@ TEST(UrlWithSpace, Redirect) {
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(Server, BindDualStack) {
|
||||
TEST(BindServerTest, BindDualStack) {
|
||||
Server svr;
|
||||
|
||||
svr.Get("/1", [&](const Request & /*req*/, Response &res) {
|
||||
@@ -862,7 +876,7 @@ TEST(Server, BindDualStack) {
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
TEST(Server, BindAndListenSeparately) {
|
||||
TEST(BindServerTest, BindAndListenSeparately) {
|
||||
Server svr;
|
||||
int port = svr.bind_to_any_port("0.0.0.0");
|
||||
ASSERT_TRUE(svr.is_valid());
|
||||
@@ -871,7 +885,7 @@ TEST(Server, BindAndListenSeparately) {
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(SSLServer, BindAndListenSeparately) {
|
||||
TEST(BindServerTest, BindAndListenSeparatelySSL) {
|
||||
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE,
|
||||
CLIENT_CA_CERT_DIR);
|
||||
int port = svr.bind_to_any_port("0.0.0.0");
|
||||
@@ -881,6 +895,41 @@ TEST(SSLServer, BindAndListenSeparately) {
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(ErrorHandlerTest, ContentLength) {
|
||||
Server svr;
|
||||
|
||||
svr.set_error_handler([](const Request & /*req*/, Response &res) {
|
||||
res.status = 200;
|
||||
res.set_content("abcdefghijklmnopqrstuvwxyz",
|
||||
"text/html"); // <= Content-Length still 13
|
||||
});
|
||||
|
||||
svr.Get("/hi", [](const Request & /*req*/, Response &res) {
|
||||
res.set_content("Hello World!\n", "text/plain");
|
||||
res.status = 524;
|
||||
});
|
||||
|
||||
auto thread = std::thread([&]() { svr.listen(HOST, PORT); });
|
||||
|
||||
// Give GET time to get a few messages.
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
{
|
||||
Client cli(HOST, PORT);
|
||||
|
||||
auto res = cli.Get("/hi");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ("26", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ("abcdefghijklmnopqrstuvwxyz", res->body);
|
||||
}
|
||||
|
||||
svr.stop();
|
||||
thread.join();
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
class ServerTest : public ::testing::Test {
|
||||
protected:
|
||||
ServerTest()
|
||||
@@ -1387,6 +1436,7 @@ TEST_F(ServerTest, GetMethod302Redirect) {
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("Hello World!", res->body);
|
||||
EXPECT_EQ("/hi", res->location);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetMethod404) {
|
||||
@@ -1615,6 +1665,7 @@ TEST_F(ServerTest, PostMethod303Redirect) {
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("redirected.", res->body);
|
||||
EXPECT_EQ("/2", res->location);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, UserDefinedMIMETypeMapping) {
|
||||
@@ -1708,7 +1759,8 @@ TEST_F(ServerTest, LongHeader) {
|
||||
"@@@@@@@@@@@@@@@@");
|
||||
|
||||
auto res = std::make_shared<Response>();
|
||||
auto ret = cli_.send(req, *res);
|
||||
auto error = Error::Success;
|
||||
auto ret = cli_.send(req, *res, error);
|
||||
|
||||
ASSERT_TRUE(ret);
|
||||
EXPECT_EQ(200, res->status);
|
||||
@@ -1768,7 +1820,8 @@ TEST_F(ServerTest, TooLongHeader) {
|
||||
"@@@@@@@@@@@@@@@@@");
|
||||
|
||||
auto res = std::make_shared<Response>();
|
||||
auto ret = cli_.send(req, *res);
|
||||
auto error = Error::Success;
|
||||
auto ret = cli_.send(req, *res, error);
|
||||
|
||||
ASSERT_TRUE(ret);
|
||||
EXPECT_EQ(200, res->status);
|
||||
@@ -1857,7 +1910,8 @@ TEST_F(ServerTest, CaseInsensitiveTransferEncoding) {
|
||||
req.body = "4\r\ndech\r\nf\r\nunked post body\r\n0\r\n\r\n";
|
||||
|
||||
auto res = std::make_shared<Response>();
|
||||
auto ret = cli_.send(req, *res);
|
||||
auto error = Error::Success;
|
||||
auto ret = cli_.send(req, *res, error);
|
||||
|
||||
ASSERT_TRUE(ret);
|
||||
EXPECT_EQ(200, res->status);
|
||||
@@ -1898,9 +1952,7 @@ TEST_F(ServerTest, GetStreamedWithRange2) {
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetStreamedWithRangeSuffix1) {
|
||||
auto res = cli_.Get("/streamed-with-range", {
|
||||
{"Range", "bytes=-3"}
|
||||
});
|
||||
auto res = cli_.Get("/streamed-with-range", {{"Range", "bytes=-3"}});
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(206, res->status);
|
||||
EXPECT_EQ("3", res->get_header_value("Content-Length"));
|
||||
@@ -1908,11 +1960,8 @@ TEST_F(ServerTest, GetStreamedWithRangeSuffix1) {
|
||||
EXPECT_EQ(std::string("efg"), res->body);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(ServerTest, GetStreamedWithRangeSuffix2) {
|
||||
auto res = cli_.Get("/streamed-with-range", {
|
||||
{"Range", "bytes=-9999"}
|
||||
});
|
||||
auto res = cli_.Get("/streamed-with-range", {{"Range", "bytes=-9999"}});
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(206, res->status);
|
||||
EXPECT_EQ("7", res->get_header_value("Content-Length"));
|
||||
@@ -1920,15 +1969,23 @@ TEST_F(ServerTest, GetStreamedWithRangeSuffix2) {
|
||||
EXPECT_EQ(std::string("abcdefg"), res->body);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(ServerTest, GetStreamedWithRangeError) {
|
||||
auto res = cli_.Get("/streamed-with-range", {
|
||||
{"Range", "bytes=92233720368547758079223372036854775806-92233720368547758079223372036854775807"}
|
||||
});
|
||||
auto res = cli_.Get("/streamed-with-range",
|
||||
{{"Range", "bytes=92233720368547758079223372036854775806-"
|
||||
"92233720368547758079223372036854775807"}});
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(416, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetRangeWithMaxLongLength) {
|
||||
auto res =
|
||||
cli_.Get("/with-range", {{"Range", "bytes=0-9223372036854775807"}});
|
||||
EXPECT_EQ(206, res->status);
|
||||
EXPECT_EQ("7", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ(true, res->has_header("Content-Range"));
|
||||
EXPECT_EQ(std::string("abcdefg"), res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetStreamedWithRangeMultipart) {
|
||||
auto res =
|
||||
cli_.Get("/streamed-with-range", {{make_range_header({{1, 2}, {4, 5}})}});
|
||||
@@ -1964,8 +2021,7 @@ TEST_F(ServerTest, ClientStop) {
|
||||
}));
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
while (cli_.is_socket_open()) {
|
||||
cli_.stop();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
@@ -2011,6 +2067,12 @@ TEST_F(ServerTest, GetWithRange4) {
|
||||
EXPECT_EQ(std::string("fg"), res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetWithRangeOffsetGreaterThanContent) {
|
||||
auto res = cli_.Get("/with-range", {{make_range_header({{10000, 20000}})}});
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(416, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetWithRangeMultipart) {
|
||||
auto res = cli_.Get("/with-range", {{make_range_header({{1, 2}, {4, 5}})}});
|
||||
ASSERT_TRUE(res);
|
||||
@@ -2020,6 +2082,13 @@ TEST_F(ServerTest, GetWithRangeMultipart) {
|
||||
EXPECT_EQ(269, res->body.size());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetWithRangeMultipartOffsetGreaterThanContent) {
|
||||
auto res =
|
||||
cli_.Get("/with-range", {{make_range_header({{-1, 2}, {10000, 30000}})}});
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(416, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetStreamedChunked) {
|
||||
auto res = cli_.Get("/streamed-chunked");
|
||||
ASSERT_TRUE(res);
|
||||
@@ -2059,7 +2128,8 @@ TEST_F(ServerTest, LargeChunkedPost) {
|
||||
req.body = chunk + chunk + chunk + chunk + chunk + chunk + "0\r\n\r\n";
|
||||
|
||||
auto res = std::make_shared<Response>();
|
||||
auto ret = cli_.send(req, *res);
|
||||
auto error = Error::Success;
|
||||
auto ret = cli_.send(req, *res, error);
|
||||
|
||||
ASSERT_TRUE(ret);
|
||||
EXPECT_EQ(200, res->status);
|
||||
@@ -2155,6 +2225,31 @@ TEST_F(ServerTest, PostWithContentProviderAbort) {
|
||||
EXPECT_EQ(Error::Canceled, res.error());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PutWithContentProviderWithoutLength) {
|
||||
auto res = cli_.Put(
|
||||
"/put",
|
||||
[](size_t /*offset*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
sink.os << "PUT";
|
||||
sink.done();
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("PUT", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PostWithContentProviderWithoutLengthAbort) {
|
||||
auto res = cli_.Post(
|
||||
"/post", [](size_t /*offset*/, DataSink & /*sink*/) { return false; },
|
||||
"text/plain");
|
||||
|
||||
ASSERT_TRUE(!res);
|
||||
EXPECT_EQ(Error::Canceled, res.error());
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
||||
TEST_F(ServerTest, PutWithContentProviderWithGzip) {
|
||||
cli_.set_compress(true);
|
||||
@@ -2185,6 +2280,33 @@ TEST_F(ServerTest, PostWithContentProviderWithGzipAbort) {
|
||||
EXPECT_EQ(Error::Canceled, res.error());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PutWithContentProviderWithoutLengthWithGzip) {
|
||||
cli_.set_compress(true);
|
||||
auto res = cli_.Put(
|
||||
"/put",
|
||||
[](size_t /*offset*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
sink.os << "PUT";
|
||||
sink.done();
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("PUT", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PostWithContentProviderWithoutLengthWithGzipAbort) {
|
||||
cli_.set_compress(true);
|
||||
auto res = cli_.Post(
|
||||
"/post", [](size_t /*offset*/, DataSink & /*sink*/) { return false; },
|
||||
"text/plain");
|
||||
|
||||
ASSERT_TRUE(!res);
|
||||
EXPECT_EQ(Error::Canceled, res.error());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PutLargeFileWithGzip) {
|
||||
cli_.set_compress(true);
|
||||
auto res = cli_.Put("/put-large", LARGE_DATA, "text/plain");
|
||||
@@ -2272,7 +2394,7 @@ TEST(GzipDecompressor, ChunkedDecompression) {
|
||||
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
|
||||
TEST_F(ServerTest, GetStreamedChunkedWithBrotli) {
|
||||
Headers headers;
|
||||
headers.emplace("Accept-Encoding", "brotli");
|
||||
headers.emplace("Accept-Encoding", "br");
|
||||
|
||||
auto res = cli_.Get("/streamed-chunked", headers);
|
||||
ASSERT_TRUE(res);
|
||||
@@ -2282,7 +2404,7 @@ TEST_F(ServerTest, GetStreamedChunkedWithBrotli) {
|
||||
|
||||
TEST_F(ServerTest, GetStreamedChunkedWithBrotli2) {
|
||||
Headers headers;
|
||||
headers.emplace("Accept-Encoding", "brotli");
|
||||
headers.emplace("Accept-Encoding", "br");
|
||||
|
||||
auto res = cli_.Get("/streamed-chunked2", headers);
|
||||
ASSERT_TRUE(res);
|
||||
@@ -2433,7 +2555,8 @@ TEST_F(ServerTest, HTTP2Magic) {
|
||||
req.body = "SM";
|
||||
|
||||
auto res = std::make_shared<Response>();
|
||||
auto ret = cli_.send(req, *res);
|
||||
auto error = Error::Success;
|
||||
auto ret = cli_.send(req, *res, error);
|
||||
|
||||
ASSERT_TRUE(ret);
|
||||
EXPECT_EQ(400, res->status);
|
||||
@@ -2631,7 +2754,7 @@ TEST_F(ServerTest, Brotli) {
|
||||
auto res = cli_.Get("/compress", headers);
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ("brotli", res->get_header_value("Content-Encoding"));
|
||||
EXPECT_EQ("br", res->get_header_value("Content-Encoding"));
|
||||
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ("19", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456"
|
||||
@@ -2644,7 +2767,7 @@ TEST_F(ServerTest, Brotli) {
|
||||
// Sends a raw request to a server listening at HOST:PORT.
|
||||
static bool send_request(time_t read_timeout_sec, const std::string &req,
|
||||
std::string *resp = nullptr) {
|
||||
Error error = Error::Success;
|
||||
auto error = Error::Success;
|
||||
|
||||
auto client_sock =
|
||||
detail::create_client_socket(HOST, PORT, false, nullptr,
|
||||
@@ -3037,9 +3160,7 @@ TEST(KeepAliveTest, ReadTimeoutSSL) {
|
||||
res.set_content("b", "text/plain");
|
||||
});
|
||||
|
||||
auto listen_thread = std::thread([&svr]() {
|
||||
svr.listen("localhost", PORT);
|
||||
});
|
||||
auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
|
||||
while (!svr.is_running()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
@@ -3245,6 +3366,31 @@ TEST(SSLClientTest, ServerCertificateVerification3) {
|
||||
ASSERT_EQ(301, res->status);
|
||||
}
|
||||
|
||||
TEST(SSLClientTest, ServerCertificateVerification4) {
|
||||
SSLServer svr(SERVER_CERT2_FILE, SERVER_PRIVATE_KEY_FILE);
|
||||
ASSERT_TRUE(svr.is_valid());
|
||||
|
||||
svr.Get("/test", [&](const Request &, Response &res) {
|
||||
res.set_content("test", "text/plain");
|
||||
svr.stop();
|
||||
ASSERT_TRUE(true);
|
||||
});
|
||||
|
||||
thread t = thread([&]() { ASSERT_TRUE(svr.listen("127.0.0.1", PORT)); });
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
|
||||
SSLClient cli("127.0.0.1", PORT);
|
||||
cli.set_ca_cert_path(SERVER_CERT2_FILE);
|
||||
cli.enable_server_certificate_verification(true);
|
||||
cli.set_connection_timeout(30);
|
||||
|
||||
auto res = cli.Get("/test");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST(SSLClientTest, WildcardHostNameMatch) {
|
||||
SSLClient cli("www.youtube.com");
|
||||
|
||||
@@ -3420,6 +3566,51 @@ TEST(SSLClientServerTest, TrustDirOptional) {
|
||||
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST(SSLClientServerTest, SSLConnectTimeout) {
|
||||
class NoListenSSLServer : public SSLServer {
|
||||
public:
|
||||
NoListenSSLServer(const char *cert_path, const char *private_key_path,
|
||||
const char *client_ca_cert_file_path,
|
||||
const char *client_ca_cert_dir_path = nullptr)
|
||||
: SSLServer(cert_path, private_key_path, client_ca_cert_file_path,
|
||||
client_ca_cert_dir_path),
|
||||
stop_(false) {}
|
||||
|
||||
bool stop_;
|
||||
|
||||
private:
|
||||
bool process_and_close_socket(socket_t /*sock*/) override {
|
||||
// Don't create SSL context
|
||||
while (!stop_) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
NoListenSSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE,
|
||||
CLIENT_CA_CERT_FILE);
|
||||
ASSERT_TRUE(svr.is_valid());
|
||||
|
||||
svr.Get("/test", [&](const Request &, Response &res) {
|
||||
res.set_content("test", "text/plain");
|
||||
});
|
||||
|
||||
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
|
||||
SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
|
||||
cli.enable_server_certificate_verification(false);
|
||||
cli.set_connection_timeout(1);
|
||||
|
||||
auto res = cli.Get("/test");
|
||||
ASSERT_TRUE(!res);
|
||||
EXPECT_EQ(Error::SSLConnection, res.error());
|
||||
|
||||
svr.stop_ = true;
|
||||
svr.stop();
|
||||
t.join();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -3456,6 +3647,7 @@ TEST(YahooRedirectTest2, SimpleInterface) {
|
||||
res = cli.Get("/");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("https://yahoo.com/", res->location);
|
||||
}
|
||||
|
||||
TEST(YahooRedirectTest3, SimpleInterface) {
|
||||
@@ -3469,6 +3661,7 @@ TEST(YahooRedirectTest3, SimpleInterface) {
|
||||
res = cli.Get("/");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("https://www.yahoo.com/", res->location);
|
||||
}
|
||||
|
||||
TEST(YahooRedirectTest3, NewResultInterface) {
|
||||
@@ -3492,13 +3685,14 @@ TEST(YahooRedirectTest3, NewResultInterface) {
|
||||
EXPECT_EQ(200, res.value().status);
|
||||
EXPECT_EQ(200, (*res).status);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("https://www.yahoo.com/", res->location);
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
|
||||
TEST(DecodeWithChunkedEncoding, BrotliEncoding) {
|
||||
Client cli("https://cdnjs.cloudflare.com");
|
||||
auto res = cli.Get("/ajax/libs/jquery/3.5.1/jquery.js",
|
||||
{{"Accept-Encoding", "brotli"}});
|
||||
auto res =
|
||||
cli.Get("/ajax/libs/jquery/3.5.1/jquery.js", {{"Accept-Encoding", "br"}});
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
|
||||
@@ -16,3 +16,6 @@ emailAddress = test@email.address
|
||||
|
||||
[req_attributes]
|
||||
challengePassword = 1234
|
||||
|
||||
[SAN]
|
||||
subjectAltName=IP:127.0.0.1
|
||||
|
||||
Reference in New Issue
Block a user