mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-20 11:29:38 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66f698fab6 | ||
|
|
b9a9df4d73 | ||
|
|
25aa3ca982 | ||
|
|
2d67211183 | ||
|
|
f4c5d94d74 | ||
|
|
63a96aeb20 | ||
|
|
bbb83d12c1 | ||
|
|
2d4b42b70b | ||
|
|
1919d08f71 | ||
|
|
824c02fcd3 | ||
|
|
2c0613f211 | ||
|
|
be45ff1ff1 | ||
|
|
803ebe1e20 | ||
|
|
ba685dbe48 | ||
|
|
49c4c2f9c1 | ||
|
|
58909f5917 | ||
|
|
5982b5c360 | ||
|
|
eb1fe5b191 | ||
|
|
5e01587ed6 | ||
|
|
5935d9fa59 | ||
|
|
5bb4c12c6b | ||
|
|
85637844c9 | ||
|
|
d043b18097 | ||
|
|
31bb13abd2 | ||
|
|
8728db7477 | ||
|
|
1c50ac3667 | ||
|
|
cf386f97fd | ||
|
|
b2203bb05a | ||
|
|
f5b806d995 | ||
|
|
3895210f19 | ||
|
|
d45250fd88 | ||
|
|
528cacdc0d | ||
|
|
ed1b6afa10 | ||
|
|
08fc7085e5 | ||
|
|
8333340e2c | ||
|
|
98a0887571 | ||
|
|
b0a189e50e | ||
|
|
776b3ffbf9 | ||
|
|
a061b97677 | ||
|
|
d359e3a5f7 | ||
|
|
5928e0af1a | ||
|
|
a5005789ff | ||
|
|
fae30af47d | ||
|
|
2feea0c9ab | ||
|
|
a2e4af54b7 | ||
|
|
d0b123be26 | ||
|
|
df138366e4 | ||
|
|
c49441ae64 | ||
|
|
e1506fa186 | ||
|
|
ad9fd3bd93 | ||
|
|
05e0253195 | ||
|
|
da26b517a3 | ||
|
|
2b7a968468 | ||
|
|
240cc85ccb | ||
|
|
129e2f00b8 | ||
|
|
da746c6e67 | ||
|
|
3451da940d | ||
|
|
38a6b3e69f | ||
|
|
d1037ee9fd | ||
|
|
2ece5f116b | ||
|
|
c2b6e4ac04 | ||
|
|
8674555b88 | ||
|
|
85327e19ae | ||
|
|
ed8efea98b | ||
|
|
1ccddd1b0b | ||
|
|
992f3dc690 | ||
|
|
402d47e2cd | ||
|
|
171fc2e353 | ||
|
|
ced4160d05 | ||
|
|
5b51aa6851 | ||
|
|
dc13cde820 | ||
|
|
b0af78e340 | ||
|
|
914c8860e8 | ||
|
|
dc6a72a0fd | ||
|
|
685533ba50 | ||
|
|
6e46ccb37c | ||
|
|
ac18b70a0f | ||
|
|
e1acb949e7 | ||
|
|
ab96f49766 | ||
|
|
7b3cea5317 | ||
|
|
26deffe0c6 | ||
|
|
e07c5fec01 | ||
|
|
6e473a7c5c | ||
|
|
c74129a1c2 | ||
|
|
18e750b4e7 | ||
|
|
bf7700d192 | ||
|
|
3da925d6fe | ||
|
|
319417f26d | ||
|
|
4c3b119dde | ||
|
|
6de8684328 | ||
|
|
ccc9a9b3f4 |
No files matched your search
@@ -3,6 +3,7 @@ tags
|
|||||||
example/server
|
example/server
|
||||||
example/client
|
example/client
|
||||||
example/hello
|
example/hello
|
||||||
|
example/simplecli
|
||||||
example/simplesvr
|
example/simplesvr
|
||||||
example/benchmark
|
example/benchmark
|
||||||
example/redirect
|
example/redirect
|
||||||
|
|||||||
-14
@@ -1,14 +0,0 @@
|
|||||||
# Environment
|
|
||||||
language: cpp
|
|
||||||
os:
|
|
||||||
- linux
|
|
||||||
- osx
|
|
||||||
|
|
||||||
# Compiler selection
|
|
||||||
compiler:
|
|
||||||
- clang
|
|
||||||
|
|
||||||
# Build/test steps
|
|
||||||
script:
|
|
||||||
- cd ${TRAVIS_BUILD_DIR}/test
|
|
||||||
- make all
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.7.0)
|
|
||||||
project(httplib)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
|
||||||
|
|
||||||
# Include
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
include(ExternalProject)
|
|
||||||
|
|
||||||
add_library(${PROJECT_NAME} INTERFACE)
|
|
||||||
target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_11)
|
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} INTERFACE
|
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
|
||||||
$<INSTALL_INTERFACE:include>)
|
|
||||||
|
|
||||||
install(TARGETS ${PROJECT_NAME} EXPORT httplibConfig
|
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
|
||||||
|
|
||||||
install(FILES httplib.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
|
|
||||||
|
|
||||||
install(EXPORT httplibConfig DESTINATION share/httplib/cmake)
|
|
||||||
|
|
||||||
export(TARGETS ${PROJECT_NAME} FILE httplibConfig.cmake)
|
|
||||||
|
|
||||||
#add_subdirectory(example)
|
|
||||||
#add_subdirectory(test)
|
|
||||||
@@ -2,13 +2,14 @@ cpp-httplib
|
|||||||
===========
|
===========
|
||||||
|
|
||||||
[](https://github.com/yhirose/cpp-httplib/actions)
|
[](https://github.com/yhirose/cpp-httplib/actions)
|
||||||
[](https://travis-ci.org/yhirose/cpp-httplib)
|
|
||||||
[](https://ci.appveyor.com/project/yhirose/cpp-httplib)
|
[](https://ci.appveyor.com/project/yhirose/cpp-httplib)
|
||||||
|
|
||||||
A C++11 single-file header-only cross platform HTTP/HTTPS library.
|
A C++11 single-file header-only cross platform HTTP/HTTPS library.
|
||||||
|
|
||||||
It's extremely easy to setup. Just include **httplib.h** file in your code!
|
It's extremely easy to setup. Just include **httplib.h** file in your code!
|
||||||
|
|
||||||
|
For Windows users: Please read [this note](https://github.com/yhirose/cpp-httplib#windows).
|
||||||
|
|
||||||
Server Example
|
Server Example
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
@@ -30,6 +31,16 @@ int main(void)
|
|||||||
res.set_content(numbers, "text/plain");
|
res.set_content(numbers, "text/plain");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
svr.Get("/body-header-param", [](const Request& req, Response& res) {
|
||||||
|
if (req.has_header("Content-Length")) {
|
||||||
|
auto val = req.get_header_value("Content-Length");
|
||||||
|
}
|
||||||
|
if (req.has_param("key")) {
|
||||||
|
auto val = req.get_param_value("key");
|
||||||
|
}
|
||||||
|
res.set_content(req.body, "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
svr.Get("/stop", [&](const Request& req, Response& res) {
|
svr.Get("/stop", [&](const Request& req, Response& res) {
|
||||||
svr.stop();
|
svr.stop();
|
||||||
});
|
});
|
||||||
@@ -160,16 +171,17 @@ svr.Post("/content_receiver",
|
|||||||
### Send content with Content provider
|
### Send content with Content provider
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
const uint64_t DATA_CHUNK_SIZE = 4;
|
const size_t DATA_CHUNK_SIZE = 4;
|
||||||
|
|
||||||
svr.Get("/stream", [&](const Request &req, Response &res) {
|
svr.Get("/stream", [&](const Request &req, Response &res) {
|
||||||
auto data = new std::string("abcdefg");
|
auto data = new std::string("abcdefg");
|
||||||
|
|
||||||
res.set_content_provider(
|
res.set_content_provider(
|
||||||
data->size(), // Content length
|
data->size(), // Content length
|
||||||
[data](uint64_t offset, uint64_t length, DataSink &sink) {
|
[data](size_t offset, size_t length, DataSink &sink) {
|
||||||
const auto &d = *data;
|
const auto &d = *data;
|
||||||
sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
|
sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
|
||||||
|
return true; // return 'false' if you want to cancel the process.
|
||||||
},
|
},
|
||||||
[data] { delete data; });
|
[data] { delete data; });
|
||||||
});
|
});
|
||||||
@@ -180,11 +192,12 @@ svr.Get("/stream", [&](const Request &req, Response &res) {
|
|||||||
```cpp
|
```cpp
|
||||||
svr.Get("/chunked", [&](const Request& req, Response& res) {
|
svr.Get("/chunked", [&](const Request& req, Response& res) {
|
||||||
res.set_chunked_content_provider(
|
res.set_chunked_content_provider(
|
||||||
[](uint64_t offset, DataSink &sink) {
|
[](size_t offset, DataSink &sink) {
|
||||||
sink.write("123", 3);
|
sink.write("123", 3);
|
||||||
sink.write("345", 3);
|
sink.write("345", 3);
|
||||||
sink.write("789", 3);
|
sink.write("789", 3);
|
||||||
sink.done();
|
sink.done();
|
||||||
|
return true; // return 'false' if you want to cancel the process.
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -248,14 +261,13 @@ svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
|
|||||||
Client Example
|
Client Example
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
### GET
|
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
#include <httplib.h>
|
#include <httplib.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
|
// IMPORTANT: 1st parameter must be a hostname or an IP adress string.
|
||||||
httplib::Client cli("localhost", 1234);
|
httplib::Client cli("localhost", 1234);
|
||||||
|
|
||||||
auto res = cli.Get("/hi");
|
auto res = cli.Get("/hi");
|
||||||
@@ -280,7 +292,7 @@ auto res = cli.Get("/hi", headers);
|
|||||||
std::string body;
|
std::string body;
|
||||||
|
|
||||||
auto res = cli.Get("/large-data",
|
auto res = cli.Get("/large-data",
|
||||||
[&](const char *data, uint64_t data_length) {
|
[&](const char *data, size_t data_length) {
|
||||||
body.append(data, data_length);
|
body.append(data, data_length);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@@ -353,6 +365,35 @@ res = cli.Options("/resource/foo");
|
|||||||
```c++
|
```c++
|
||||||
cli.set_timeout_sec(5); // timeouts in 5 seconds
|
cli.set_timeout_sec(5); // timeouts in 5 seconds
|
||||||
```
|
```
|
||||||
|
### Receive content with Content receiver
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::string body;
|
||||||
|
auto res = cli.Get(
|
||||||
|
"/stream", Headers(),
|
||||||
|
[&](const Response &response) {
|
||||||
|
EXPECT_EQ(200, response.status);
|
||||||
|
return true; // return 'false' if you want to cancel the request.
|
||||||
|
},
|
||||||
|
[&](const char *data, size_t data_length) {
|
||||||
|
body.append(data, data_length);
|
||||||
|
return true; // return 'false' if you want to cancel the request.
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Send content with Content provider
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::string body = ...;
|
||||||
|
auto res = cli_.Post(
|
||||||
|
"/stream", body.size(),
|
||||||
|
[](size_t offset, size_t length, DataSink &sink) {
|
||||||
|
sink.write(body.data() + offset, length);
|
||||||
|
return true; // return 'false' if you want to cancel the request.
|
||||||
|
},
|
||||||
|
"text/plain");
|
||||||
|
```
|
||||||
|
|
||||||
### With Progress Callback
|
### With Progress Callback
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -363,7 +404,7 @@ std::shared_ptr<httplib::Response> res =
|
|||||||
cli.Get("/", [](uint64_t len, uint64_t total) {
|
cli.Get("/", [](uint64_t len, uint64_t total) {
|
||||||
printf("%lld / %lld bytes => %d%% complete\n",
|
printf("%lld / %lld bytes => %d%% complete\n",
|
||||||
len, total,
|
len, total,
|
||||||
(int)((len/total)*100));
|
(int)(len*100/total));
|
||||||
return true; // return 'false' if you want to cancel the request.
|
return true; // return 'false' if you want to cancel the request.
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -426,6 +467,15 @@ Get(requests, "/get-request2");
|
|||||||
Post(requests, "/post-request1", "text", "text/plain");
|
Post(requests, "/post-request1", "text", "text/plain");
|
||||||
Post(requests, "/post-request2", "text", "text/plain");
|
Post(requests, "/post-request2", "text", "text/plain");
|
||||||
|
|
||||||
|
const size_t DATA_CHUNK_SIZE = 4;
|
||||||
|
std::string data("abcdefg");
|
||||||
|
Post(requests, "/post-request-with-content-provider",
|
||||||
|
data.size(),
|
||||||
|
[&](size_t offset, size_t length, DataSink &sink){
|
||||||
|
sink.write(&data[offset], std::min(length, DATA_CHUNK_SIZE));
|
||||||
|
},
|
||||||
|
"text/plain");
|
||||||
|
|
||||||
std::vector<Response> responses;
|
std::vector<Response> responses;
|
||||||
if (cli.send(requests, responses)) {
|
if (cli.send(requests, responses)) {
|
||||||
for (const auto& res: responses) {
|
for (const auto& res: responses) {
|
||||||
@@ -475,7 +525,7 @@ cli.enable_server_certificate_verification(true);
|
|||||||
Zlib Support
|
Zlib Support
|
||||||
------------
|
------------
|
||||||
|
|
||||||
'gzip' compression is available with `CPPHTTPLIB_ZLIB_SUPPORT`.
|
'gzip' compression is available with `CPPHTTPLIB_ZLIB_SUPPORT`. `libz` should be linked.
|
||||||
|
|
||||||
The server applies gzip compression to the following MIME type contents:
|
The server applies gzip compression to the following MIME type contents:
|
||||||
|
|
||||||
@@ -505,8 +555,27 @@ httplib.h httplib.cc
|
|||||||
NOTE
|
NOTE
|
||||||
----
|
----
|
||||||
|
|
||||||
|
### g++
|
||||||
|
|
||||||
g++ 4.8 and below cannot build this library since `<regex>` in the versions are [broken](https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions).
|
g++ 4.8 and below cannot build this library since `<regex>` in the versions are [broken](https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions).
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32_LEAN_AND_MEAN` beforehand.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <httplib.h>
|
||||||
|
#include <Windows.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <httplib.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: Cygwin on Windows is not supported.
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@@ -515,43 +584,4 @@ MIT license (© 2020 Yuji Hirose)
|
|||||||
Special Thanks To
|
Special Thanks To
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
The following folks made great contributions to polish this library to totally another level from a simple toy!
|
[These folks](https://github.com/yhirose/cpp-httplib/graphs/contributors) made great contributions to polish this library to totally another level from a simple toy!
|
||||||
|
|
||||||
* [Zefz](https://github.com/Zefz)
|
|
||||||
* [PixlRainbow](https://github.com/PixlRainbow)
|
|
||||||
* [sgraham](https://github.com/sgraham)
|
|
||||||
* [mrexodia](https://github.com/mrexodia)
|
|
||||||
* [hyperxor](https://github.com/hyperxor)
|
|
||||||
* [omaralvarez](https://github.com/omaralvarez)
|
|
||||||
* [vvanelslande](https://github.com/vvanelslande)
|
|
||||||
* [underscorediscovery](https://github.com/underscorediscovery)
|
|
||||||
* [sux2mfgj](https://github.com/sux2mfgj)
|
|
||||||
* [matvore](https://github.com/matvore)
|
|
||||||
* [intmain-io](https://github.com/intmain)
|
|
||||||
* [davidgfnet](https://github.com/davidgfnet)
|
|
||||||
* [crtxcr](https://github.com/crtxcr)
|
|
||||||
* [const-volatile](https://github.com/const)
|
|
||||||
* [aguadoenzo](https://github.com/aguadoenzo)
|
|
||||||
* [TheMaverickProgrammer](https://github.com/TheMaverickProgrammer)
|
|
||||||
* [vdudouyt](https://github.com/vdudouyt)
|
|
||||||
* [stupedama](https://github.com/stupedama)
|
|
||||||
* [rockwotj](https://github.com/rockwotj)
|
|
||||||
* [marknelson](https://github.com/marknelson)
|
|
||||||
* [jaspervandeven](https://github.com/jaspervandeven)
|
|
||||||
* [hans-erickson](https://github.com/hans)
|
|
||||||
* [ha11owed](https://github.com/ha11owed)
|
|
||||||
* [gulrak](https://github.com/gulrak)
|
|
||||||
* [dolphineye](https://github.com/dolphineye)
|
|
||||||
* [danielzehe](https://github.com/danielzehe)
|
|
||||||
* [batist73](https://github.com/batist73)
|
|
||||||
* [barryam3](https://github.com/barryam3)
|
|
||||||
* [adikabintang](https://github.com/adikabintang)
|
|
||||||
* [aaronalbers](https://github.com/aaronalbers)
|
|
||||||
* [Whitetigerswt](https://github.com/Whitetigerswt)
|
|
||||||
* [TangHuaiZhe](https://github.com/TangHuaiZhe)
|
|
||||||
* [Sil3ntStorm](https://github.com/Sil3ntStorm)
|
|
||||||
* [MannyClicks](https://github.com/MannyClicks)
|
|
||||||
* [DraTeots](https://github.com/DraTeots)
|
|
||||||
* [BastienDurel](https://github.com/BastienDurel)
|
|
||||||
* [vitalyster](https://github.com/vitalyster)
|
|
||||||
* [trollixx](https://github.com/trollixx)
|
|
||||||
+9
-4
@@ -1,9 +1,14 @@
|
|||||||
version: 1.0.{build}
|
image:
|
||||||
image: Visual Studio 2017
|
- Visual Studio 2019
|
||||||
|
|
||||||
|
platform:
|
||||||
|
- x64
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- cmd: >-
|
- cmd: >-
|
||||||
cd test
|
cd test
|
||||||
|
|
||||||
msbuild.exe test.sln /verbosity:minimal /t:Build /p:Configuration=Debug;Platform=Win32
|
msbuild.exe test.sln /verbosity:minimal /t:Build /p:Configuration=Release;Platform=%PLATFORM%
|
||||||
|
|
||||||
test_script:
|
test_script:
|
||||||
- cmd: Debug\test.exe
|
- cmd: x64\Release\test.exe
|
||||||
+5
-2
@@ -5,7 +5,7 @@ OPENSSL_DIR = /usr/local/opt/openssl
|
|||||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||||
|
|
||||||
all: server client hello simplesvr upload redirect sse benchmark
|
all: server client hello simplecli simplesvr upload redirect sse benchmark
|
||||||
|
|
||||||
server : server.cc ../httplib.h Makefile
|
server : server.cc ../httplib.h Makefile
|
||||||
$(CXX) -o server $(CXXFLAGS) server.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
$(CXX) -o server $(CXXFLAGS) server.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||||
@@ -16,6 +16,9 @@ client : client.cc ../httplib.h Makefile
|
|||||||
hello : hello.cc ../httplib.h Makefile
|
hello : hello.cc ../httplib.h Makefile
|
||||||
$(CXX) -o hello $(CXXFLAGS) hello.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
$(CXX) -o hello $(CXXFLAGS) hello.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||||
|
|
||||||
|
simplecli : simplecli.cc ../httplib.h Makefile
|
||||||
|
$(CXX) -o simplecli $(CXXFLAGS) simplecli.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||||
|
|
||||||
simplesvr : simplesvr.cc ../httplib.h Makefile
|
simplesvr : simplesvr.cc ../httplib.h Makefile
|
||||||
$(CXX) -o simplesvr $(CXXFLAGS) simplesvr.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
$(CXX) -o simplesvr $(CXXFLAGS) simplesvr.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||||
|
|
||||||
@@ -36,4 +39,4 @@ pem:
|
|||||||
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm server client hello simplesvr upload redirect sse benchmark *.pem
|
rm server client hello simplecli simplesvr upload redirect sse benchmark *.pem
|
||||||
+1
-1
@@ -15,5 +15,5 @@ int main(void) {
|
|||||||
res.set_content("Hello World!", "text/plain");
|
res.set_content("Hello World!", "text/plain");
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.listen("localhost", 1234);
|
svr.listen("localhost", 8080);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
//
|
||||||
|
// simplecli.cc
|
||||||
|
//
|
||||||
|
// Copyright (c) 2019 Yuji Hirose. All rights reserved.
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <httplib.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#define CA_CERT_FILE "./ca-bundle.crt"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
|
auto res = httplib::Client2("https://localhost:8080")
|
||||||
|
.set_ca_cert_path(CA_CERT_FILE)
|
||||||
|
// .enable_server_certificate_verification(true)
|
||||||
|
.Get("/hi");
|
||||||
|
#else
|
||||||
|
auto res = httplib::Client2("http://localhost:8080").Get("/hi");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
cout << res->status << endl;
|
||||||
|
cout << res->get_header_value("Content-Type") << endl;
|
||||||
|
cout << res->body << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -127,7 +127,7 @@ int main(int argc, const char **argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
cout << "The server started at port " << port << "...";
|
cout << "The server started at port " << port << "..." << endl;
|
||||||
|
|
||||||
svr.listen("localhost", port);
|
svr.listen("localhost", port);
|
||||||
|
|
||||||
|
|||||||
+8
-4
@@ -80,15 +80,19 @@ int main(void) {
|
|||||||
svr.Get("/event1", [&](const Request & /*req*/, Response &res) {
|
svr.Get("/event1", [&](const Request & /*req*/, Response &res) {
|
||||||
cout << "connected to event1..." << endl;
|
cout << "connected to event1..." << endl;
|
||||||
res.set_header("Content-Type", "text/event-stream");
|
res.set_header("Content-Type", "text/event-stream");
|
||||||
res.set_chunked_content_provider(
|
res.set_chunked_content_provider([&](size_t /*offset*/, DataSink &sink) {
|
||||||
[&](uint64_t /*offset*/, DataSink &sink) { ed.wait_event(&sink); });
|
ed.wait_event(&sink);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.Get("/event2", [&](const Request & /*req*/, Response &res) {
|
svr.Get("/event2", [&](const Request & /*req*/, Response &res) {
|
||||||
cout << "connected to event2..." << endl;
|
cout << "connected to event2..." << endl;
|
||||||
res.set_header("Content-Type", "text/event-stream");
|
res.set_header("Content-Type", "text/event-stream");
|
||||||
res.set_chunked_content_provider(
|
res.set_chunked_content_provider([&](size_t /*offset*/, DataSink &sink) {
|
||||||
[&](uint64_t /*offset*/, DataSink &sink) { ed.wait_event(&sink); });
|
ed.wait_event(&sink);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
thread t([&] {
|
thread t([&] {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
#CXX = clang++
|
#CXX = clang++
|
||||||
CXXFLAGS = -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits
|
CXXFLAGS = -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion
|
||||||
OPENSSL_DIR = /usr/local/opt/openssl
|
OPENSSL_DIR = /usr/local/opt/openssl
|
||||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||||
|
|||||||
+14
-1
@@ -38,6 +38,14 @@
|
|||||||
// when it's fused.
|
// when it's fused.
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wsign-conversion"
|
||||||
|
#elif __GNUC__
|
||||||
|
#pragma gcc diagnostic push
|
||||||
|
#pragma gcc diagnostic ignored "-Wsign-conversion"
|
||||||
|
#endif
|
||||||
|
|
||||||
// The following lines pull in the real gtest *.cc files.
|
// The following lines pull in the real gtest *.cc files.
|
||||||
// Copyright 2005, Google Inc.
|
// Copyright 2005, Google Inc.
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
@@ -9039,7 +9047,6 @@ void HasNewFatalFailureHelper::ReportTestPartResult(
|
|||||||
//
|
//
|
||||||
// Author: wan@google.com (Zhanyong Wan)
|
// Author: wan@google.com (Zhanyong Wan)
|
||||||
|
|
||||||
|
|
||||||
namespace testing {
|
namespace testing {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
@@ -9116,3 +9123,9 @@ const char* TypedTestCasePState::VerifyRegisteredTestNames(
|
|||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace testing
|
} // namespace testing
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#elif __GNUC__
|
||||||
|
#pragma gcc diagnostic pop
|
||||||
|
#endif
|
||||||
+15
-1
@@ -59,6 +59,14 @@
|
|||||||
#pragma warning(disable : 4996)
|
#pragma warning(disable : 4996)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wsign-compare"
|
||||||
|
#elif __GNUC__
|
||||||
|
#pragma gcc diagnostic push
|
||||||
|
#pragma gcc diagnostic ignored "-Wsign-compare"
|
||||||
|
#endif
|
||||||
|
|
||||||
// Copyright 2005, Google Inc.
|
// Copyright 2005, Google Inc.
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
@@ -7311,7 +7319,7 @@ inline const char* SkipComma(const char* str) {
|
|||||||
// the entire string if it contains no comma.
|
// the entire string if it contains no comma.
|
||||||
inline String GetPrefixUntilComma(const char* str) {
|
inline String GetPrefixUntilComma(const char* str) {
|
||||||
const char* comma = strchr(str, ',');
|
const char* comma = strchr(str, ',');
|
||||||
return comma == NULL ? String(str) : String(str, comma - str);
|
return comma == NULL ? String(str) : String(str, static_cast<size_t>(comma - str));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeParameterizedTest<Fixture, TestSel, Types>::Register()
|
// TypeParameterizedTest<Fixture, TestSel, Types>::Register()
|
||||||
@@ -19553,4 +19561,10 @@ bool StaticAssertTypeEq() {
|
|||||||
#pragma warning( pop )
|
#pragma warning( pop )
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#elif __GNUC__
|
||||||
|
#pragma gcc diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // GTEST_INCLUDE_GTEST_GTEST_H_
|
#endif // GTEST_INCLUDE_GTEST_GTEST_H_
|
||||||
+582
-69
@@ -77,6 +77,21 @@ TEST(ParseQueryTest, ParseQueryString) {
|
|||||||
EXPECT_EQ("val3", dic.find("key3")->second);
|
EXPECT_EQ("val3", dic.find("key3")->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ParamsToQueryTest, ConvertParamsToQuery) {
|
||||||
|
Params dic;
|
||||||
|
|
||||||
|
EXPECT_EQ(detail::params_to_query_str(dic), "");
|
||||||
|
|
||||||
|
dic.emplace("key1", "val1");
|
||||||
|
|
||||||
|
EXPECT_EQ(detail::params_to_query_str(dic), "key1=val1");
|
||||||
|
|
||||||
|
dic.emplace("key2", "val2");
|
||||||
|
dic.emplace("key3", "val3");
|
||||||
|
|
||||||
|
EXPECT_EQ(detail::params_to_query_str(dic), "key1=val1&key2=val2&key3=val3");
|
||||||
|
}
|
||||||
|
|
||||||
TEST(GetHeaderValueTest, DefaultValue) {
|
TEST(GetHeaderValueTest, DefaultValue) {
|
||||||
Headers headers = {{"Dummy", "Dummy"}};
|
Headers headers = {{"Dummy", "Dummy"}};
|
||||||
auto val = detail::get_header_value(headers, "Content-Type", 0, "text/plain");
|
auto val = detail::get_header_value(headers, "Content-Type", 0, "text/plain");
|
||||||
@@ -548,15 +563,17 @@ TEST(DigestAuthTest, FromHTTPWatch) {
|
|||||||
for (auto path : paths) {
|
for (auto path : paths) {
|
||||||
auto res = cli.Get(path.c_str());
|
auto res = cli.Get(path.c_str());
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ(400, res->status);
|
EXPECT_EQ(401, res->status);
|
||||||
}
|
}
|
||||||
|
|
||||||
cli.set_digest_auth("bad", "world");
|
// NOTE: Until httpbin.org fixes issue #46, the following test is commented
|
||||||
for (auto path : paths) {
|
// out. Plese see https://httpbin.org/digest-auth/auth/hello/world
|
||||||
auto res = cli.Get(path.c_str());
|
// cli.set_digest_auth("bad", "world");
|
||||||
ASSERT_TRUE(res != nullptr);
|
// for (auto path : paths) {
|
||||||
EXPECT_EQ(400, res->status);
|
// auto res = cli.Get(path.c_str());
|
||||||
}
|
// ASSERT_TRUE(res != nullptr);
|
||||||
|
// EXPECT_EQ(400, res->status);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -640,9 +657,82 @@ TEST(HttpsToHttpRedirectTest, Redirect) {
|
|||||||
auto res =
|
auto res =
|
||||||
cli.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
|
cli.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
res.set_content("Hello World!", "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
|
auto thread8080 = std::thread([&]() { svr8080.listen("localhost", 8080); });
|
||||||
|
|
||||||
|
auto thread8081 = std::thread([&]() { svr8081.listen("localhost", 8081); });
|
||||||
|
|
||||||
|
while (!svr8080.is_running() || !svr8081.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);
|
||||||
|
cli.set_follow_location(true);
|
||||||
|
|
||||||
|
auto res = cli.Get("/1");
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
|
EXPECT_EQ(res->body, "Hello World!");
|
||||||
|
|
||||||
|
svr8080.stop();
|
||||||
|
svr8081.stop();
|
||||||
|
thread8080.join();
|
||||||
|
thread8081.join();
|
||||||
|
ASSERT_FALSE(svr8080.is_running());
|
||||||
|
ASSERT_FALSE(svr8081.is_running());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
TEST(Server, BindDualStack) {
|
||||||
|
Server svr;
|
||||||
|
|
||||||
|
svr.Get("/1", [&](const Request & /*req*/, Response &res) {
|
||||||
|
res.set_content("Hello World!", "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
|
auto thread = std::thread([&]() { svr.listen("::", PORT); });
|
||||||
|
|
||||||
|
// Give GET time to get a few messages.
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
|
||||||
|
{
|
||||||
|
Client cli("127.0.0.1", PORT);
|
||||||
|
|
||||||
|
auto res = cli.Get("/1");
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
|
EXPECT_EQ(res->body, "Hello World!");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Client cli("::1", PORT);
|
||||||
|
|
||||||
|
auto res = cli.Get("/1");
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
|
EXPECT_EQ(res->body, "Hello World!");
|
||||||
|
}
|
||||||
|
svr.stop();
|
||||||
|
thread.join();
|
||||||
|
ASSERT_FALSE(svr.is_running());
|
||||||
|
}
|
||||||
|
|
||||||
TEST(Server, BindAndListenSeparately) {
|
TEST(Server, BindAndListenSeparately) {
|
||||||
Server svr;
|
Server svr;
|
||||||
int port = svr.bind_to_any_port("0.0.0.0");
|
int port = svr.bind_to_any_port("0.0.0.0");
|
||||||
@@ -653,7 +743,8 @@ TEST(Server, BindAndListenSeparately) {
|
|||||||
|
|
||||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
TEST(SSLServer, BindAndListenSeparately) {
|
TEST(SSLServer, BindAndListenSeparately) {
|
||||||
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE, CLIENT_CA_CERT_DIR);
|
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");
|
int port = svr.bind_to_any_port("0.0.0.0");
|
||||||
ASSERT_TRUE(svr.is_valid());
|
ASSERT_TRUE(svr.is_valid());
|
||||||
ASSERT_TRUE(port > 0);
|
ASSERT_TRUE(port > 0);
|
||||||
@@ -681,6 +772,36 @@ protected:
|
|||||||
[&](const Request & /*req*/, Response &res) {
|
[&](const Request & /*req*/, Response &res) {
|
||||||
res.set_content("Hello World!", "text/plain");
|
res.set_content("Hello World!", "text/plain");
|
||||||
})
|
})
|
||||||
|
.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_FALSE(res.has_header("a"));
|
||||||
|
|
||||||
|
res.set_header("a", "1\nSet-Cookie: a=1");
|
||||||
|
EXPECT_EQ(0, 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_FALSE(res.has_header("a"));
|
||||||
|
|
||||||
|
res.set_header("a\r\nb", "0");
|
||||||
|
EXPECT_EQ(0, res.headers.size());
|
||||||
|
EXPECT_FALSE(res.has_header("a"));
|
||||||
|
|
||||||
|
res.set_header("a\rb", "0");
|
||||||
|
EXPECT_EQ(0, res.headers.size());
|
||||||
|
EXPECT_FALSE(res.has_header("a"));
|
||||||
|
|
||||||
|
res.set_header("a\nb", "0");
|
||||||
|
EXPECT_EQ(0, 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_FALSE(res.has_header("Location"));
|
||||||
|
})
|
||||||
.Get("/slow",
|
.Get("/slow",
|
||||||
[&](const Request & /*req*/, Response &res) {
|
[&](const Request & /*req*/, Response &res) {
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||||
@@ -689,14 +810,31 @@ protected:
|
|||||||
.Get("/remote_addr",
|
.Get("/remote_addr",
|
||||||
[&](const Request &req, Response &res) {
|
[&](const Request &req, Response &res) {
|
||||||
auto remote_addr = req.headers.find("REMOTE_ADDR")->second;
|
auto remote_addr = req.headers.find("REMOTE_ADDR")->second;
|
||||||
|
EXPECT_TRUE(req.has_header("REMOTE_PORT"));
|
||||||
|
EXPECT_EQ(req.remote_addr, req.get_header_value("REMOTE_ADDR"));
|
||||||
|
EXPECT_EQ(req.remote_port,
|
||||||
|
std::stoi(req.get_header_value("REMOTE_PORT")));
|
||||||
res.set_content(remote_addr.c_str(), "text/plain");
|
res.set_content(remote_addr.c_str(), "text/plain");
|
||||||
})
|
})
|
||||||
.Get("/endwith%",
|
.Get("/endwith%",
|
||||||
[&](const Request & /*req*/, Response &res) {
|
[&](const Request & /*req*/, Response &res) {
|
||||||
res.set_content("Hello World!", "text/plain");
|
res.set_content("Hello World!", "text/plain");
|
||||||
})
|
})
|
||||||
|
.Get("/a\\+\\+b",
|
||||||
|
[&](const Request &req, Response &res) {
|
||||||
|
ASSERT_TRUE(req.has_param("a +b"));
|
||||||
|
auto val = req.get_param_value("a +b");
|
||||||
|
res.set_content(val, "text/plain");
|
||||||
|
})
|
||||||
.Get("/", [&](const Request & /*req*/,
|
.Get("/", [&](const Request & /*req*/,
|
||||||
Response &res) { res.set_redirect("/hi"); })
|
Response &res) { res.set_redirect("/hi"); })
|
||||||
|
.Post("/1", [](const Request & /*req*/,
|
||||||
|
Response &res) { res.set_redirect("/2", 303); })
|
||||||
|
.Get("/2",
|
||||||
|
[](const Request & /*req*/, Response &res) {
|
||||||
|
res.set_content("redirected.", "text/plain");
|
||||||
|
res.status = 200;
|
||||||
|
})
|
||||||
.Post("/person",
|
.Post("/person",
|
||||||
[&](const Request &req, Response &res) {
|
[&](const Request &req, Response &res) {
|
||||||
if (req.has_param("name") && req.has_param("note")) {
|
if (req.has_param("name") && req.has_param("note")) {
|
||||||
@@ -735,35 +873,38 @@ protected:
|
|||||||
.Get("/streamed-chunked",
|
.Get("/streamed-chunked",
|
||||||
[&](const Request & /*req*/, Response &res) {
|
[&](const Request & /*req*/, Response &res) {
|
||||||
res.set_chunked_content_provider(
|
res.set_chunked_content_provider(
|
||||||
[](uint64_t /*offset*/, DataSink &sink) {
|
[](size_t /*offset*/, DataSink &sink) {
|
||||||
ASSERT_TRUE(sink.is_writable());
|
EXPECT_TRUE(sink.is_writable());
|
||||||
sink.write("123", 3);
|
sink.os << "123";
|
||||||
sink.write("456", 3);
|
sink.os << "456";
|
||||||
sink.write("789", 3);
|
sink.os << "789";
|
||||||
sink.done();
|
sink.done();
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.Get("/streamed-chunked2",
|
.Get("/streamed-chunked2",
|
||||||
[&](const Request & /*req*/, Response &res) {
|
[&](const Request & /*req*/, Response &res) {
|
||||||
auto i = new int(0);
|
auto i = new int(0);
|
||||||
res.set_chunked_content_provider(
|
res.set_chunked_content_provider(
|
||||||
[i](uint64_t /*offset*/, DataSink &sink) {
|
[i](size_t /*offset*/, DataSink &sink) {
|
||||||
ASSERT_TRUE(sink.is_writable());
|
EXPECT_TRUE(sink.is_writable());
|
||||||
switch (*i) {
|
switch (*i) {
|
||||||
case 0: sink.write("123", 3); break;
|
case 0: sink.os << "123"; break;
|
||||||
case 1: sink.write("456", 3); break;
|
case 1: sink.os << "456"; break;
|
||||||
case 2: sink.write("789", 3); break;
|
case 2: sink.os << "789"; break;
|
||||||
case 3: sink.done(); break;
|
case 3: sink.done(); break;
|
||||||
}
|
}
|
||||||
(*i)++;
|
(*i)++;
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
[i] { delete i; });
|
[i] { delete i; });
|
||||||
})
|
})
|
||||||
.Get("/streamed",
|
.Get("/streamed",
|
||||||
[&](const Request & /*req*/, Response &res) {
|
[&](const Request & /*req*/, Response &res) {
|
||||||
res.set_content_provider(
|
res.set_content_provider(
|
||||||
6, [](uint64_t offset, uint64_t /*length*/, DataSink &sink) {
|
6, [](size_t offset, size_t /*length*/, DataSink &sink) {
|
||||||
sink.write(offset < 3 ? "a" : "b", 1);
|
sink.os << (offset < 3 ? "a" : "b");
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.Get("/streamed-with-range",
|
.Get("/streamed-with-range",
|
||||||
@@ -771,24 +912,25 @@ protected:
|
|||||||
auto data = new std::string("abcdefg");
|
auto data = new std::string("abcdefg");
|
||||||
res.set_content_provider(
|
res.set_content_provider(
|
||||||
data->size(),
|
data->size(),
|
||||||
[data](uint64_t offset, uint64_t length, DataSink &sink) {
|
[data](size_t offset, size_t length, DataSink &sink) {
|
||||||
ASSERT_TRUE(sink.is_writable());
|
EXPECT_TRUE(sink.is_writable());
|
||||||
size_t DATA_CHUNK_SIZE = 4;
|
size_t DATA_CHUNK_SIZE = 4;
|
||||||
const auto &d = *data;
|
const auto &d = *data;
|
||||||
auto out_len =
|
auto out_len =
|
||||||
std::min(static_cast<size_t>(length), DATA_CHUNK_SIZE);
|
std::min(static_cast<size_t>(length), DATA_CHUNK_SIZE);
|
||||||
sink.write(&d[static_cast<size_t>(offset)], out_len);
|
sink.write(&d[static_cast<size_t>(offset)], out_len);
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
[data] { delete data; });
|
[data] { delete data; });
|
||||||
})
|
})
|
||||||
.Get("/streamed-cancel",
|
.Get("/streamed-cancel",
|
||||||
[&](const Request & /*req*/, Response &res) {
|
[&](const Request & /*req*/, Response &res) {
|
||||||
res.set_content_provider(size_t(-1), [](uint64_t /*offset*/,
|
res.set_content_provider(
|
||||||
uint64_t /*length*/,
|
size_t(-1),
|
||||||
DataSink &sink) {
|
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||||
ASSERT_TRUE(sink.is_writable());
|
EXPECT_TRUE(sink.is_writable());
|
||||||
std::string data = "data_chunk";
|
sink.os << "data_chunk";
|
||||||
sink.write(data.data(), data.size());
|
return true;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.Get("/with-range",
|
.Get("/with-range",
|
||||||
@@ -808,16 +950,17 @@ protected:
|
|||||||
[&](const Request &req, Response & /*res*/) {
|
[&](const Request &req, Response & /*res*/) {
|
||||||
EXPECT_EQ(5u, req.files.size());
|
EXPECT_EQ(5u, req.files.size());
|
||||||
ASSERT_TRUE(!req.has_file("???"));
|
ASSERT_TRUE(!req.has_file("???"));
|
||||||
|
ASSERT_TRUE(req.body.empty());
|
||||||
|
|
||||||
{
|
{
|
||||||
const auto &file = req.get_file_value("text1");
|
const auto &file = req.get_file_value("text1");
|
||||||
EXPECT_EQ("", file.filename);
|
EXPECT_TRUE(file.filename.empty());
|
||||||
EXPECT_EQ("text default", file.content);
|
EXPECT_EQ("text default", file.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const auto &file = req.get_file_value("text2");
|
const auto &file = req.get_file_value("text2");
|
||||||
EXPECT_EQ("", file.filename);
|
EXPECT_TRUE(file.filename.empty());
|
||||||
EXPECT_EQ("aωb", file.content);
|
EXPECT_EQ("aωb", file.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -830,7 +973,7 @@ protected:
|
|||||||
|
|
||||||
{
|
{
|
||||||
const auto &file = req.get_file_value("file3");
|
const auto &file = req.get_file_value("file3");
|
||||||
EXPECT_EQ("", file.filename);
|
EXPECT_TRUE(file.filename.empty());
|
||||||
EXPECT_EQ("application/octet-stream", file.content_type);
|
EXPECT_EQ("application/octet-stream", file.content_type);
|
||||||
EXPECT_EQ(0u, file.content.size());
|
EXPECT_EQ(0u, file.content.size());
|
||||||
}
|
}
|
||||||
@@ -838,8 +981,24 @@ protected:
|
|||||||
.Post("/empty",
|
.Post("/empty",
|
||||||
[&](const Request &req, Response &res) {
|
[&](const Request &req, Response &res) {
|
||||||
EXPECT_EQ(req.body, "");
|
EXPECT_EQ(req.body, "");
|
||||||
|
EXPECT_EQ("text/plain", req.get_header_value("Content-Type"));
|
||||||
|
EXPECT_EQ("0", req.get_header_value("Content-Length"));
|
||||||
res.set_content("empty", "text/plain");
|
res.set_content("empty", "text/plain");
|
||||||
})
|
})
|
||||||
|
.Post("/empty-no-content-type",
|
||||||
|
[&](const Request &req, Response &res) {
|
||||||
|
EXPECT_EQ(req.body, "");
|
||||||
|
EXPECT_FALSE(req.has_header("Content-Type"));
|
||||||
|
EXPECT_EQ("0", req.get_header_value("Content-Length"));
|
||||||
|
res.set_content("empty-no-content-type", "text/plain");
|
||||||
|
})
|
||||||
|
.Put("/empty-no-content-type",
|
||||||
|
[&](const Request &req, Response &res) {
|
||||||
|
EXPECT_EQ(req.body, "");
|
||||||
|
EXPECT_FALSE(req.has_header("Content-Type"));
|
||||||
|
EXPECT_EQ("0", req.get_header_value("Content-Length"));
|
||||||
|
res.set_content("empty-no-content-type", "text/plain");
|
||||||
|
})
|
||||||
.Put("/put",
|
.Put("/put",
|
||||||
[&](const Request &req, Response &res) {
|
[&](const Request &req, Response &res) {
|
||||||
EXPECT_EQ(req.body, "PUT");
|
EXPECT_EQ(req.body, "PUT");
|
||||||
@@ -859,6 +1018,11 @@ protected:
|
|||||||
[&](const Request & /*req*/, Response &res) {
|
[&](const Request & /*req*/, Response &res) {
|
||||||
res.set_content("DELETE", "text/plain");
|
res.set_content("DELETE", "text/plain");
|
||||||
})
|
})
|
||||||
|
.Delete("/delete-body",
|
||||||
|
[&](const Request &req, Response &res) {
|
||||||
|
EXPECT_EQ(req.body, "content");
|
||||||
|
res.set_content(req.body, "text/plain");
|
||||||
|
})
|
||||||
.Options(R"(\*)",
|
.Options(R"(\*)",
|
||||||
[&](const Request & /*req*/, Response &res) {
|
[&](const Request & /*req*/, Response &res) {
|
||||||
res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
|
res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
|
||||||
@@ -905,13 +1069,13 @@ protected:
|
|||||||
|
|
||||||
{
|
{
|
||||||
const auto &file = get_file_value(files, "text1");
|
const auto &file = get_file_value(files, "text1");
|
||||||
EXPECT_EQ("", file.filename);
|
EXPECT_TRUE(file.filename.empty());
|
||||||
EXPECT_EQ("text default", file.content);
|
EXPECT_EQ("text default", file.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const auto &file = get_file_value(files, "text2");
|
const auto &file = get_file_value(files, "text2");
|
||||||
EXPECT_EQ("", file.filename);
|
EXPECT_TRUE(file.filename.empty());
|
||||||
EXPECT_EQ("aωb", file.content);
|
EXPECT_EQ("aωb", file.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -924,7 +1088,7 @@ protected:
|
|||||||
|
|
||||||
{
|
{
|
||||||
const auto &file = get_file_value(files, "file3");
|
const auto &file = get_file_value(files, "file3");
|
||||||
EXPECT_EQ("", file.filename);
|
EXPECT_TRUE(file.filename.empty());
|
||||||
EXPECT_EQ("application/octet-stream", file.content_type);
|
EXPECT_EQ("application/octet-stream", file.content_type);
|
||||||
EXPECT_EQ(0u, file.content.size());
|
EXPECT_EQ(0u, file.content.size());
|
||||||
}
|
}
|
||||||
@@ -989,13 +1153,13 @@ protected:
|
|||||||
|
|
||||||
{
|
{
|
||||||
const auto &file = req.get_file_value("key1");
|
const auto &file = req.get_file_value("key1");
|
||||||
EXPECT_EQ("", file.filename);
|
EXPECT_TRUE(file.filename.empty());
|
||||||
EXPECT_EQ("test", file.content);
|
EXPECT_EQ("test", file.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const auto &file = req.get_file_value("key2");
|
const auto &file = req.get_file_value("key2");
|
||||||
EXPECT_EQ("", file.filename);
|
EXPECT_TRUE(file.filename.empty());
|
||||||
EXPECT_EQ("--abcdefg123", file.content);
|
EXPECT_EQ("--abcdefg123", file.content);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -1058,6 +1222,14 @@ TEST_F(ServerTest, GetMethod302) {
|
|||||||
EXPECT_EQ("/hi", res->get_header_value("Location"));
|
EXPECT_EQ("/hi", res->get_header_value("Location"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ServerTest, GetMethod302Redirect) {
|
||||||
|
cli_.set_follow_location(true);
|
||||||
|
auto res = cli_.Get("/");
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
|
EXPECT_EQ("Hello World!", res->body);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, GetMethod404) {
|
TEST_F(ServerTest, GetMethod404) {
|
||||||
auto res = cli_.Get("/invalid");
|
auto res = cli_.Get("/invalid");
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
@@ -1069,7 +1241,7 @@ TEST_F(ServerTest, HeadMethod200) {
|
|||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ(200, res->status);
|
EXPECT_EQ(200, res->status);
|
||||||
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||||
EXPECT_EQ("", res->body);
|
EXPECT_TRUE(res->body.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, HeadMethod200Static) {
|
TEST_F(ServerTest, HeadMethod200Static) {
|
||||||
@@ -1078,14 +1250,14 @@ TEST_F(ServerTest, HeadMethod200Static) {
|
|||||||
EXPECT_EQ(200, res->status);
|
EXPECT_EQ(200, res->status);
|
||||||
EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
|
EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
|
||||||
EXPECT_EQ(104, std::stoi(res->get_header_value("Content-Length")));
|
EXPECT_EQ(104, std::stoi(res->get_header_value("Content-Length")));
|
||||||
EXPECT_EQ("", res->body);
|
EXPECT_TRUE(res->body.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, HeadMethod404) {
|
TEST_F(ServerTest, HeadMethod404) {
|
||||||
auto res = cli_.Head("/invalid");
|
auto res = cli_.Head("/invalid");
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ(404, res->status);
|
EXPECT_EQ(404, res->status);
|
||||||
EXPECT_EQ("", res->body);
|
EXPECT_TRUE(res->body.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, GetMethodPersonJohn) {
|
TEST_F(ServerTest, GetMethodPersonJohn) {
|
||||||
@@ -1171,6 +1343,20 @@ TEST_F(ServerTest, PostEmptyContent) {
|
|||||||
ASSERT_EQ("empty", res->body);
|
ASSERT_EQ("empty", res->body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ServerTest, PostEmptyContentWithNoContentType) {
|
||||||
|
auto res = cli_.Post("/empty-no-content-type");
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
ASSERT_EQ(200, res->status);
|
||||||
|
ASSERT_EQ("empty-no-content-type", res->body);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ServerTest, PutEmptyContentWithNoContentType) {
|
||||||
|
auto res = cli_.Put("/empty-no-content-type");
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
ASSERT_EQ(200, res->status);
|
||||||
|
ASSERT_EQ("empty-no-content-type", res->body);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, GetMethodDir) {
|
TEST_F(ServerTest, GetMethodDir) {
|
||||||
auto res = cli_.Get("/dir/");
|
auto res = cli_.Get("/dir/");
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
@@ -1257,6 +1443,21 @@ TEST_F(ServerTest, GetMethodOutOfBaseDirMount2) {
|
|||||||
EXPECT_EQ(404, res->status);
|
EXPECT_EQ(404, res->status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ServerTest, PostMethod303) {
|
||||||
|
auto res = cli_.Post("/1", "body", "text/plain");
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(303, res->status);
|
||||||
|
EXPECT_EQ("/2", res->get_header_value("Location"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ServerTest, PostMethod303Redirect) {
|
||||||
|
cli_.set_follow_location(true);
|
||||||
|
auto res = cli_.Post("/1", "body", "text/plain");
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
|
EXPECT_EQ("redirected.", res->body);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, UserDefinedMIMETypeMapping) {
|
TEST_F(ServerTest, UserDefinedMIMETypeMapping) {
|
||||||
auto res = cli_.Get("/dir/test.abcde");
|
auto res = cli_.Get("/dir/test.abcde");
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
@@ -1443,6 +1644,13 @@ TEST_F(ServerTest, EndWithPercentCharacterInQuery) {
|
|||||||
EXPECT_EQ(404, res->status);
|
EXPECT_EQ(404, res->status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ServerTest, PlusSignEncoding) {
|
||||||
|
auto res = cli_.Get("/a+%2Bb?a %2bb=a %2Bb");
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
|
EXPECT_EQ("a +b", res->body);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, MultipartFormData) {
|
TEST_F(ServerTest, MultipartFormData) {
|
||||||
MultipartFormDataItems items = {
|
MultipartFormDataItems items = {
|
||||||
{"text1", "text default", "", ""},
|
{"text1", "text default", "", ""},
|
||||||
@@ -1552,6 +1760,17 @@ TEST_F(ServerTest, GetStreamedEndless) {
|
|||||||
ASSERT_TRUE(res == nullptr);
|
ASSERT_TRUE(res == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ServerTest, ClientStop) {
|
||||||
|
thread t = thread([&]() {
|
||||||
|
auto res = cli_.Get("/streamed-cancel",
|
||||||
|
[&](const char *, uint64_t) { return true; });
|
||||||
|
ASSERT_TRUE(res == nullptr);
|
||||||
|
});
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
cli_.stop();
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, GetWithRange1) {
|
TEST_F(ServerTest, GetWithRange1) {
|
||||||
auto res = cli_.Get("/with-range", {{make_range_header({{3, 5}})}});
|
auto res = cli_.Get("/with-range", {{make_range_header({{3, 5}})}});
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
@@ -1650,6 +1869,12 @@ TEST_F(ServerTest, GetMethodRemoteAddr) {
|
|||||||
EXPECT_TRUE(res->body == "::1" || res->body == "127.0.0.1");
|
EXPECT_TRUE(res->body == "::1" || res->body == "127.0.0.1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ServerTest, HTTPResponseSplitting) {
|
||||||
|
auto res = cli_.Get("/http_response_splitting");
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, SlowRequest) {
|
TEST_F(ServerTest, SlowRequest) {
|
||||||
request_threads_.push_back(
|
request_threads_.push_back(
|
||||||
std::thread([=]() { auto res = cli_.Get("/slow"); }));
|
std::thread([=]() { auto res = cli_.Get("/slow"); }));
|
||||||
@@ -1671,8 +1896,9 @@ TEST_F(ServerTest, PutWithContentProvider) {
|
|||||||
auto res = cli_.Put(
|
auto res = cli_.Put(
|
||||||
"/put", 3,
|
"/put", 3,
|
||||||
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||||
ASSERT_TRUE(sink.is_writable());
|
EXPECT_TRUE(sink.is_writable());
|
||||||
sink.write("PUT", 3);
|
sink.os << "PUT";
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
"text/plain");
|
"text/plain");
|
||||||
|
|
||||||
@@ -1681,14 +1907,26 @@ TEST_F(ServerTest, PutWithContentProvider) {
|
|||||||
EXPECT_EQ("PUT", res->body);
|
EXPECT_EQ("PUT", res->body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ServerTest, PostWithContentProviderAbort) {
|
||||||
|
auto res = cli_.Post(
|
||||||
|
"/post", 42,
|
||||||
|
[](size_t /*offset*/, size_t /*length*/, DataSink & /*sink*/) {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"text/plain");
|
||||||
|
|
||||||
|
ASSERT_TRUE(res == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
||||||
TEST_F(ServerTest, PutWithContentProviderWithGzip) {
|
TEST_F(ServerTest, PutWithContentProviderWithGzip) {
|
||||||
cli_.set_compress(true);
|
cli_.set_compress(true);
|
||||||
auto res = cli_.Put(
|
auto res = cli_.Put(
|
||||||
"/put", 3,
|
"/put", 3,
|
||||||
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||||
ASSERT_TRUE(sink.is_writable());
|
EXPECT_TRUE(sink.is_writable());
|
||||||
sink.write("PUT", 3);
|
sink.os << "PUT";
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
"text/plain");
|
"text/plain");
|
||||||
|
|
||||||
@@ -1697,6 +1935,18 @@ TEST_F(ServerTest, PutWithContentProviderWithGzip) {
|
|||||||
EXPECT_EQ("PUT", res->body);
|
EXPECT_EQ("PUT", res->body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ServerTest, PostWithContentProviderWithGzipAbort) {
|
||||||
|
cli_.set_compress(true);
|
||||||
|
auto res = cli_.Post(
|
||||||
|
"/post", 42,
|
||||||
|
[](size_t /*offset*/, size_t /*length*/, DataSink & /*sink*/) {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"text/plain");
|
||||||
|
|
||||||
|
ASSERT_TRUE(res == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, PutLargeFileWithGzip) {
|
TEST_F(ServerTest, PutLargeFileWithGzip) {
|
||||||
cli_.set_compress(true);
|
cli_.set_compress(true);
|
||||||
auto res = cli_.Put("/put-large", LARGE_DATA, "text/plain");
|
auto res = cli_.Put("/put-large", LARGE_DATA, "text/plain");
|
||||||
@@ -1711,7 +1961,8 @@ TEST_F(ServerTest, PutContentWithDeflate) {
|
|||||||
httplib::Headers headers;
|
httplib::Headers headers;
|
||||||
headers.emplace("Content-Encoding", "deflate");
|
headers.emplace("Content-Encoding", "deflate");
|
||||||
// PUT in deflate format:
|
// PUT in deflate format:
|
||||||
auto res = cli_.Put("/put", headers, "\170\234\013\010\015\001\0\001\361\0\372", "text/plain");
|
auto res = cli_.Put("/put", headers,
|
||||||
|
"\170\234\013\010\015\001\0\001\361\0\372", "text/plain");
|
||||||
|
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ(200, res->status);
|
EXPECT_EQ(200, res->status);
|
||||||
@@ -1734,6 +1985,13 @@ TEST_F(ServerTest, Delete) {
|
|||||||
EXPECT_EQ("DELETE", res->body);
|
EXPECT_EQ("DELETE", res->body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ServerTest, DeleteContentReceiver) {
|
||||||
|
auto res = cli_.Delete("/delete-body", "content", "text/plain");
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
|
EXPECT_EQ("content", res->body);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, Options) {
|
TEST_F(ServerTest, Options) {
|
||||||
auto res = cli_.Options("*");
|
auto res = cli_.Options("*");
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
@@ -1835,6 +2093,9 @@ TEST_F(ServerTest, KeepAlive) {
|
|||||||
Get(requests, "/hi");
|
Get(requests, "/hi");
|
||||||
Get(requests, "/not-exist");
|
Get(requests, "/not-exist");
|
||||||
Post(requests, "/empty", "", "text/plain");
|
Post(requests, "/empty", "", "text/plain");
|
||||||
|
Post(
|
||||||
|
requests, "/empty", 0,
|
||||||
|
[&](size_t, size_t, httplib::DataSink &) { return true; }, "text/plain");
|
||||||
|
|
||||||
std::vector<Response> responses;
|
std::vector<Response> responses;
|
||||||
auto ret = cli_.send(requests, responses);
|
auto ret = cli_.send(requests, responses);
|
||||||
@@ -1842,7 +2103,7 @@ TEST_F(ServerTest, KeepAlive) {
|
|||||||
ASSERT_TRUE(ret == true);
|
ASSERT_TRUE(ret == true);
|
||||||
ASSERT_TRUE(requests.size() == responses.size());
|
ASSERT_TRUE(requests.size() == responses.size());
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
for (size_t i = 0; i < 3; i++) {
|
||||||
auto &res = responses[i];
|
auto &res = responses[i];
|
||||||
EXPECT_EQ(200, res.status);
|
EXPECT_EQ(200, res.status);
|
||||||
EXPECT_EQ("text/plain", res.get_header_value("Content-Type"));
|
EXPECT_EQ("text/plain", res.get_header_value("Content-Type"));
|
||||||
@@ -1854,8 +2115,8 @@ TEST_F(ServerTest, KeepAlive) {
|
|||||||
EXPECT_EQ(404, res.status);
|
EXPECT_EQ(404, res.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
for (size_t i = 4; i < 6; i++) {
|
||||||
auto &res = responses[4];
|
auto &res = responses[i];
|
||||||
EXPECT_EQ(200, res.status);
|
EXPECT_EQ(200, res.status);
|
||||||
EXPECT_EQ("text/plain", res.get_header_value("Content-Type"));
|
EXPECT_EQ("text/plain", res.get_header_value("Content-Type"));
|
||||||
EXPECT_EQ("empty", res.body);
|
EXPECT_EQ("empty", res.body);
|
||||||
@@ -1883,7 +2144,7 @@ TEST_F(ServerTest, GzipWithoutAcceptEncoding) {
|
|||||||
auto res = cli_.Get("/gzip", headers);
|
auto res = cli_.Get("/gzip", headers);
|
||||||
|
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ("", res->get_header_value("Content-Encoding"));
|
EXPECT_TRUE(res->get_header_value("Content-Encoding").empty());
|
||||||
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||||
EXPECT_EQ("100", res->get_header_value("Content-Length"));
|
EXPECT_EQ("100", res->get_header_value("Content-Length"));
|
||||||
EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456"
|
EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456"
|
||||||
@@ -1924,7 +2185,7 @@ TEST_F(ServerTest, GzipWithContentReceiverWithoutAcceptEncoding) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ("", res->get_header_value("Content-Encoding"));
|
EXPECT_TRUE(res->get_header_value("Content-Encoding").empty());
|
||||||
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||||
EXPECT_EQ("100", res->get_header_value("Content-Length"));
|
EXPECT_EQ("100", res->get_header_value("Content-Length"));
|
||||||
EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456"
|
EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456"
|
||||||
@@ -1984,7 +2245,8 @@ TEST_F(ServerTest, MultipartFormDataGzip) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Sends a raw request to a server listening at HOST:PORT.
|
// Sends a raw request to a server listening at HOST:PORT.
|
||||||
static bool send_request(time_t read_timeout_sec, const std::string &req) {
|
static bool send_request(time_t read_timeout_sec, const std::string &req,
|
||||||
|
std::string *resp = nullptr) {
|
||||||
auto client_sock = detail::create_client_socket(HOST, PORT, /*timeout_sec=*/5,
|
auto client_sock = detail::create_client_socket(HOST, PORT, /*timeout_sec=*/5,
|
||||||
std::string());
|
std::string());
|
||||||
|
|
||||||
@@ -2002,7 +2264,9 @@ static bool send_request(time_t read_timeout_sec, const std::string &req) {
|
|||||||
char buf[512];
|
char buf[512];
|
||||||
|
|
||||||
detail::stream_line_reader line_reader(strm, buf, sizeof(buf));
|
detail::stream_line_reader line_reader(strm, buf, sizeof(buf));
|
||||||
while (line_reader.getline()) {}
|
while (line_reader.getline()) {
|
||||||
|
if (resp) { *resp += line_reader.ptr(); }
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -2033,11 +2297,16 @@ TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
|
|||||||
EXPECT_EQ(header_value, "\v bar \e");
|
EXPECT_EQ(header_value, "\v bar \e");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity) {
|
// Sends a raw request and verifies that there isn't a crash or exception.
|
||||||
|
static void test_raw_request(const std::string &req,
|
||||||
|
std::string *out = nullptr) {
|
||||||
Server svr;
|
Server svr;
|
||||||
svr.Get("/hi", [&](const Request & /*req*/, Response &res) {
|
svr.Get("/hi", [&](const Request & /*req*/, Response &res) {
|
||||||
res.set_content("ok", "text/plain");
|
res.set_content("ok", "text/plain");
|
||||||
});
|
});
|
||||||
|
svr.Put("/put_hi", [&](const Request & /*req*/, Response &res) {
|
||||||
|
res.set_content("ok", "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
// Server read timeout must be longer than the client read timeout for the
|
// Server read timeout must be longer than the client read timeout for the
|
||||||
// bug to reproduce, probably to force the server to process a request
|
// bug to reproduce, probably to force the server to process a request
|
||||||
@@ -2050,19 +2319,119 @@ TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity) {
|
|||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// A certain header line causes an exception if the header property is parsed
|
ASSERT_TRUE(send_request(client_read_timeout_sec, req, out));
|
||||||
// naively with a single regex. This occurs with libc++ but not libstdc++.
|
|
||||||
const std::string req =
|
|
||||||
"GET /hi HTTP/1.1\r\n"
|
|
||||||
" : "
|
|
||||||
" ";
|
|
||||||
|
|
||||||
ASSERT_TRUE(send_request(client_read_timeout_sec, req));
|
|
||||||
svr.stop();
|
svr.stop();
|
||||||
t.join();
|
t.join();
|
||||||
EXPECT_TRUE(listen_thread_ok);
|
EXPECT_TRUE(listen_thread_ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity) {
|
||||||
|
// A certain header line causes an exception if the header property is parsed
|
||||||
|
// naively with a single regex. This occurs with libc++ but not libstdc++.
|
||||||
|
test_raw_request(
|
||||||
|
"GET /hi HTTP/1.1\r\n"
|
||||||
|
" : "
|
||||||
|
" "
|
||||||
|
" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity2) {
|
||||||
|
// A certain header line causes an exception if the header property *name* is
|
||||||
|
// parsed with a regular expression starting with "(.+?):" - this is a non-
|
||||||
|
// greedy matcher and requires backtracking when there are a lot of ":"
|
||||||
|
// characters.
|
||||||
|
// This occurs with libc++ but not libstdc++.
|
||||||
|
test_raw_request(
|
||||||
|
"GET /hi HTTP/1.1\r\n"
|
||||||
|
":-:::::::::::::::::::::::::::-::::::::::::::::::::::::@-&&&&&&&&&&&"
|
||||||
|
"--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&"
|
||||||
|
"&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-:::::"
|
||||||
|
"::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-::::::::::::::::::::::::"
|
||||||
|
":::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::"
|
||||||
|
"::::::::-:::::::::::::::::@-&&&&&&&--:::::::-::::::::::::::::::::::"
|
||||||
|
":::::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::"
|
||||||
|
"::::::::::-:::::::::::::::::@-&&&&&::::::::::::-:::::::::::::::::@-"
|
||||||
|
"&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::::::::::::"
|
||||||
|
":@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::"
|
||||||
|
"::::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::@-&&"
|
||||||
|
"&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@"
|
||||||
|
"::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&"
|
||||||
|
"--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&"
|
||||||
|
"&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&"
|
||||||
|
"&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&"
|
||||||
|
"&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@"
|
||||||
|
"-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::"
|
||||||
|
"::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::::::::"
|
||||||
|
":::::@-&&&&&&&&&&&::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-::::::"
|
||||||
|
":::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-:::"
|
||||||
|
"::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-"
|
||||||
|
":::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&---&&:&"
|
||||||
|
"&&.0------------:-:::::::::::::::::::::::::::::-:::::::::::::::::@-"
|
||||||
|
"&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::::::::::::"
|
||||||
|
":@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::"
|
||||||
|
"::::@-&&&&&&&&&&&---&&:&&&.0------------O--------\rH PUTHTTP/1.1\r\n"
|
||||||
|
"&&&%%%");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ServerRequestParsingTest, ExcessiveWhitespaceInUnparseableHeaderLine) {
|
||||||
|
// Make sure this doesn't crash the server.
|
||||||
|
// In a previous version of the header line regex, the "\r" rendered the line
|
||||||
|
// unparseable and the regex engine repeatedly backtracked, trying to look for
|
||||||
|
// a new position where the leading white space ended and the field value
|
||||||
|
// began.
|
||||||
|
// The crash occurs with libc++ but not libstdc++.
|
||||||
|
test_raw_request("GET /hi HTTP/1.1\r\n"
|
||||||
|
"a:" +
|
||||||
|
std::string(2000, ' ') + '\r' + std::string(20, 'z') +
|
||||||
|
"\r\n"
|
||||||
|
"\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ServerRequestParsingTest, InvalidFirstChunkLengthInRequest) {
|
||||||
|
std::string out;
|
||||||
|
|
||||||
|
test_raw_request("PUT /put_hi HTTP/1.1\r\n"
|
||||||
|
"Content-Type: text/plain\r\n"
|
||||||
|
"Transfer-Encoding: chunked\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"nothex\r\n",
|
||||||
|
&out);
|
||||||
|
EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ServerRequestParsingTest, InvalidSecondChunkLengthInRequest) {
|
||||||
|
std::string out;
|
||||||
|
|
||||||
|
test_raw_request("PUT /put_hi HTTP/1.1\r\n"
|
||||||
|
"Content-Type: text/plain\r\n"
|
||||||
|
"Transfer-Encoding: chunked\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"3\r\n"
|
||||||
|
"xyz\r\n"
|
||||||
|
"NaN\r\n",
|
||||||
|
&out);
|
||||||
|
EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ServerRequestParsingTest, ChunkLengthTooHighInRequest) {
|
||||||
|
std::string out;
|
||||||
|
|
||||||
|
test_raw_request("PUT /put_hi HTTP/1.1\r\n"
|
||||||
|
"Content-Type: text/plain\r\n"
|
||||||
|
"Transfer-Encoding: chunked\r\n"
|
||||||
|
"\r\n"
|
||||||
|
// Length is too large for 64 bits.
|
||||||
|
"1ffffffffffffffff\r\n"
|
||||||
|
"xyz\r\n",
|
||||||
|
&out);
|
||||||
|
EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ServerRequestParsingTest, InvalidHeaderTextWithExtraCR) {
|
||||||
|
test_raw_request("GET /hi HTTP/1.1\r\n"
|
||||||
|
"Content-Type: text/plain\r\n\r");
|
||||||
|
}
|
||||||
|
|
||||||
TEST(ServerStopTest, StopServerWithChunkedTransmission) {
|
TEST(ServerStopTest, StopServerWithChunkedTransmission) {
|
||||||
Server svr;
|
Server svr;
|
||||||
|
|
||||||
@@ -2071,9 +2440,10 @@ TEST(ServerStopTest, StopServerWithChunkedTransmission) {
|
|||||||
res.set_header("Cache-Control", "no-cache");
|
res.set_header("Cache-Control", "no-cache");
|
||||||
res.set_chunked_content_provider([](size_t offset, const DataSink &sink) {
|
res.set_chunked_content_provider([](size_t offset, const DataSink &sink) {
|
||||||
char buffer[27];
|
char buffer[27];
|
||||||
int size = sprintf(buffer, "data:%ld\n\n", offset);
|
auto size = static_cast<size_t>(sprintf(buffer, "data:%ld\n\n", offset));
|
||||||
sink.write(buffer, size);
|
sink.write(buffer, size);
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2147,6 +2517,34 @@ TEST(MountTest, Unmount) {
|
|||||||
ASSERT_FALSE(svr.is_running());
|
ASSERT_FALSE(svr.is_running());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ExceptionTest, ThrowExceptionInHandler) {
|
||||||
|
Server svr;
|
||||||
|
|
||||||
|
svr.Get("/hi", [&](const Request & /*req*/, Response &res) {
|
||||||
|
throw std::runtime_error("exception...");
|
||||||
|
res.set_content("Hello World!", "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
|
auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
|
||||||
|
while (!svr.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", PORT);
|
||||||
|
|
||||||
|
auto res = cli.Get("/hi");
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(500, res->status);
|
||||||
|
ASSERT_FALSE(res->has_header("EXCEPTION_WHAT"));
|
||||||
|
|
||||||
|
svr.stop();
|
||||||
|
listen_thread.join();
|
||||||
|
ASSERT_FALSE(svr.is_running());
|
||||||
|
}
|
||||||
|
|
||||||
class ServerTestWithAI_PASSIVE : public ::testing::Test {
|
class ServerTestWithAI_PASSIVE : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
ServerTestWithAI_PASSIVE()
|
ServerTestWithAI_PASSIVE()
|
||||||
@@ -2331,7 +2729,7 @@ TEST(SSLClientServerTest, ClientCertPresent) {
|
|||||||
char name[BUFSIZ];
|
char name[BUFSIZ];
|
||||||
auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
|
auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
|
||||||
name, sizeof(name));
|
name, sizeof(name));
|
||||||
common_name.assign(name, name_len);
|
common_name.assign(name, static_cast<size_t>(name_len));
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_EQ("Common Name", common_name);
|
EXPECT_EQ("Common Name", common_name);
|
||||||
@@ -2351,6 +2749,80 @@ TEST(SSLClientServerTest, ClientCertPresent) {
|
|||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(SSLClientServerTest, MemoryClientCertPresent) {
|
||||||
|
X509 *server_cert;
|
||||||
|
EVP_PKEY *server_private_key;
|
||||||
|
X509_STORE *client_ca_cert_store;
|
||||||
|
X509 *client_cert;
|
||||||
|
EVP_PKEY *client_private_key;
|
||||||
|
|
||||||
|
FILE *f = fopen(SERVER_CERT_FILE, "r+");
|
||||||
|
server_cert = PEM_read_X509(f, nullptr, nullptr, nullptr);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
f = fopen(SERVER_PRIVATE_KEY_FILE, "r+");
|
||||||
|
server_private_key = PEM_read_PrivateKey(f, nullptr, nullptr, nullptr);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
f = fopen(CLIENT_CA_CERT_FILE, "r+");
|
||||||
|
client_cert = PEM_read_X509(f, nullptr, nullptr, nullptr);
|
||||||
|
client_ca_cert_store = X509_STORE_new();
|
||||||
|
X509_STORE_add_cert(client_ca_cert_store, client_cert);
|
||||||
|
X509_free(client_cert);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
f = fopen(CLIENT_CERT_FILE, "r+");
|
||||||
|
client_cert = PEM_read_X509(f, nullptr, nullptr, nullptr);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
f = fopen(CLIENT_PRIVATE_KEY_FILE, "r+");
|
||||||
|
client_private_key = PEM_read_PrivateKey(f, nullptr, nullptr, nullptr);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
SSLServer svr(server_cert, server_private_key, client_ca_cert_store);
|
||||||
|
ASSERT_TRUE(svr.is_valid());
|
||||||
|
|
||||||
|
svr.Get("/test", [&](const Request &req, Response &res) {
|
||||||
|
res.set_content("test", "text/plain");
|
||||||
|
svr.stop();
|
||||||
|
ASSERT_TRUE(true);
|
||||||
|
|
||||||
|
auto peer_cert = SSL_get_peer_certificate(req.ssl);
|
||||||
|
ASSERT_TRUE(peer_cert != nullptr);
|
||||||
|
|
||||||
|
auto subject_name = X509_get_subject_name(peer_cert);
|
||||||
|
ASSERT_TRUE(subject_name != nullptr);
|
||||||
|
|
||||||
|
std::string common_name;
|
||||||
|
{
|
||||||
|
char name[BUFSIZ];
|
||||||
|
auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
|
||||||
|
name, sizeof(name));
|
||||||
|
common_name.assign(name, static_cast<size_t>(name_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ("Common Name", common_name);
|
||||||
|
|
||||||
|
X509_free(peer_cert);
|
||||||
|
});
|
||||||
|
|
||||||
|
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
|
||||||
|
httplib::SSLClient cli(HOST, PORT, client_cert, client_private_key);
|
||||||
|
auto res = cli.Get("/test");
|
||||||
|
cli.set_timeout_sec(30);
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
ASSERT_EQ(200, res->status);
|
||||||
|
|
||||||
|
X509_free(server_cert);
|
||||||
|
EVP_PKEY_free(server_private_key);
|
||||||
|
X509_free(client_cert);
|
||||||
|
EVP_PKEY_free(client_private_key);
|
||||||
|
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
TEST(SSLClientServerTest, ClientCertMissing) {
|
TEST(SSLClientServerTest, ClientCertMissing) {
|
||||||
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE,
|
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE,
|
||||||
CLIENT_CA_CERT_DIR);
|
CLIENT_CA_CERT_DIR);
|
||||||
@@ -2391,13 +2863,6 @@ TEST(SSLClientServerTest, TrustDirOptional) {
|
|||||||
|
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cannot test this case as there is no external access to SSL object to check
|
|
||||||
SSL_get_peer_certificate() == NULL TEST(SSLClientServerTest,
|
|
||||||
ClientCAPathRequired) { SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE,
|
|
||||||
nullptr, CLIENT_CA_CERT_DIR);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -2406,3 +2871,51 @@ TEST(CleanupTest, WSACleanup) {
|
|||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
|
TEST(InvalidScheme, SimpleInterface) {
|
||||||
|
httplib::Client2 cli("scheme://yahoo.com");
|
||||||
|
ASSERT_FALSE(cli.is_valid());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NoScheme, SimpleInterface) {
|
||||||
|
httplib::Client2 cli("yahoo.com");
|
||||||
|
ASSERT_FALSE(cli.is_valid());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(YahooRedirectTest2, SimpleInterface) {
|
||||||
|
httplib::Client2 cli("http://yahoo.com");
|
||||||
|
|
||||||
|
auto res = cli.Get("/");
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(301, res->status);
|
||||||
|
|
||||||
|
cli.set_follow_location(true);
|
||||||
|
res = cli.Get("/");
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(YahooRedirectTest3, SimpleInterface) {
|
||||||
|
httplib::Client2 cli("https://yahoo.com");
|
||||||
|
|
||||||
|
auto res = cli.Get("/");
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(301, res->status);
|
||||||
|
|
||||||
|
cli.set_follow_location(true);
|
||||||
|
res = cli.Get("/");
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(HttpsToHttpRedirectTest2, SimpleInterface) {
|
||||||
|
auto res =
|
||||||
|
httplib::Client2("https://httpbin.org")
|
||||||
|
.set_follow_location(true)
|
||||||
|
.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
|
||||||
|
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
+13
-13
@@ -28,7 +28,7 @@
|
|||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -69,13 +69,13 @@
|
|||||||
<PropertyGroup Label="UserMacros" />
|
<PropertyGroup Label="UserMacros" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
<LinkIncremental>true</LinkIncremental>
|
<LinkIncremental>true</LinkIncremental>
|
||||||
<IncludePath>C:\Program Files\OpenSSL-Win64\lib\VC;C:\Program Files\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
|
<IncludePath>$(IncludePath)</IncludePath>
|
||||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<LinkIncremental>true</LinkIncremental>
|
<LinkIncremental>true</LinkIncremental>
|
||||||
<IncludePath>C:\Program Files\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
|
<IncludePath>$(IncludePath)</IncludePath>
|
||||||
<LibraryPath>C:\Program Files\OpenSSL-Win64\lib;$(LibraryPath)</LibraryPath>
|
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
<LinkIncremental>false</LinkIncremental>
|
<LinkIncremental>false</LinkIncremental>
|
||||||
@@ -84,14 +84,14 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
<LinkIncremental>false</LinkIncremental>
|
<LinkIncremental>false</LinkIncremental>
|
||||||
<IncludePath>C:\Program Files\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
|
<IncludePath>$(IncludePath)</IncludePath>
|
||||||
<LibraryPath>C:\Program Files\OpenSSL-Win64\lib;$(LibraryPath)</LibraryPath>
|
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<PrecompiledHeader>
|
<PrecompiledHeader>
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
<ClCompile>
|
<ClCompile>
|
||||||
<PrecompiledHeader>
|
<PrecompiledHeader>
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
||||||
@@ -118,12 +118,12 @@
|
|||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalDependencies>Ws2_32.lib;libssl.lib;libcrypto.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<PrecompiledHeader>
|
<PrecompiledHeader>
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<Optimization>MaxSpeed</Optimization>
|
<Optimization>MaxSpeed</Optimization>
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<PrecompiledHeader>
|
<PrecompiledHeader>
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<Optimization>MaxSpeed</Optimization>
|
<Optimization>MaxSpeed</Optimization>
|
||||||
@@ -160,7 +160,7 @@
|
|||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<AdditionalDependencies>Ws2_32.lib;libssl.lib;libcrypto.lib;libssl.lib;libcrypto.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
Reference in new issue
Block a user