Compare commits

...

18 Commits

Author SHA1 Message Date
yhirose ab477b5631 Fix "Issue 37742 in oss-fuzz: cpp-httplib:server_fuzzer: Timeout in server_fuzzer" 2021-09-02 22:57:57 -04:00
yhirose 0823d5c7f2 Fixed #1031 2021-08-30 17:16:31 -04:00
yhirose 1cc6930363 Append '_Online' suffix to Unit test names that access external servers 2021-08-23 13:02:19 -04:00
yhirose 4297500928 Fix #1024 2021-08-17 09:28:17 -04:00
Thomas Behn a58f042614 Don't define INVALID_SOCKET if it has been defined already (i.e. by libpcap) (#1021)
Co-authored-by: Thomas Behn <Thomas.Behn@meinberg.de>
2021-08-10 08:29:27 -04:00
yhirose 469c6bc2b6 Fix #1017 2021-08-02 15:44:50 -04:00
Joel Rosdahl 887074efd2 Add test of httplib.h split into .h + .cc (#1015)
In order to test the split version (.h + .cc via split.py):

- Added a test_split program in the test directory whose main purpose is
  to verify that it works to compile and link the test case code against
  the split httplib.h version.
- Moved types needed for test cases to the “header part” of httplib.h.
  Also added forward declarations of functions needed by test cases.
- Added an include_httplib.cc file which is linked together with test.cc
  to verify that inline keywords have not been forgotten.

The changes to httplib.h just move code around (or add forward
declarations), with one exception: detail::split and
detail::process_client_socket have been converted to non-template
functions (taking an std::function instead of using a type parameter for
the function) and forward-declared instead. This avoids having to move
the templates to the “header part”.
2021-07-31 09:53:30 -04:00
Joel Rosdahl 9c2c15ca45 Add missing template method implementations (#1013)
When using the split version of httplib.h the templated implementation
of e.g. Client::set_connection_timeout ends up in httplib.cc and
therefore results in a linker error since the needed template
specialization has not been instantiated. Fix this by moving the
implementation of template methods into the part that ends up in
httplib.h after the split.

Fixes #1008.
2021-07-30 10:05:49 -04:00
Joel Rosdahl 1b3b098329 Avoid hardcoded ports in RedirectToDifferentPort.Redirect test (#1012)
The RedirectToDifferentPort.Redirect test assumes that port 8080 and
8081 are available on localhost. They aren’t on my system so the test
fails. Improve this by binding to available ports instead of hardcoded
ones.
2021-07-30 10:04:02 -04:00
Joel Rosdahl 6f7075e3aa Improve split script (#1011)
- Added shebang and made the script executable.
- Added help text.
- Added -o/--out argument for specifying output directory.
- Added -e/--extension argument for specifying file extension of the
  implementation file.
- Made the script find httplib.h next to split.py instead of the current
  working directory. This makes it possible to call the script from
  another directory.
- Simplified code structure slightly.
- Improved variable naming to follow Python conventions.
2021-07-30 10:03:05 -04:00
yhirose ccbddd8842 Allow LargeRandomData test only on Windows 2021-07-22 22:17:31 -04:00
yosh-matsuda 879dd261c2 Fix gzip compression/decompression over 4 GiB data size (#1002)
* Fix gzip compression/decompression over 4 GiB data size

* Add gzip test for large random data
2021-07-22 22:07:40 -04:00
xxrl 52f5eb5980 [Fix] ca_cert_path/ce_cert_store lose (#1004)
When redirect from http to https, user setting for ca_cert will lose

issue: #1003
2021-07-22 21:41:41 -04:00
Gregor Jasny ea2f69a0d7 Add httplib::Error to std::string function (#999)
Fixes: #978
2021-07-19 21:17:44 -04:00
Gregor Jasny 9f2064a8ed Fix remaining test warnings (#1001)
* Use portable way to encode ESC

'\e' is a GNU extension

* Use length specifier for size_t
2021-07-19 21:17:18 -04:00
yhirose e3750d9ddf Simplefied server APIs 2021-07-17 18:09:30 -04:00
yhirose c1eee3012e Fix #998 2021-07-17 17:18:56 -04:00
Gregor Jasny 6b08babbd2 Use googletest 1.11.0 (#1000)
* Update googletest to version 1.11.0

* Fix test warnings
2021-07-17 13:21:03 -04:00
11 changed files with 13600 additions and 17147 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
run: brew install brotli
- name: make
if: matrix.os != 'windows-latest'
run: cd test && make
run: cd test && make -j2
- name: check fuzz test target
if: matrix.os == 'ubuntu-latest'
run: cd test && make -f Makefile.fuzz_test
+3
View File
@@ -10,8 +10,11 @@ example/redirect
example/sse*
example/upload
example/*.pem
test/httplib.cc
test/httplib.h
test/test
test/test_proxy
test/test_split
test/test.xcodeproj/xcuser*
test/test.xcodeproj/*/xcuser*
test/*.pem
+15 -5
View File
@@ -256,6 +256,7 @@ svr.Post("/multipart", [&](const auto& req, auto& res) {
svr.Post("/content_receiver",
[&](const Request &req, Response &res, const ContentReader &content_reader) {
if (req.is_multipart_form_data()) {
// NOTE: `content_reader` is blocking until every form data field is read
MultipartFormDataItems files;
content_reader(
[&](const MultipartFormData &file) {
@@ -272,7 +273,6 @@ svr.Post("/content_receiver",
body.append(data, data_length);
return true;
});
res.set_content(body, "text/plain");
}
});
```
@@ -749,10 +749,20 @@ res->body; // Compressed data
Split httplib.h into .h and .cc
-------------------------------
```bash
> python3 split.py
> ls out
httplib.h httplib.cc
```console
$ ./split.py -h
usage: split.py [-h] [-e EXTENSION] [-o OUT]
This script splits httplib.h into .h and .cc parts.
optional arguments:
-h, --help show this help message and exit
-e EXTENSION, --extension EXTENSION
extension of the implementation file (default: cc)
-o OUT, --out OUT where to write the files (default: out)
$ ./split.py
Wrote out/httplib.h and out/httplib.cc
```
NOTE
+678 -550
View File
File diff suppressed because it is too large Load Diff
Regular → Executable
+40 -25
View File
@@ -1,32 +1,47 @@
#!/usr/bin/env python3
"""This script splits httplib.h into .h and .cc parts."""
import argparse
import os
import sys
border = '// ----------------------------------------------------------------------------'
PythonVersion = sys.version_info[0];
args_parser = argparse.ArgumentParser(description=__doc__)
args_parser.add_argument(
"-e", "--extension", help="extension of the implementation file (default: cc)",
default="cc"
)
args_parser.add_argument(
"-o", "--out", help="where to write the files (default: out)", default="out"
)
args = args_parser.parse_args()
with open('httplib.h') as f:
cur_dir = os.path.dirname(sys.argv[0])
with open(cur_dir + '/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')
python_version = sys.version_info[0]
if python_version < 3:
os.makedirs(args.out)
else:
os.makedirs(args.out, exist_ok=True)
in_implementation = False
h_out = args.out + '/httplib.h'
cc_out = args.out + '/httplib.' + args.extension
with open(h_out, 'w') as fh, open(cc_out, 'w') as fc:
fc.write('#include "httplib.h"\n')
fc.write('namespace httplib {\n')
for line in lines:
is_border_line = border in line
if is_border_line:
in_implementation = not in_implementation
elif in_implementation:
fc.write(line.replace('inline ', ''))
else:
fh.write(line)
fc.write('} // namespace httplib\n')
print("Wrote {} and {}".format(h_out, cc_out))
+16 -6
View File
@@ -1,5 +1,5 @@
#CXX = clang++
CXXFLAGS = -g -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion #-fsanitize=address
CXXFLAGS = -g -std=c++11 -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
@@ -9,17 +9,27 @@ ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
BROTLI_DIR = /usr/local/opt/brotli
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
all : test
TEST_ARGS = gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) -pthread
all : test test_split
./test
# Note: The intention of test_split is to verify that it works to compile and
# link the split httplib.h, so there is normally no need to execute it.
proxy : test_proxy
./test_proxy
test : test.cc ../httplib.h Makefile cert.pem
$(CXX) -o test $(CXXFLAGS) test.cc gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) -pthread
test : test.cc include_httplib.cc ../httplib.h Makefile cert.pem
$(CXX) -o $@ -I.. $(CXXFLAGS) test.cc include_httplib.cc $(TEST_ARGS)
test_split : test.cc ../httplib.h httplib.cc Makefile cert.pem
$(CXX) -o $@ $(CXXFLAGS) test.cc httplib.cc $(TEST_ARGS)
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) $(BROTLI_SUPPORT) -pthread
$(CXX) -o $@ -I.. $(CXXFLAGS) test_proxy.cc $(TEST_ARGS)
httplib.cc : ../httplib.h
python3 ../split.py -o .
cert.pem:
openssl genrsa 2048 > key.pem
@@ -32,4 +42,4 @@ cert.pem:
#c_rehash .
clean:
rm -f test test_proxy pem *.0 *.1 *.srl
rm -f test test_proxy pem *.0 *.1 *.srl httplib.h httplib.cc
+5790 -2421
View File
File diff suppressed because it is too large Load Diff
+6845 -14039
View File
File diff suppressed because it is too large Load Diff
+19 -4
View File
@@ -27,13 +27,28 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <iostream>
#include <cstdio>
#include "gtest/gtest.h"
GTEST_API_ int main(int argc, char **argv) {
std::cout << "Running main() from gtest_main.cc\n";
#if GTEST_OS_ESP8266 || GTEST_OS_ESP32
#if GTEST_OS_ESP8266
extern "C" {
#endif
void setup() {
testing::InitGoogleTest();
}
void loop() { RUN_ALL_TESTS(); }
#if GTEST_OS_ESP8266
}
#endif
#else
GTEST_API_ int main(int argc, char **argv) {
printf("Running main() from %s\n", __FILE__);
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
#endif
+5
View File
@@ -0,0 +1,5 @@
// The sole purpose of this file is to include httplib.h in a separate
// compilation unit, thus verifying that inline keywords have not been forgotten
// when linked together with test.cc.
#include <httplib.h>
+188 -96
View File
@@ -172,7 +172,7 @@ TEST(GetHeaderValueTest, SetContent) {
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(1U, res.get_header_value_count("Content-Type"));
EXPECT_EQ("text/plain", res.get_header_value("Content-Type"));
}
@@ -354,7 +354,7 @@ TEST(BufferStreamTest, read) {
EXPECT_EQ(0, strm.read(buf, 1));
}
TEST(ChunkedEncodingTest, FromHTTPWatch) {
TEST(ChunkedEncodingTest, FromHTTPWatch_Online) {
auto host = "www.httpwatch.com";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -377,7 +377,7 @@ TEST(ChunkedEncodingTest, FromHTTPWatch) {
EXPECT_EQ(out, res->body);
}
TEST(ChunkedEncodingTest, WithContentReceiver) {
TEST(ChunkedEncodingTest, WithContentReceiver_Online) {
auto host = "www.httpwatch.com";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -405,7 +405,7 @@ TEST(ChunkedEncodingTest, WithContentReceiver) {
EXPECT_EQ(out, body);
}
TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver) {
TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver_Online) {
auto host = "www.httpwatch.com";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -437,7 +437,7 @@ TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver) {
EXPECT_EQ(out, body);
}
TEST(RangeTest, FromHTTPBin) {
TEST(RangeTest, FromHTTPBin_Online) {
auto host = "httpbin.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -542,7 +542,7 @@ TEST(ConnectionErrorTest, InvalidHostCheckResultErrorToString) {
ASSERT_TRUE(!res);
stringstream s;
s << "error code: " << res.error();
EXPECT_EQ("error code: 2", s.str());
EXPECT_EQ("error code: Connection (2)", s.str());
}
TEST(ConnectionErrorTest, InvalidPort) {
@@ -578,7 +578,7 @@ TEST(ConnectionErrorTest, Timeout) {
EXPECT_TRUE(res.error() == Error::Connection);
}
TEST(CancelTest, NoCancel) {
TEST(CancelTest, NoCancel_Online) {
auto host = "httpbin.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -596,7 +596,7 @@ TEST(CancelTest, NoCancel) {
EXPECT_EQ(200, res->status);
}
TEST(CancelTest, WithCancelSmallPayload) {
TEST(CancelTest, WithCancelSmallPayload_Online) {
auto host = "httpbin.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -613,7 +613,7 @@ TEST(CancelTest, WithCancelSmallPayload) {
EXPECT_EQ(Error::Canceled, res.error());
}
TEST(CancelTest, WithCancelLargePayload) {
TEST(CancelTest, WithCancelLargePayload_Online) {
auto host = "httpbin.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -632,7 +632,7 @@ TEST(CancelTest, WithCancelLargePayload) {
EXPECT_EQ(Error::Canceled, res.error());
}
TEST(BaseAuthTest, FromHTTPWatch) {
TEST(BaseAuthTest, FromHTTPWatch_Online) {
auto host = "httpbin.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -683,7 +683,7 @@ TEST(BaseAuthTest, FromHTTPWatch) {
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
TEST(DigestAuthTest, FromHTTPWatch) {
TEST(DigestAuthTest, FromHTTPWatch_Online) {
auto host = "httpbin.org";
auto port = 443;
SSLClient cli(host, port);
@@ -730,7 +730,7 @@ TEST(DigestAuthTest, FromHTTPWatch) {
}
#endif
TEST(AbsoluteRedirectTest, Redirect) {
TEST(AbsoluteRedirectTest, Redirect_Online) {
auto host = "nghttp2.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -745,7 +745,7 @@ TEST(AbsoluteRedirectTest, Redirect) {
EXPECT_EQ(200, res->status);
}
TEST(RedirectTest, Redirect) {
TEST(RedirectTest, Redirect_Online) {
auto host = "nghttp2.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -760,7 +760,7 @@ TEST(RedirectTest, Redirect) {
EXPECT_EQ(200, res->status);
}
TEST(RelativeRedirectTest, Redirect) {
TEST(RelativeRedirectTest, Redirect_Online) {
auto host = "nghttp2.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -775,7 +775,7 @@ TEST(RelativeRedirectTest, Redirect) {
EXPECT_EQ(200, res->status);
}
TEST(TooManyRedirectTest, Redirect) {
TEST(TooManyRedirectTest, Redirect_Online) {
auto host = "nghttp2.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -791,7 +791,7 @@ TEST(TooManyRedirectTest, Redirect) {
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
TEST(YahooRedirectTest, Redirect) {
TEST(YahooRedirectTest, Redirect_Online) {
Client cli("yahoo.com");
auto res = cli.Get("/");
@@ -805,7 +805,7 @@ TEST(YahooRedirectTest, Redirect) {
EXPECT_EQ("https://yahoo.com/", res->location);
}
TEST(HttpsToHttpRedirectTest, Redirect) {
TEST(HttpsToHttpRedirectTest, Redirect_Online) {
SSLClient cli("nghttp2.org");
cli.set_follow_location(true);
auto res = cli.Get(
@@ -814,7 +814,7 @@ TEST(HttpsToHttpRedirectTest, Redirect) {
EXPECT_EQ(200, res->status);
}
TEST(HttpsToHttpRedirectTest2, Redirect) {
TEST(HttpsToHttpRedirectTest2, Redirect_Online) {
SSLClient cli("nghttp2.org");
cli.set_follow_location(true);
@@ -827,7 +827,7 @@ TEST(HttpsToHttpRedirectTest2, Redirect) {
EXPECT_EQ(200, res->status);
}
TEST(HttpsToHttpRedirectTest3, Redirect) {
TEST(HttpsToHttpRedirectTest3, Redirect_Online) {
SSLClient cli("nghttp2.org");
cli.set_follow_location(true);
@@ -839,55 +839,62 @@ TEST(HttpsToHttpRedirectTest3, Redirect) {
EXPECT_EQ(200, res->status);
}
TEST(UrlWithSpace, Redirect) {
TEST(UrlWithSpace, Redirect_Online) {
SSLClient cli("edge.forgecdn.net");
cli.set_follow_location(true);
auto res = cli.Get("/files/2595/310/Neat 1.4-17.jar");
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
EXPECT_EQ(18527, res->get_header_value<uint64_t>("Content-Length"));
EXPECT_EQ(18527U, res->get_header_value<uint64_t>("Content-Length"));
}
#endif
TEST(RedirectToDifferentPort, Redirect) {
Server svr8080;
Server svr8081;
svr8080.Get("/1", [&](const Request & /*req*/, Response &res) {
res.set_redirect("http://localhost:8081/2");
});
svr8081.Get("/2", [&](const Request & /*req*/, Response &res) {
Server svr1;
svr1.Get("/1", [&](const Request & /*req*/, Response &res) {
res.set_content("Hello World!", "text/plain");
});
auto thread8080 = std::thread([&]() { svr8080.listen("localhost", 8080); });
int svr1_port = 0;
auto thread1 = std::thread([&]() {
svr1_port = svr1.bind_to_any_port("localhost");
svr1.listen_after_bind();
});
auto thread8081 = std::thread([&]() { svr8081.listen("localhost", 8081); });
Server svr2;
svr2.Get("/2", [&](const Request & /*req*/, Response &res) {
res.set_redirect("http://localhost:" + std::to_string(svr1_port) + "/1");
});
while (!svr8080.is_running() || !svr8081.is_running()) {
int svr2_port = 0;
auto thread2 = std::thread([&]() {
svr2_port = svr2.bind_to_any_port("localhost");
svr2.listen_after_bind();
});
while (!svr1.is_running() || !svr2.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
Client cli("localhost", 8080);
Client cli("localhost", svr2_port);
cli.set_follow_location(true);
auto res = cli.Get("/1");
auto res = cli.Get("/2");
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
EXPECT_EQ("Hello World!", res->body);
svr8080.stop();
svr8081.stop();
thread8080.join();
thread8081.join();
ASSERT_FALSE(svr8080.is_running());
ASSERT_FALSE(svr8081.is_running());
svr1.stop();
svr2.stop();
thread1.join();
thread2.join();
ASSERT_FALSE(svr1.is_running());
ASSERT_FALSE(svr2.is_running());
}
TEST(RedirectFromPageWithContent, Redirect) {
@@ -954,7 +961,11 @@ TEST(RedirectFromPageWithContentIP6, Redirect) {
res.set_redirect("http://[::1]:1234/2");
});
svr.Get("/2", [&](const Request & /*req*/, Response &res) {
svr.Get("/2", [&](const Request &req, Response &res) {
auto host_header = req.headers.find("Host");
ASSERT_TRUE(host_header != req.headers.end());
EXPECT_EQ("[::1]:1234", host_header->second);
res.set_content("Hello World!", "text/plain");
});
@@ -1222,9 +1233,9 @@ TEST(RoutingHandlerTest, PreRoutingHandler) {
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
EXPECT_EQ("Routing Handler", res->body);
EXPECT_EQ(1, res->get_header_value_count("PRE_ROUTING"));
EXPECT_EQ(1U, res->get_header_value_count("PRE_ROUTING"));
EXPECT_EQ("on", res->get_header_value("PRE_ROUTING"));
EXPECT_EQ(1, res->get_header_value_count("POST_ROUTING"));
EXPECT_EQ(1U, res->get_header_value_count("POST_ROUTING"));
EXPECT_EQ("on", res->get_header_value("POST_ROUTING"));
}
@@ -1235,8 +1246,8 @@ TEST(RoutingHandlerTest, PreRoutingHandler) {
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
EXPECT_EQ("Hello World!\n", res->body);
EXPECT_EQ(0, res->get_header_value_count("PRE_ROUTING"));
EXPECT_EQ(0, res->get_header_value_count("POST_ROUTING"));
EXPECT_EQ(0U, res->get_header_value_count("PRE_ROUTING"));
EXPECT_EQ(0U, res->get_header_value_count("POST_ROUTING"));
}
{
@@ -1246,8 +1257,8 @@ TEST(RoutingHandlerTest, PreRoutingHandler) {
ASSERT_TRUE(res);
EXPECT_EQ(404, res->status);
EXPECT_EQ("Error", res->body);
EXPECT_EQ(0, res->get_header_value_count("PRE_ROUTING"));
EXPECT_EQ(0, res->get_header_value_count("POST_ROUTING"));
EXPECT_EQ(0U, res->get_header_value_count("PRE_ROUTING"));
EXPECT_EQ(0U, res->get_header_value_count("POST_ROUTING"));
}
svr.stop();
@@ -1306,31 +1317,31 @@ protected:
.Get("/http_response_splitting",
[&](const Request & /*req*/, Response &res) {
res.set_header("a", "1\r\nSet-Cookie: a=1");
EXPECT_EQ(0, res.headers.size());
EXPECT_EQ(0U, res.headers.size());
EXPECT_FALSE(res.has_header("a"));
res.set_header("a", "1\nSet-Cookie: a=1");
EXPECT_EQ(0, res.headers.size());
EXPECT_EQ(0U, res.headers.size());
EXPECT_FALSE(res.has_header("a"));
res.set_header("a", "1\rSet-Cookie: a=1");
EXPECT_EQ(0, res.headers.size());
EXPECT_EQ(0U, res.headers.size());
EXPECT_FALSE(res.has_header("a"));
res.set_header("a\r\nb", "0");
EXPECT_EQ(0, res.headers.size());
EXPECT_EQ(0U, res.headers.size());
EXPECT_FALSE(res.has_header("a"));
res.set_header("a\rb", "0");
EXPECT_EQ(0, res.headers.size());
EXPECT_EQ(0U, res.headers.size());
EXPECT_FALSE(res.has_header("a"));
res.set_header("a\nb", "0");
EXPECT_EQ(0, res.headers.size());
EXPECT_EQ(0U, res.headers.size());
EXPECT_FALSE(res.has_header("a"));
res.set_redirect("1\r\nSet-Cookie: a=1");
EXPECT_EQ(0, res.headers.size());
EXPECT_EQ(0U, res.headers.size());
EXPECT_FALSE(res.has_header("Location"));
})
.Get("/slow",
@@ -1648,7 +1659,7 @@ protected:
} else {
std::string body;
content_reader([&](const char *data, size_t data_length) {
EXPECT_EQ(data_length, 7);
EXPECT_EQ(7U, data_length);
body.append(data, data_length);
return true;
});
@@ -1696,7 +1707,7 @@ protected:
})
.Post("/binary",
[&](const Request &req, Response &res) {
EXPECT_EQ(4, req.body.size());
EXPECT_EQ(4U, req.body.size());
EXPECT_EQ("application/octet-stream",
req.get_header_value("Content-Type"));
EXPECT_EQ("4", req.get_header_value("Content-Length"));
@@ -1704,7 +1715,7 @@ protected:
})
.Put("/binary",
[&](const Request &req, Response &res) {
EXPECT_EQ(4, req.body.size());
EXPECT_EQ(4U, req.body.size());
EXPECT_EQ("application/octet-stream",
req.get_header_value("Content-Type"));
EXPECT_EQ("4", req.get_header_value("Content-Length"));
@@ -1712,7 +1723,7 @@ protected:
})
.Patch("/binary",
[&](const Request &req, Response &res) {
EXPECT_EQ(4, req.body.size());
EXPECT_EQ(4U, req.body.size());
EXPECT_EQ("application/octet-stream",
req.get_header_value("Content-Type"));
EXPECT_EQ("4", req.get_header_value("Content-Length"));
@@ -1720,7 +1731,7 @@ protected:
})
.Delete("/binary",
[&](const Request &req, Response &res) {
EXPECT_EQ(4, req.body.size());
EXPECT_EQ(4U, req.body.size());
EXPECT_EQ("application/octet-stream",
req.get_header_value("Content-Type"));
EXPECT_EQ("4", req.get_header_value("Content-Length"));
@@ -1800,7 +1811,7 @@ TEST_F(ServerTest, GetMethod200) {
EXPECT_EQ(200, res->status);
EXPECT_EQ("OK", res->reason);
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
EXPECT_EQ(1, res->get_header_value_count("Content-Type"));
EXPECT_EQ(1U, res->get_header_value_count("Content-Type"));
EXPECT_EQ("Hello World!", res->body);
}
@@ -1810,7 +1821,7 @@ TEST_F(ServerTest, GetMethod200withPercentEncoding) {
EXPECT_EQ("HTTP/1.1", res->version);
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(1U, res->get_header_value_count("Content-Type"));
EXPECT_EQ("Hello World!", res->body);
}
@@ -2088,25 +2099,25 @@ TEST_F(ServerTest, Binary) {
"application/octet-stream");
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
ASSERT_EQ(4, res->body.size());
ASSERT_EQ(4U, res->body.size());
res = cli_.Put("/binary", binary.data(), binary.size(),
"application/octet-stream");
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
ASSERT_EQ(4, res->body.size());
ASSERT_EQ(4U, res->body.size());
res = cli_.Patch("/binary", binary.data(), binary.size(),
"application/octet-stream");
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
ASSERT_EQ(4, res->body.size());
ASSERT_EQ(4U, res->body.size());
res = cli_.Delete("/binary", binary.data(), binary.size(),
"application/octet-stream");
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
ASSERT_EQ(4, res->body.size());
ASSERT_EQ(4U, res->body.size());
}
TEST_F(ServerTest, BinaryString) {
@@ -2115,22 +2126,22 @@ TEST_F(ServerTest, BinaryString) {
auto res = cli_.Post("/binary", binary, "application/octet-stream");
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
ASSERT_EQ(4, res->body.size());
ASSERT_EQ(4U, res->body.size());
res = cli_.Put("/binary", binary, "application/octet-stream");
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
ASSERT_EQ(4, res->body.size());
ASSERT_EQ(4U, res->body.size());
res = cli_.Patch("/binary", binary, "application/octet-stream");
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
ASSERT_EQ(4, res->body.size());
ASSERT_EQ(4U, res->body.size());
res = cli_.Delete("/binary", binary, "application/octet-stream");
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
ASSERT_EQ(4, res->body.size());
ASSERT_EQ(4U, res->body.size());
}
TEST_F(ServerTest, EmptyRequest) {
@@ -2446,7 +2457,7 @@ TEST_F(ServerTest, GetStreamedWithRangeMultipart) {
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());
EXPECT_EQ(269U, res->body.size());
}
TEST_F(ServerTest, GetStreamedEndless) {
@@ -2533,7 +2544,7 @@ TEST_F(ServerTest, GetWithRangeMultipart) {
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());
EXPECT_EQ(269U, res->body.size());
}
TEST_F(ServerTest, GetWithRangeMultipartOffsetGreaterThanContent) {
@@ -2864,6 +2875,54 @@ TEST(GzipDecompressor, ChunkedDecompression) {
}
ASSERT_EQ(data, decompressed_data);
}
#ifdef _WIN32
TEST(GzipDecompressor, LargeRandomData) {
// prepare large random data that is difficult to be compressed and is
// expected to have large size even when compressed
std::random_device seed_gen;
std::mt19937 random(seed_gen());
constexpr auto large_size_byte = 4294967296UL; // 4GiB
constexpr auto data_size = large_size_byte + 134217728UL; // + 128MiB
std::vector<std::uint32_t> data(data_size / sizeof(std::uint32_t));
std::generate(data.begin(), data.end(), [&]() { return random(); });
// compress data over 4GiB
std::string compressed_data;
compressed_data.reserve(large_size_byte + 536870912UL); // + 512MiB reserved
httplib::detail::gzip_compressor compressor;
auto result = compressor.compress(reinterpret_cast<const char *>(data.data()),
data.size() * sizeof(std::uint32_t), true,
[&](const char *data, size_t size) {
compressed_data.insert(
compressed_data.size(), data, size);
return true;
});
ASSERT_TRUE(result);
// FIXME: compressed data size is expected to be greater than 4GiB,
// but there is no guarantee
// ASSERT_TRUE(compressed_data.size() >= large_size_byte);
// decompress data over 4GiB
std::string decompressed_data;
decompressed_data.reserve(data_size);
httplib::detail::gzip_decompressor decompressor;
result = decompressor.decompress(
compressed_data.data(), compressed_data.size(),
[&](const char *data, size_t size) {
decompressed_data.insert(decompressed_data.size(), data, size);
return true;
});
ASSERT_TRUE(result);
// compare
ASSERT_EQ(data_size, decompressed_data.size());
ASSERT_TRUE(std::memcmp(data.data(), decompressed_data.data(), data_size) ==
0);
}
#endif
#endif
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
@@ -3124,7 +3183,7 @@ TEST_F(ServerTest, GzipWithContentReceiver) {
std::string body;
auto res = cli_.Get("/compress", headers,
[&](const char *data, uint64_t data_length) {
EXPECT_EQ(data_length, 100);
EXPECT_EQ(100U, data_length);
body.append(data, data_length);
return true;
});
@@ -3150,14 +3209,14 @@ TEST_F(ServerTest, GzipWithoutDecompressing) {
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(33, res->body.size());
EXPECT_EQ(33U, res->body.size());
EXPECT_EQ(200, res->status);
}
TEST_F(ServerTest, GzipWithContentReceiverWithoutAcceptEncoding) {
std::string body;
auto res = cli_.Get("/compress", [&](const char *data, uint64_t data_length) {
EXPECT_EQ(data_length, 100);
EXPECT_EQ(100U, data_length);
body.append(data, data_length);
return true;
});
@@ -3193,7 +3252,7 @@ TEST_F(ServerTest, NoGzipWithContentReceiver) {
std::string body;
auto res = cli_.Get("/nocompress", headers,
[&](const char *data, uint64_t data_length) {
EXPECT_EQ(data_length, 100);
EXPECT_EQ(100U, data_length);
body.append(data, data_length);
return true;
});
@@ -3289,14 +3348,14 @@ TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
// Only space and horizontal tab are whitespace. Make sure other whitespace-
// like characters are not treated the same - use vertical tab and escape.
const std::string req = "GET /validate-ws-in-headers HTTP/1.1\r\n"
"foo: \t \v bar \e\t \r\n"
"foo: \t \v bar \x1B\t \r\n"
"Connection: close\r\n"
"\r\n";
ASSERT_TRUE(send_request(5, req));
svr.stop();
t.join();
EXPECT_EQ(header_value, "\v bar \e");
EXPECT_EQ(header_value, "\v bar \x1B");
}
// Sends a raw request and verifies that there isn't a crash or exception.
@@ -3448,7 +3507,7 @@ TEST(ServerStopTest, StopServerWithChunkedTransmission) {
res.set_chunked_content_provider("text/event-stream", [](size_t offset,
DataSink &sink) {
char buffer[27];
auto size = static_cast<size_t>(sprintf(buffer, "data:%ld\n\n", offset));
auto size = static_cast<size_t>(sprintf(buffer, "data:%zd\n\n", offset));
auto ret = sink.write(buffer, size);
EXPECT_TRUE(ret);
std::this_thread::sleep_for(std::chrono::seconds(1));
@@ -3705,19 +3764,19 @@ TEST(ErrorHandlerWithContentProviderTest, ErrorHandler) {
TEST(GetWithParametersTest, GetWithParameters) {
Server svr;
svr.Get("/", [&](const Request &req, Response &res) {
svr.Get("/", [&](const Request &req, Response &) {
EXPECT_EQ("world", req.get_param_value("hello"));
EXPECT_EQ("world2", req.get_param_value("hello2"));
EXPECT_EQ("world3", req.get_param_value("hello3"));
});
svr.Get("/params", [&](const Request &req, Response &res) {
svr.Get("/params", [&](const Request &req, Response &) {
EXPECT_EQ("world", req.get_param_value("hello"));
EXPECT_EQ("world2", req.get_param_value("hello2"));
EXPECT_EQ("world3", req.get_param_value("hello3"));
});
svr.Get(R"(/resources/([a-z0-9\\-]+))", [&](const Request& req, Response& res) {
svr.Get(R"(/resources/([a-z0-9\\-]+))", [&](const Request &req, Response &) {
EXPECT_EQ("resource-id", req.matches[1]);
EXPECT_EQ("foo", req.get_param_value("param1"));
EXPECT_EQ("bar", req.get_param_value("param2"));
@@ -3800,7 +3859,7 @@ TEST(GetWithParametersTest, GetWithParameters2) {
ASSERT_FALSE(svr.is_running());
}
TEST(ClientDefaultHeadersTest, DefaultHeaders) {
TEST(ClientDefaultHeadersTest, DefaultHeaders_Online) {
Client cli("httpbin.org");
cli.set_default_headers({make_range_header({{1, 10}})});
cli.set_connection_timeout(5);
@@ -4037,21 +4096,21 @@ TEST(SSLClientTest, UpdateCAStore) {
httplib_client.set_ca_cert_store(ca_store_2);
}
TEST(SSLClientTest, ServerNameIndication) {
TEST(SSLClientTest, ServerNameIndication_Online) {
SSLClient cli("httpbin.org", 443);
auto res = cli.Get("/get");
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
}
TEST(SSLClientTest, ServerCertificateVerification1) {
TEST(SSLClientTest, ServerCertificateVerification1_Online) {
SSLClient cli("google.com");
auto res = cli.Get("/");
ASSERT_TRUE(res);
ASSERT_EQ(301, res->status);
}
TEST(SSLClientTest, ServerCertificateVerification2) {
TEST(SSLClientTest, ServerCertificateVerification2_Online) {
SSLClient cli("google.com");
cli.enable_server_certificate_verification(true);
cli.set_ca_cert_path("hello");
@@ -4060,7 +4119,7 @@ TEST(SSLClientTest, ServerCertificateVerification2) {
EXPECT_EQ(Error::SSLLoadingCerts, res.error());
}
TEST(SSLClientTest, ServerCertificateVerification3) {
TEST(SSLClientTest, ServerCertificateVerification3_Online) {
SSLClient cli("google.com");
cli.set_ca_cert_path(CA_CERT_FILE);
auto res = cli.Get("/");
@@ -4093,7 +4152,7 @@ TEST(SSLClientTest, ServerCertificateVerification4) {
t.join();
}
TEST(SSLClientTest, WildcardHostNameMatch) {
TEST(SSLClientTest, WildcardHostNameMatch_Online) {
SSLClient cli("www.youtube.com");
cli.set_ca_cert_path(CA_CERT_FILE);
@@ -4338,7 +4397,7 @@ TEST(NoScheme, SimpleInterface) {
ASSERT_TRUE(cli.is_valid());
}
TEST(SendAPI, SimpleInterface) {
TEST(SendAPI, SimpleInterface_Online) {
Client cli("http://yahoo.com");
Request req;
@@ -4351,7 +4410,7 @@ TEST(SendAPI, SimpleInterface) {
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
TEST(YahooRedirectTest2, SimpleInterface) {
TEST(YahooRedirectTest2, SimpleInterface_Online) {
Client cli("http://yahoo.com");
auto res = cli.Get("/");
@@ -4365,7 +4424,7 @@ TEST(YahooRedirectTest2, SimpleInterface) {
EXPECT_EQ("https://yahoo.com/", res->location);
}
TEST(YahooRedirectTest3, SimpleInterface) {
TEST(YahooRedirectTest3, SimpleInterface_Online) {
Client cli("https://yahoo.com");
auto res = cli.Get("/");
@@ -4379,7 +4438,7 @@ TEST(YahooRedirectTest3, SimpleInterface) {
EXPECT_EQ("https://www.yahoo.com/", res->location);
}
TEST(YahooRedirectTest3, NewResultInterface) {
TEST(YahooRedirectTest3, NewResultInterface_Online) {
Client cli("https://yahoo.com");
auto res = cli.Get("/");
@@ -4404,20 +4463,20 @@ TEST(YahooRedirectTest3, NewResultInterface) {
}
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
TEST(DecodeWithChunkedEncoding, BrotliEncoding) {
TEST(DecodeWithChunkedEncoding, BrotliEncoding_Online) {
Client cli("https://cdnjs.cloudflare.com");
auto res =
cli.Get("/ajax/libs/jquery/3.5.1/jquery.js", {{"Accept-Encoding", "br"}});
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
EXPECT_EQ(287630, res->body.size());
EXPECT_EQ(287630U, res->body.size());
EXPECT_EQ("application/javascript; charset=utf-8",
res->get_header_value("Content-Type"));
}
#endif
TEST(HttpsToHttpRedirectTest, SimpleInterface) {
TEST(HttpsToHttpRedirectTest, SimpleInterface_Online) {
Client cli("https://nghttp2.org");
cli.set_follow_location(true);
auto res =
@@ -4428,7 +4487,7 @@ TEST(HttpsToHttpRedirectTest, SimpleInterface) {
EXPECT_EQ(200, res->status);
}
TEST(HttpsToHttpRedirectTest2, SimpleInterface) {
TEST(HttpsToHttpRedirectTest2, SimpleInterface_Online) {
Client cli("https://nghttp2.org");
cli.set_follow_location(true);
@@ -4441,7 +4500,7 @@ TEST(HttpsToHttpRedirectTest2, SimpleInterface) {
EXPECT_EQ(200, res->status);
}
TEST(HttpsToHttpRedirectTest3, SimpleInterface) {
TEST(HttpsToHttpRedirectTest3, SimpleInterface_Online) {
Client cli("https://nghttp2.org");
cli.set_follow_location(true);
@@ -4452,4 +4511,37 @@ TEST(HttpsToHttpRedirectTest3, SimpleInterface) {
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
}
TEST(HttpToHttpsRedirectTest, CertFile) {
Server svr;
ASSERT_TRUE(svr.is_valid());
svr.Get("/index", [&](const Request &, Response &res) {
res.set_redirect("https://127.0.0.1:1235/index");
svr.stop();
});
SSLServer ssl_svr(SERVER_CERT2_FILE, SERVER_PRIVATE_KEY_FILE);
ASSERT_TRUE(ssl_svr.is_valid());
ssl_svr.Get("/index", [&](const Request &, Response &res) {
res.set_content("test", "text/plain");
ssl_svr.stop();
});
thread t = thread([&]() { ASSERT_TRUE(svr.listen("127.0.0.1", PORT)); });
thread t2 = thread([&]() { ASSERT_TRUE(ssl_svr.listen("127.0.0.1", 1235)); });
std::this_thread::sleep_for(std::chrono::milliseconds(1));
Client cli("127.0.0.1", PORT);
cli.set_ca_cert_path(SERVER_CERT2_FILE);
cli.enable_server_certificate_verification(true);
cli.set_follow_location(true);
cli.set_connection_timeout(30);
auto res = cli.Get("/index");
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
t.join();
t2.join();
}
#endif