Compare commits

..

65 Commits

Author SHA1 Message Date
yhirose c0b461a3b7 Release v0.11.3 2022-11-29 19:26:10 -05:00
yhirose 74fe5a5029 Fix #1426 2022-11-27 10:53:11 -05:00
yhirose 9d0a9d4e23 Fix #1437 2022-11-27 10:21:24 -05:00
TheMarpe 5758769ad3 Windows CMake directory install fix (#1434)
* Fixed install convention pre 3.25

* Simplified CMAKEDIR installation directory
2022-11-24 22:51:55 -05:00
Ray Beck e7eadc3605 add SYSTEM to include to prevent warnings (#1428) 2022-11-24 22:23:11 -05:00
yhirose 07c6e58951 Fix #1421 2022-11-15 11:57:14 -05:00
Pavel Artemkin 87994811a1 undef poll at the end if CPPHTTPLIB_USE_POLL (#1427)
* undef poll at the end if CPPHTTPLIB_USE_POLL

* win32 only
2022-11-13 21:49:49 -05:00
yhirose 42feb7e8be Updated README 2022-11-10 08:27:30 -05:00
yhirose 26196b70af Add test case for 'Issue 52666 in oss-fuzz: cpp-httplib:server_fuzzer: Timeout in server_fuzzer' 2022-11-03 13:07:04 -04:00
Changbin Park 93a51979c4 Get client process id over ip/port when server runs on UNIX socket. (#1418)
* handle socket options for UNIX socket same as others

 * set FD_CLOEXEC by default
 * invoke `socket_options` callback if set

* Offer Client info even on UNIX socket based Server

HTTP Request header "REMOTE_PORT" contains client process id if possible
when Server works on UNIX socket.

* retrigger checks

* retrigger checks

* add support macOS

Co-authored-by: Changbin Park <changbin.park@ahnlab.com>
2022-11-03 10:25:18 -04:00
Changbin Park ad7edc7b27 avoid lockup in ThreadPool::shutdown() on legacy host (#1417)
It is found on CentOS 7.0.1406 which is very early version of CentOS 7.

Co-authored-by: Changbin Park <changbin.park@ahnlab.com>
2022-11-03 10:23:45 -04:00
Andreas Kempf 27cd4e6ffe Support compilation on AIX (#1402)
Disable the ifaddrs.h include statement and the USE_IF2IP flag on AIX.
AIX is a Unix OS but does not offer the ifaddrs.h header.
2022-10-14 10:49:48 -04:00
yhirose cae5a8be1c Added more fuzzing corpus 2022-09-24 08:31:21 -04:00
yhirose 8e10d4e8e7 Release v0.11.2 2022-09-12 11:36:14 -04:00
Changbin Park b57f79f438 Detecting client disconnection (#1373)
* SocketStream need to check connectivity for writability.

When the stream is used for SSE server which works via chunked content
provider it's not possible to check connectivity over writability
because it's wrapped by DataSink. It could make the server stalled, when
the provider wants to only keep connection without send data and certain
amount of clients consumes entire threadpool.

* add unittest for SocketStream::is_writable()

SocketStream::is_writable() should return false if peer is disconnected.

* revise broken unittest ServerTest.ClientStop

DataSink could be unwritable if it's backed by connection based. Because
it could be disconnected by client right after enter centent provider.

Co-authored-by: Changbin Park <changbin.park@ahnlab.com>
2022-08-30 21:11:19 -04:00
Andrea Pappacoda a9cf097951 build: set soversion to major.minor (#1357)
Release 0.11 broke backwards compatibility, meaning that different
cpp-httplib versions are compatible with each other only if the major
and minor version numbers are the same.

This patch reflects this in the build systems.

See #1209 for some more context.
2022-08-12 13:48:40 -04:00
yhirose 5c3624e1af Updated example/uploader.sh 2022-08-06 14:43:56 -04:00
yhirose cba9ef8c0b Issue 49740 in oss-fuzz: cpp-httplib:server_fuzzer: Timeout in server_fuzzer 2022-08-06 08:08:08 -04:00
yhirose 4f8407a3a7 Refactoring the previous commit 2022-08-04 20:56:02 -04:00
Gopinath K 656e936f49 add multipart formdata for PUT requests. (#1351)
* httplib.h

  add multipart formdata for PUT in addition to POST as some REST
  APIs use that.

  Factor the boundary checking code into a helper and use it from
  both Post() and Put().

* test/test.cc

  add test cases for the above.
2022-08-04 20:42:13 -04:00
yhirose d92c314466 Release v0.11.1 2022-08-02 19:44:25 -04:00
yhirose b747fb111d Updated README 2022-08-02 19:42:11 -04:00
yhirose 7e0a0b2d0c Updated README 2022-08-02 19:30:15 -04:00
Changbin Park 362d064afa UNIX domain socket support (#1346)
* Add support UNIX domain socket

* `set_address_family(AF_UNIX)` is required

* add unittest for UNIX domain socket

* add support UNIX domain socket with abstract address

Abstract address of AF_UNIX begins with null(0x00) which can't be
delivered via .c_str() method.

* add unittest for UNIX domain socket with abstract address

Co-authored-by: Changbin Park <changbin.park@ahnlab.com>
2022-08-01 06:57:25 -04:00
Ata Yardımcı 1bd88de2e5 Fix test build warning (#1344)
Co-authored-by: ata.yardimci <ata.yardimci@erstream.com>
2022-07-31 16:51:06 -04:00
Rockybilly 0b541ffebc Add get_socket_fd method to Client and ClientImpl, add according unit… (#1341)
* Add get_socket_fd method to Client and ClientImpl, add according unit test

* Change name get_socket_fd to get_socket

* Change name get_socket to socket

Co-authored-by: ata.yardimci <ata.yardimci@erstream.com>
2022-07-31 08:27:38 -04:00
yhirose 106be19c3e Issue 49512: cpp-httplib:server_fuzzer: Timeout in server_fuzzer 2022-07-30 23:27:29 -04:00
yhirose 25d72bf881 Release v0.11.0 2022-07-29 20:45:55 -04:00
Mehmet İbrahimoğlu 9d5b5297cc ssize_t redefinition on Windows - int/int64 vs long/long long (#1337)
* ssize_t redefinition on Windows - int/int64 vs long/long long

* Define ssize_t as __int64 for _WIN64, not long long

Co-authored-by: iamttaM <9880090+oculusbytes@users.noreply.github.com>
2022-07-29 20:42:31 -04:00
Mehmet İbrahimoğlu 462884bebb With SSL enabled and NOMINMAX not defined, there is a conflict with 'max', which this fixes (#1334)
Co-authored-by: iamttaM <9880090+oculusbytes@users.noreply.github.com>
2022-07-27 08:16:06 -04:00
ZHANG Xiang b1cc24b795 Update README.md (#1332)
Update error list. Introduce `httplib::to_string` in the example code.
2022-07-25 06:48:52 -04:00
yhirose f0eb55b327 Changed to use const std::string & as much as possible instead of const char * (#1331)
* Changed to use `const std::string &` as much as possible instead of `const char *`

* Fix problems on Windows
2022-07-22 22:44:33 -04:00
yhirose 6dc285b5ca Merge branch 'kuguma-use_exception_ptr' 2022-07-15 17:33:53 -04:00
yhirose 07e614eef7 clangformat and README update 2022-07-15 17:32:38 -04:00
yhirose 916b2a8fd3 Merge branch 'use_exception_ptr' of https://github.com/kuguma/cpp-httplib into kuguma-use_exception_ptr 2022-07-15 17:19:39 -04:00
Kai Aoki 869f5bb279 fix ExceptionHandlerTest.ContentLength 2022-07-15 11:50:26 +09:00
yhirose 3e21338f82 Update README 2022-07-14 20:59:48 -04:00
yhirose 37bb3c6a77 No longer support VS 2013 and older #1325 (#1326)
* Fixed a warning

* No longer support VS 2013 and older (Fix #1325)
2022-07-14 20:57:41 -04:00
Kai Aoki d4ab2fa0e6 fix double ref and case of exceptions that are not std::exception 2022-07-15 01:45:10 +09:00
Kai Aoki 72d3f4896a Update httplib.h
use std::exception_ptr
2022-07-12 00:10:57 +09:00
yhirose 5e6f973b99 Release v0.10.9 2022-07-08 17:40:05 -04:00
yhirose 7ed77b02ad Disable YouTubeNoSSLDigest 2022-07-08 17:40:05 -04:00
yhirose 127a64d5a0 Skip preamble and epilogue in multipart/form-data (Fix #1317) (#1320)
* fix: skip MIME preamble (#1317)

* Skip epilogue in multipart/form-data

Co-authored-by: Gavin1937 <71205842+Gavin1937@users.noreply.github.com>
2022-07-08 17:26:50 -04:00
yhirose caa31aafda Accept large data transfer over SSL (Fix #1261, Close #1312) 2022-07-02 07:50:33 -04:00
yhirose dae318495f Revert "Accept large data transfer over SSL (#1261)"
This reverts commit 307b729549.
2022-07-02 07:18:59 -04:00
Andrea Pappacoda 305a7abcb9 fix: update CPPHTTPLIB_VERSION to 0.10.8 (#1305) 2022-06-08 16:44:10 -04:00
yhirose 219d13b718 Fix #1303 2022-06-07 09:52:08 -04:00
conghuawang df20c27696 resolve http server can't send file large than 2GB (Fix #1290) (#1294)
* resolve problem: http server can't send file large than 2GB.
add unit test for http server send large file.
add /bigobj compile option to msvc x64.

* disable unit test "ServerLargeContentTest" due to out-of-memory on GitHub Actions.
2022-05-27 11:56:20 -04:00
yhirose a5a62768c0 Fix #1292 (#1296) 2022-05-27 11:54:43 -04:00
yhirose 4001637beb Added CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 2022-05-26 10:16:32 -04:00
Martín Córdova 47044c05a8 Fix compile error with MINGW-64 GCC-12.1.0 (#1283) 2022-05-24 07:16:54 -04:00
Andrea Pappacoda a449d82723 build(cmake): minor tweaks (#1274)
- Enable THREADS_PREFER_PTHREAD_FLAG to use -pthread where supported
- Remove low-level compile features (closes #1272)
- Remove unneeded DESTINATION options where possible
2022-05-17 07:02:44 -04:00
yhirose fee8e97b4e Rename fuzzing test corpus for #1264 2022-05-03 14:53:59 -04:00
yhirose 72d9ed4056 Added fuzzing corpus for #1264 2022-05-02 18:06:14 -04:00
Andrea Pappacoda 1be1b3a86d build(meson): don't require python3 (#1267)
Thanks to abf3a67dd0 the use of python3
isn't required anymore to configure the build, so I moved the
find_program('python3') inside the "if compile" block.

This makes it possible to configure cpp-httplib on systems where python
isn't available with tools like muon: https://sr.ht/~lattis/muon/
2022-04-30 17:40:47 -04:00
yhirose 9452c0a4b6 Release v0.10.7 2022-04-28 10:21:14 -04:00
Yoshiki Matsuda 307b729549 Accept large data transfer over SSL (#1261)
* Add large data transfer test

* Replace `SSL_read` and `SSL_write` with `ex` functions

* Reflect review comment

* Fix return value of `SSLSocketStream::read/write`

* Fix return value in the case of `SSL_ERROR_ZERO_RETURN`

* Disable `LargeDataTransfer` test due to OoM in CI
2022-04-27 21:08:39 -04:00
mylogin 696239d6e1 Link Windows crypto libs only when CPPHTTPLIB_OPENSSL_SUPPORT is set (#1254) 2022-04-20 22:04:55 -04:00
Andrea Pappacoda 6929d90353 build(meson): allow using OpenSSL 3.0 (#1256)
Following 0857eba17b cpp-httplib is fully compatible with OpenSSL versions newer than 1.1.1
2022-04-20 21:39:52 -04:00
yhirose 348d032029 Updated README 2022-04-19 23:02:30 -04:00
Andrea Pappacoda d1d3fcdfd5 build(meson): mark *_encrypted_pem as test deps (#1255)
Meson only runs required targets. The key_encrypted_pem and
cert_encrypted_pem targets added in 020b0db090
and 8191fd8e6c weren't added to the list
of targets required by the test target, so the generation of the
encrypted certs was skipped, resulting in the failure of
BindServerTest.BindAndListenSeparatelySSLEncryptedKey.
2022-04-19 07:12:00 -04:00
Eli Schwartz abf3a67dd0 meson: fix regression that broke extracting version (#1253)
* meson: fix regression that broke extracting version

In commit 33f67386fe the code that
heuristically parsed the version broke due to the version being moved
around into a more easily accessible define.

While we are at it, pass the exact path of httplib.h to un-break usage
as a meson subproject. This was broken in commit
8ecdb11979 which checked the return code
of trying to get the version; it was always broken, but formerly failed
in silence and resulted in no version number.

* meson: use the compiler builtins to extract the version from the header

As a convenient string define, it is now possible to ask the
preprocessor what the version of cpp-httplib is. This can be used from
meson too, in order to avoid encoding C++ file structure into python
regexes.
2022-04-19 07:11:51 -04:00
yhirose d87abeecf0 Release v0.10.6 2022-04-17 17:34:48 -04:00
yhirose 4e28e4f741 Fix #1251 2022-04-17 11:53:41 -04:00
yhirose 80a55cedeb Removed Repl.it examples 2022-04-16 21:11:17 -04:00
15 changed files with 1591 additions and 769 deletions
+13 -31
View File
@@ -60,10 +60,10 @@
]]
cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR)
# Get the user agent and use it as a version
# This gets the string with the user agent from the header.
# Get the CPPHTTPLIB_VERSION value and use it as a version
# This gets the string with the CPPHTTPLIB_VERSION value from the header.
# This is so the maintainer doesn't actually need to update this manually.
file(STRINGS httplib.h _raw_version_string REGEX "User\-Agent.*cpp\-httplib/([0-9]+\.?)+")
file(STRINGS httplib.h _raw_version_string REGEX "CPPHTTPLIB_VERSION \"([0-9]+\\.[0-9]+\\.[0-9]+)\"")
# Needed since git tags have "v" prefixing them.
# Also used if the fallback to user agent string is being used.
@@ -100,6 +100,7 @@ if (BUILD_SHARED_LIBS AND WIN32 AND HTTPLIB_COMPILE)
endif()
# Threads needed for <thread> on some systems, and for <pthread.h> on Linux
set(THREADS_PREFER_PTHREAD_FLAG true)
find_package(Threads REQUIRED)
# Since Cmake v3.11, Crypto & SSL became optional when not specified as COMPONENTS.
if(HTTPLIB_REQUIRE_OPENSSL)
@@ -160,7 +161,7 @@ if(HTTPLIB_COMPILE)
ERROR_VARIABLE _httplib_split_error
)
if(_httplib_split_error)
message(FATAL_ERROR "Failed when trying to split Cpp-httplib with the Python script.\n${_httplib_split_error}")
message(FATAL_ERROR "Failed when trying to split cpp-httplib with the Python script.\n${_httplib_split_error}")
endif()
# split.py puts output in "out"
@@ -175,7 +176,7 @@ if(HTTPLIB_COMPILE)
set_target_properties(${PROJECT_NAME}
PROPERTIES
VERSION ${${PROJECT_NAME}_VERSION}
SOVERSION ${${PROJECT_NAME}_VERSION_MAJOR}
SOVERSION "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}"
)
else()
# This is for header-only.
@@ -187,22 +188,12 @@ endif()
# Only useful if building in-tree, versus using it from an installation.
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
# Might be missing some, but this list is somewhat comprehensive
# Require C++11
target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
cxx_std_11
cxx_nullptr
cxx_lambdas
cxx_override
cxx_defaulted_functions
cxx_attribute_deprecated
cxx_auto_type
cxx_decltype
cxx_deleted_functions
cxx_range_for
cxx_sizeof_member
)
target_include_directories(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
target_include_directories(${PROJECT_NAME} SYSTEM ${_INTERFACE_OR_PUBLIC}
$<BUILD_INTERFACE:${_httplib_build_includedir}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
@@ -230,15 +221,8 @@ target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
$<$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>:CPPHTTPLIB_OPENSSL_SUPPORT>
)
# Cmake's find_package search path is different based on the system
# See https://cmake.org/cmake/help/latest/command/find_package.html for the list
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(_TARGET_INSTALL_CMAKEDIR "${CMAKE_INSTALL_PREFIX}/cmake/${PROJECT_NAME}")
else()
# On Non-Windows, it should be /usr/lib/cmake/<name>/<name>Config.cmake
# NOTE: This may or may not work for macOS...
set(_TARGET_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
endif()
# CMake configuration files installation directory
set(_TARGET_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
include(CMakePackageConfigHelpers)
@@ -256,13 +240,13 @@ if(HTTPLIB_COMPILE)
write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake"
# Example: if you find_package(httplib 0.5.4)
# then anything >= 0.5 and <= 1.0 is accepted
COMPATIBILITY SameMajorVersion
COMPATIBILITY SameMinorVersion
)
else()
write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake"
# Example: if you find_package(httplib 0.5.4)
# then anything >= 0.5 and <= 1.0 is accepted
COMPATIBILITY SameMajorVersion
COMPATIBILITY SameMinorVersion
# Tells Cmake that it's a header-only lib
# Mildly useful for end-users :)
ARCH_INDEPENDENT
@@ -274,11 +258,9 @@ endif()
# and linkage information (doesn't find deps though).
install(TARGETS ${PROJECT_NAME}
EXPORT httplibTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(FILES "${_httplib_build_includedir}/httplib.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES "${_httplib_build_includedir}/httplib.h" TYPE INCLUDE)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
+21 -15
View File
@@ -7,12 +7,12 @@ A C++11 single-file header-only cross platform HTTP/HTTPS library.
It's extremely easy to setup. Just include the **httplib.h** file in your code!
NOTE: This is a multi-threaded 'blocking' HTTP library. If you are looking for a 'non-blocking' library, this is not the one that you want.
NOTE: This library uses 'blocking' socket I/O. If you are looking for a library with 'non-blocking' socket I/O, this is not the one that you want.
Simple examples
---------------
#### Server
#### Server (Multi-threaded)
```c++
#define CPPHTTPLIB_OPENSSL_SUPPORT
@@ -48,17 +48,12 @@ res->status;
res->body;
```
### Try out the examples on Repl.it!
1. Run server at https://repl.it/@yhirose/cpp-httplib-server
2. Run client at https://repl.it/@yhirose/cpp-httplib-client
SSL Support
-----------
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
NOTE: cpp-httplib currently supports only version 1.1.1.
NOTE: cpp-httplib currently supports only version 1.1.1 and 3.0.
```c++
#define CPPHTTPLIB_OPENSSL_SUPPORT
@@ -70,6 +65,7 @@ httplib::SSLServer svr("./cert.pem", "./key.pem");
// Client
httplib::Client cli("https://localhost:1234"); // scheme + host
httplib::SSLClient cli("localhost:1234"); // host
httplib::SSLClient cli("localhost", 1234); // host, port
// Use your CA bundle
cli.set_ca_cert_path("./ca-bundle.crt");
@@ -217,15 +213,23 @@ svr.set_error_handler([](const auto& req, auto& res) {
The exception handler gets called if a user routing handler throws an error.
```cpp
svr.set_exception_handler([](const auto& req, auto& res, std::exception &e) {
res.status = 500;
svr.set_exception_handler([](const auto& req, auto& res, std::exception_ptr ep) {
auto fmt = "<h1>Error 500</h1><p>%s</p>";
char buf[BUFSIZ];
snprintf(buf, sizeof(buf), fmt, e.what());
try {
std::rethrow_exception(ep);
} catch (std::exception &e) {
snprintf(buf, sizeof(buf), fmt, e.what());
} catch (...) { // See the following NOTE
snprintf(buf, sizeof(buf), fmt, "Unknown Exception");
}
res.set_content(buf, "text/html");
res.status = 500;
});
```
NOTE: if you don't provide the `catch (...)` block for a rethrown exception pointer, an uncaught exception will end up causing the server crash. Be careful!
### Pre routing handler
```cpp
@@ -440,7 +444,7 @@ int main(void)
}
} else {
auto err = res.error();
...
std::cout << "HTTP error: " << httplib::to_string(err) << std::endl;
}
}
```
@@ -473,7 +477,9 @@ enum Error {
SSLConnection,
SSLLoadingCerts,
SSLServerVerification,
UnsupportedMultipartBoundaryChars
UnsupportedMultipartBoundaryChars,
Compression,
ConnectionTimeout,
};
```
@@ -804,12 +810,12 @@ Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32
Note: cpp-httplib officially supports only the latest Visual Studio. It might work with former versions of Visual Studio, but I can no longer verify it. Pull requests are always welcome for the older versions of Visual Studio unless they break the C++11 conformance.
Note: Windows 8 or lower and Cygwin on Windows are not supported.
Note: Windows 8 or lower, Visual Studio 2013 or lower, and Cygwin on Windows are not supported.
License
-------
MIT license (© 2021 Yuji Hirose)
MIT license (© 2022 Yuji Hirose)
Special Thanks To
-----------------
+1 -1
View File
@@ -1,5 +1,5 @@
#/usr/bin/env bash
for i in {1..10000}
for i in {1..1000000}
do
echo "#### $i ####"
curl -X POST -F image_file=@$1 http://localhost:1234/post > /dev/null
+899 -691
View File
File diff suppressed because it is too large Load Diff
+9 -10
View File
@@ -19,22 +19,18 @@ project(
# Check just in case downstream decides to edit the source
# and add a project version
version = meson.project_version()
python3 = find_program('python3')
if version == 'undefined'
# Meson doesn't have regular expressions, but since it is implemented
# in python we can be sure we can use it to parse the file manually
version = run_command(
python3, '-c', 'import re; raw_version = re.search("User\-Agent.*cpp\-httplib/([0-9]+\.?)+", open("httplib.h").read()).group(0); print(re.search("([0-9]+\\.?)+", raw_version).group(0))',
check: true
).stdout().strip()
cxx = meson.get_compiler('cpp')
version = cxx.get_define('CPPHTTPLIB_VERSION',
prefix: '#include <httplib.h>',
include_directories: include_directories('.')).strip('"')
assert(version != '', 'failed to get version from httplib.h')
endif
message('cpp-httplib version ' + version)
deps = [dependency('threads')]
args = []
openssl_dep = dependency('openssl', version: ['>=1.1.1', '<1.1.2'], required: get_option('cpp-httplib_openssl'))
openssl_dep = dependency('openssl', version: '>=1.1.1', required: get_option('cpp-httplib_openssl'))
if openssl_dep.found()
deps += openssl_dep
args += '-DCPPHTTPLIB_OPENSSL_SUPPORT'
@@ -65,6 +61,8 @@ endif
cpp_httplib_dep = dependency('', required: false)
if get_option('cpp-httplib_compile')
python3 = find_program('python3')
httplib_ch = custom_target(
'split',
input: 'httplib.h',
@@ -79,6 +77,7 @@ if get_option('cpp-httplib_compile')
dependencies: deps,
cpp_args: args,
version: version,
soversion: version.split('.')[0] + '.' + version.split('.')[1],
install: true
)
cpp_httplib_dep = declare_dependency(compile_args: args, dependencies: deps, link_with: lib, sources: httplib_ch[1])
+1 -1
View File
@@ -5,5 +5,5 @@
option('cpp-httplib_openssl', type: 'feature', value: 'auto', description: 'Enable OpenSSL support')
option('cpp-httplib_zlib', type: 'feature', value: 'auto', description: 'Enable zlib support')
option('cpp-httplib_brotli', type: 'feature', value: 'auto', description: 'Enable Brotli support')
option('cpp-httplib_compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file')
option('cpp-httplib_compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file (requires python3)')
option('cpp-httplib_test', type: 'boolean', value: false, description: 'Build tests')
+1 -1
View File
@@ -1,5 +1,5 @@
CXX = clang++
CXXFLAGS = -g -std=c++11 -I. -Wall -Wextra -Wtype-limits -Wconversion # -fno-exceptions -DCPPHTTPLIB_NO_EXCEPTIONS -fsanitize=address
CXXFLAGS = -g -std=c++11 -I. -Wall -Wextra -Wtype-limits -Wconversion -Wshadow # -fno-exceptions -DCPPHTTPLIB_NO_EXCEPTIONS -fsanitize=address
PREFIX = /usr/local
#PREFIX = $(shell brew --prefix)
File diff suppressed because one or more lines are too long
+2
View File
@@ -100,6 +100,8 @@ test(
key_pem,
cert_pem,
cert2_pem,
key_encrypted_pem,
cert_encrypted_pem,
rootca_key_pem,
rootca_cert_pem,
client_key_pem,
+622 -18
View File
@@ -109,11 +109,11 @@ TEST(SplitTest, ParseQueryString) {
detail::split(s.c_str(), s.c_str() + s.size(), '&',
[&](const char *b, const char *e) {
string key, val;
detail::split(b, e, '=', [&](const char *b, const char *e) {
detail::split(b, e, '=', [&](const char *b2, const char *e2) {
if (key.empty()) {
key.assign(b, e);
key.assign(b2, e2);
} else {
val.assign(b, e);
val.assign(b2, e2);
}
});
dic.emplace(key, val);
@@ -1249,8 +1249,11 @@ TEST(ExceptionHandlerTest, ContentLength) {
Server svr;
svr.set_exception_handler([](const Request & /*req*/, Response &res,
std::exception &e) {
EXPECT_EQ("abc", std::string(e.what()));
std::exception_ptr ep) {
EXPECT_FALSE(ep == nullptr);
try {
std::rethrow_exception(ep);
} catch (std::exception &e) { EXPECT_EQ("abc", std::string(e.what())); }
res.status = 500;
res.set_content("abcdefghijklmnopqrstuvwxyz",
"text/html"); // <= Content-Length still 13 at this point
@@ -1640,7 +1643,8 @@ protected:
res.set_content_provider(
size_t(-1), "text/plain",
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
if (!sink.is_writable()) return false;
sink.os << "data_chunk";
return true;
});
@@ -1711,6 +1715,22 @@ protected:
EXPECT_EQ("0", req.get_header_value("Content-Length"));
res.set_content("empty-no-content-type", "text/plain");
})
.Post("/path-only",
[&](const Request &req, Response &res) {
EXPECT_EQ(req.body, "");
EXPECT_EQ("", req.get_header_value("Content-Type"));
EXPECT_EQ("0", req.get_header_value("Content-Length"));
res.set_content("path-only", "text/plain");
})
.Post("/path-headers-only",
[&](const Request &req, Response &res) {
EXPECT_EQ(req.body, "");
EXPECT_EQ("", req.get_header_value("Content-Type"));
EXPECT_EQ("0", req.get_header_value("Content-Length"));
EXPECT_EQ("world", req.get_header_value("hello"));
EXPECT_EQ("world2", req.get_header_value("hello2"));
res.set_content("path-headers-only", "text/plain");
})
.Post("/post-large",
[&](const Request &req, Response &res) {
EXPECT_EQ(req.body, LARGE_DATA);
@@ -2121,6 +2141,21 @@ TEST_F(ServerTest, PostEmptyContentWithNoContentType) {
ASSERT_EQ("empty-no-content-type", res->body);
}
TEST_F(ServerTest, PostPathOnly) {
auto res = cli_.Post("/path-only");
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
ASSERT_EQ("path-only", res->body);
}
TEST_F(ServerTest, PostPathAndHeadersOnly) {
auto res = cli_.Post("/path-headers-only",
Headers({{"hello", "world"}, {"hello2", "world2"}}));
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
ASSERT_EQ("path-headers-only", res->body);
}
TEST_F(ServerTest, PostLarge) {
auto res = cli_.Post("/post-large", LARGE_DATA, "text/plain");
ASSERT_TRUE(res);
@@ -3015,8 +3050,10 @@ TEST(GzipDecompressor, ChunkedDecompression) {
httplib::detail::gzip_compressor compressor;
bool result = compressor.compress(
data.data(), data.size(),
/*last=*/true, [&](const char *data, size_t size) {
compressed_data.insert(compressed_data.size(), data, size);
/*last=*/true,
[&](const char *compressed_data_chunk, size_t compressed_data_size) {
compressed_data.insert(compressed_data.size(), compressed_data_chunk,
compressed_data_size);
return true;
});
ASSERT_TRUE(result);
@@ -3035,8 +3072,11 @@ TEST(GzipDecompressor, ChunkedDecompression) {
std::min(compressed_data.size() - chunk_begin, chunk_size);
bool result = decompressor.decompress(
compressed_data.data() + chunk_begin, current_chunk_size,
[&](const char *data, size_t size) {
decompressed_data.insert(decompressed_data.size(), data, size);
[&](const char *decompressed_data_chunk,
size_t decompressed_data_chunk_size) {
decompressed_data.insert(decompressed_data.size(),
decompressed_data_chunk,
decompressed_data_chunk_size);
return true;
});
ASSERT_TRUE(result);
@@ -3799,9 +3839,12 @@ TEST(MountTest, Unmount) {
TEST(ExceptionTest, ThrowExceptionInHandler) {
Server svr;
svr.Get("/hi", [&](const Request & /*req*/, Response & /*res*/) {
svr.Get("/exception", [&](const Request & /*req*/, Response & /*res*/) {
throw std::runtime_error("exception...");
// res.set_content("Hello World!", "text/plain");
});
svr.Get("/unknown", [&](const Request & /*req*/, Response & /*res*/) {
throw std::runtime_error("exception\r\n...");
});
auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
@@ -3814,11 +3857,21 @@ TEST(ExceptionTest, ThrowExceptionInHandler) {
Client cli("localhost", PORT);
auto res = cli.Get("/hi");
ASSERT_TRUE(res);
EXPECT_EQ(500, res->status);
ASSERT_TRUE(res->has_header("EXCEPTION_WHAT"));
EXPECT_EQ("exception...", res->get_header_value("EXCEPTION_WHAT"));
{
auto res = cli.Get("/exception");
ASSERT_TRUE(res);
EXPECT_EQ(500, res->status);
ASSERT_TRUE(res->has_header("EXCEPTION_WHAT"));
EXPECT_EQ("exception...", res->get_header_value("EXCEPTION_WHAT"));
}
{
auto res = cli.Get("/unknown");
ASSERT_TRUE(res);
EXPECT_EQ(500, res->status);
ASSERT_TRUE(res->has_header("EXCEPTION_WHAT"));
EXPECT_EQ("exception\\r\\n...", res->get_header_value("EXCEPTION_WHAT"));
}
svr.stop();
listen_thread.join();
@@ -4172,7 +4225,8 @@ protected:
res.set_content("Hello World!", "text/plain");
});
t_ = thread([&]() { ASSERT_TRUE(svr_.listen(nullptr, PORT, AI_PASSIVE)); });
t_ = thread(
[&]() { ASSERT_TRUE(svr_.listen(std::string(), PORT, AI_PASSIVE)); });
while (!svr_.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
@@ -4660,6 +4714,50 @@ TEST(SSLClientServerTest, CustomizeServerSSLCtx) {
t.join();
}
// Disabled due to the out-of-memory problem on GitHub Actions Workflows
TEST(SSLClientServerTest, DISABLED_LargeDataTransfer) {
// prepare large data
std::random_device seed_gen;
std::mt19937 random(seed_gen());
constexpr auto large_size_byte = 2147483648UL + 1048576UL; // 2GiB + 1MiB
std::vector<std::uint32_t> binary(large_size_byte / sizeof(std::uint32_t));
std::generate(binary.begin(), binary.end(), [&random]() { return random(); });
// server
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
ASSERT_TRUE(svr.is_valid());
svr.Post("/binary", [&](const Request &req, Response &res) {
EXPECT_EQ(large_size_byte, req.body.size());
EXPECT_EQ(0, std::memcmp(binary.data(), req.body.data(), large_size_byte));
res.set_content(req.body, "application/octet-stream");
});
auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
// client POST
SSLClient cli("localhost", PORT);
cli.enable_server_certificate_verification(false);
cli.set_read_timeout(std::chrono::seconds(100));
cli.set_write_timeout(std::chrono::seconds(100));
auto res = cli.Post("/binary", reinterpret_cast<char *>(binary.data()),
large_size_byte, "application/octet-stream");
// compare
EXPECT_EQ(200, res->status);
EXPECT_EQ(large_size_byte, res->body.size());
EXPECT_EQ(0, std::memcmp(binary.data(), res->body.data(), large_size_byte));
// cleanup
svr.stop();
listen_thread.join();
ASSERT_FALSE(svr.is_running());
}
#endif
#ifdef _WIN32
@@ -4698,6 +4796,77 @@ TEST(SendAPI, SimpleInterface_Online) {
EXPECT_EQ(301, res->status);
}
TEST(ClientImplMethods, GetSocketTest) {
httplib::Server svr;
svr.Get("/", [&](const httplib::Request & /*req*/, httplib::Response &res) {
res.status = 200;
});
auto thread = std::thread([&]() { svr.listen("127.0.0.1", 3333); });
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
{
httplib::Client cli("http://127.0.0.1:3333");
cli.set_keep_alive(true);
// Use the behavior of cpp-httplib of opening the connection
// only when the first request happens. If that changes,
// this test would be obsolete.
EXPECT_EQ(cli.socket(), INVALID_SOCKET);
// This also implicitly tests the server. But other tests would fail much
// earlier than this one to be considered.
auto res = cli.Get("/");
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
ASSERT_TRUE(cli.socket() != INVALID_SOCKET);
}
svr.stop();
thread.join();
ASSERT_FALSE(svr.is_running());
}
// Disabled due to out-of-memory problem on GitHub Actions
#ifdef _WIN64
TEST(ServerLargeContentTest, DISABLED_SendLargeContent) {
// allocate content size larger than 2GB in memory
const size_t content_size = 2LL * 1024LL * 1024LL * 1024LL + 1LL;
char *content = (char *)malloc(content_size);
ASSERT_TRUE(content);
Server svr;
svr.Get("/foo", [=](const httplib::Request &req, httplib::Response &resp) {
resp.set_content(content, content_size, "application/octet-stream");
});
auto listen_thread = std::thread([&svr]() { svr.listen(HOST, 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(HOST, PORT);
auto res = cli.Get("/foo");
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
EXPECT_EQ(content_size, res->body.length());
free(content);
svr.stop();
listen_thread.join();
ASSERT_FALSE(svr.is_running());
}
#endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
TEST(YahooRedirectTest2, SimpleInterface_Online) {
Client cli("http://yahoo.com");
@@ -4896,5 +5065,440 @@ TEST(MultipartFormDataTest, LargeData) {
svr.stop();
t.join();
}
TEST(MultipartFormDataTest, WithPreamble) {
Server svr;
svr.Post("/post", [&](const Request & /*req*/, Response &res) {
res.set_content("ok", "text/plain");
});
thread t = thread([&] { svr.listen(HOST, PORT); });
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
const std::string body =
"This is the preamble. It is to be ignored, though it\r\n"
"is a handy place for composition agents to include an\r\n"
"explanatory note to non-MIME conformant readers.\r\n"
"\r\n"
"\r\n"
"--simple boundary\r\n"
"Content-Disposition: form-data; name=\"field1\"\r\n"
"\r\n"
"value1\r\n"
"--simple boundary\r\n"
"Content-Disposition: form-data; name=\"field2\"; "
"filename=\"example.txt\"\r\n"
"\r\n"
"value2\r\n"
"--simple boundary--\r\n"
"This is the epilogue. It is also to be ignored.\r\n";
std::string content_type =
R"(multipart/form-data; boundary="simple boundary")";
Client cli(HOST, PORT);
auto res = cli.Post("/post", body, content_type.c_str());
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
svr.stop();
t.join();
}
TEST(MultipartFormDataTest, PostCustomBoundary) {
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
svr.Post("/post_customboundary", [&](const Request &req, Response & /*res*/,
const ContentReader &content_reader) {
if (req.is_multipart_form_data()) {
MultipartFormDataItems files;
content_reader(
[&](const MultipartFormData &file) {
files.push_back(file);
return true;
},
[&](const char *data, size_t data_length) {
files.back().content.append(data, data_length);
return true;
});
EXPECT_TRUE(std::string(files[0].name) == "document");
EXPECT_EQ(size_t(1024 * 1024 * 2), files[0].content.size());
EXPECT_TRUE(files[0].filename == "2MB_data");
EXPECT_TRUE(files[0].content_type == "application/octet-stream");
EXPECT_TRUE(files[1].name == "hello");
EXPECT_TRUE(files[1].content == "world");
EXPECT_TRUE(files[1].filename == "");
EXPECT_TRUE(files[1].content_type == "");
} else {
std::string body;
content_reader([&](const char *data, size_t data_length) {
body.append(data, data_length);
return true;
});
}
});
auto t = std::thread([&]() { svr.listen("localhost", 8080); });
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::string data(1024 * 1024 * 2, '.');
std::stringstream buffer;
buffer << data;
Client cli("https://localhost:8080");
cli.enable_server_certificate_verification(false);
MultipartFormDataItems items{
{"document", buffer.str(), "2MB_data", "application/octet-stream"},
{"hello", "world", "", ""},
};
auto res = cli.Post("/post_customboundary", {}, items, "abc-abc");
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
}
svr.stop();
t.join();
}
TEST(MultipartFormDataTest, PostInvalidBoundaryChars) {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::string data(1024 * 1024 * 2, '&');
std::stringstream buffer;
buffer << data;
Client cli("https://localhost:8080");
MultipartFormDataItems items{
{"document", buffer.str(), "2MB_data", "application/octet-stream"},
{"hello", "world", "", ""},
};
for (const char &c : " \t\r\n") {
auto res =
cli.Post("/invalid_boundary", {}, items, string("abc123").append(1, c));
ASSERT_EQ(Error::UnsupportedMultipartBoundaryChars, res.error());
ASSERT_FALSE(res);
}
}
TEST(MultipartFormDataTest, PutFormData) {
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
svr.Put("/put", [&](const Request &req, const Response & /*res*/,
const ContentReader &content_reader) {
if (req.is_multipart_form_data()) {
MultipartFormDataItems files;
content_reader(
[&](const MultipartFormData &file) {
files.push_back(file);
return true;
},
[&](const char *data, size_t data_length) {
files.back().content.append(data, data_length);
return true;
});
EXPECT_TRUE(std::string(files[0].name) == "document");
EXPECT_EQ(size_t(1024 * 1024 * 2), files[0].content.size());
EXPECT_TRUE(files[0].filename == "2MB_data");
EXPECT_TRUE(files[0].content_type == "application/octet-stream");
EXPECT_TRUE(files[1].name == "hello");
EXPECT_TRUE(files[1].content == "world");
EXPECT_TRUE(files[1].filename == "");
EXPECT_TRUE(files[1].content_type == "");
} else {
std::string body;
content_reader([&](const char *data, size_t data_length) {
body.append(data, data_length);
return true;
});
}
});
auto t = std::thread([&]() { svr.listen("localhost", 8080); });
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::string data(1024 * 1024 * 2, '&');
std::stringstream buffer;
buffer << data;
Client cli("https://localhost:8080");
cli.enable_server_certificate_verification(false);
MultipartFormDataItems items{
{"document", buffer.str(), "2MB_data", "application/octet-stream"},
{"hello", "world", "", ""},
};
auto res = cli.Put("/put", items);
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
}
svr.stop();
t.join();
}
TEST(MultipartFormDataTest, PutFormDataCustomBoundary) {
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
svr.Put("/put_customboundary",
[&](const Request &req, const Response & /*res*/,
const ContentReader &content_reader) {
if (req.is_multipart_form_data()) {
MultipartFormDataItems files;
content_reader(
[&](const MultipartFormData &file) {
files.push_back(file);
return true;
},
[&](const char *data, size_t data_length) {
files.back().content.append(data, data_length);
return true;
});
EXPECT_TRUE(std::string(files[0].name) == "document");
EXPECT_EQ(size_t(1024 * 1024 * 2), files[0].content.size());
EXPECT_TRUE(files[0].filename == "2MB_data");
EXPECT_TRUE(files[0].content_type == "application/octet-stream");
EXPECT_TRUE(files[1].name == "hello");
EXPECT_TRUE(files[1].content == "world");
EXPECT_TRUE(files[1].filename == "");
EXPECT_TRUE(files[1].content_type == "");
} else {
std::string body;
content_reader([&](const char *data, size_t data_length) {
body.append(data, data_length);
return true;
});
}
});
auto t = std::thread([&]() { svr.listen("localhost", 8080); });
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::string data(1024 * 1024 * 2, '&');
std::stringstream buffer;
buffer << data;
Client cli("https://localhost:8080");
cli.enable_server_certificate_verification(false);
MultipartFormDataItems items{
{"document", buffer.str(), "2MB_data", "application/octet-stream"},
{"hello", "world", "", ""},
};
auto res = cli.Put("/put_customboundary", {}, items, "abc-abc_");
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
}
svr.stop();
t.join();
}
TEST(MultipartFormDataTest, PutInvalidBoundaryChars) {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::string data(1024 * 1024 * 2, '&');
std::stringstream buffer;
buffer << data;
Client cli("https://localhost:8080");
cli.enable_server_certificate_verification(false);
MultipartFormDataItems items{
{"document", buffer.str(), "2MB_data", "application/octet-stream"},
{"hello", "world", "", ""},
};
for (const char &c : " \t\r\n") {
auto res = cli.Put("/put", {}, items, string("abc123").append(1, c));
ASSERT_EQ(Error::UnsupportedMultipartBoundaryChars, res.error());
ASSERT_FALSE(res);
}
}
#endif
#ifndef _WIN32
class UnixSocketTest : public ::testing::Test {
protected:
void TearDown() override { std::remove(pathname_.c_str()); }
void client_GET(const std::string &addr) {
httplib::Client cli{addr};
cli.set_address_family(AF_UNIX);
ASSERT_TRUE(cli.is_valid());
const auto &result = cli.Get(pattern_);
ASSERT_TRUE(result) << "error: " << result.error();
const auto &resp = result.value();
EXPECT_EQ(resp.status, 200);
EXPECT_EQ(resp.body, content_);
}
const std::string pathname_{"./httplib-server.sock"};
const std::string pattern_{"/hi"};
const std::string content_{"Hello World!"};
};
TEST_F(UnixSocketTest, pathname) {
httplib::Server svr;
svr.Get(pattern_, [&](const httplib::Request &, httplib::Response &res) {
res.set_content(content_, "text/plain");
});
std::thread t{[&] {
ASSERT_TRUE(svr.set_address_family(AF_UNIX).listen(pathname_, 80));
}};
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_TRUE(svr.is_running());
client_GET(pathname_);
svr.stop();
t.join();
}
#if defined(__linux__) || \
/* __APPLE__ */ (defined(SOL_LOCAL) && defined(SO_PEERPID))
TEST_F(UnixSocketTest, PeerPid) {
httplib::Server svr;
std::string remote_port_val;
svr.Get(pattern_, [&](const httplib::Request &req, httplib::Response &res) {
res.set_content(content_, "text/plain");
remote_port_val = req.get_header_value("REMOTE_PORT");
});
std::thread t{[&] {
ASSERT_TRUE(svr.set_address_family(AF_UNIX).listen(pathname_, 80));
}};
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_TRUE(svr.is_running());
client_GET(pathname_);
EXPECT_EQ(std::to_string(getpid()), remote_port_val);
svr.stop();
t.join();
}
#endif
#ifdef __linux__
TEST_F(UnixSocketTest, abstract) {
constexpr char svr_path[]{"\x00httplib-server.sock"};
const std::string abstract_addr{svr_path, sizeof(svr_path) - 1};
httplib::Server svr;
svr.Get(pattern_, [&](const httplib::Request &, httplib::Response &res) {
res.set_content(content_, "text/plain");
});
std::thread t{[&] {
ASSERT_TRUE(svr.set_address_family(AF_UNIX).listen(abstract_addr, 80));
}};
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_TRUE(svr.is_running());
client_GET(abstract_addr);
svr.stop();
t.join();
}
#endif
TEST(SocketStream, is_writable_UNIX) {
int fds[2];
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, fds));
const auto asSocketStream = [&](socket_t fd,
std::function<bool(Stream &)> func) {
return detail::process_client_socket(fd, 0, 0, 0, 0, func);
};
asSocketStream(fds[0], [&](Stream &s0) {
EXPECT_EQ(s0.socket(), fds[0]);
EXPECT_TRUE(s0.is_writable());
EXPECT_EQ(0, close(fds[1]));
EXPECT_FALSE(s0.is_writable());
return true;
});
EXPECT_EQ(0, close(fds[0]));
}
TEST(SocketStream, is_writable_INET) {
sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT + 1);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
int disconnected_svr_sock = -1;
std::thread svr{[&] {
const int s = socket(AF_INET, SOCK_STREAM, 0);
ASSERT_LE(0, s);
ASSERT_EQ(0, ::bind(s, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)));
ASSERT_EQ(0, listen(s, 1));
ASSERT_LE(0, disconnected_svr_sock = accept(s, nullptr, nullptr));
ASSERT_EQ(0, close(s));
}};
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::thread cli{[&] {
const int s = socket(AF_INET, SOCK_STREAM, 0);
ASSERT_LE(0, s);
ASSERT_EQ(0, connect(s, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)));
ASSERT_EQ(0, close(s));
}};
cli.join();
svr.join();
ASSERT_NE(disconnected_svr_sock, -1);
const auto asSocketStream = [&](socket_t fd,
std::function<bool(Stream &)> func) {
return detail::process_client_socket(fd, 0, 0, 0, 0, func);
};
asSocketStream(disconnected_svr_sock, [&](Stream &ss) {
EXPECT_EQ(ss.socket(), disconnected_svr_sock);
EXPECT_FALSE(ss.is_writable());
return true;
});
ASSERT_EQ(0, close(disconnected_svr_sock));
}
#endif // #ifndef _WIN32
+2
View File
@@ -116,6 +116,7 @@
<AdditionalUsingDirectories>
</AdditionalUsingDirectories>
<SDLCheck>true</SDLCheck>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@@ -158,6 +159,7 @@
<AdditionalUsingDirectories>
</AdditionalUsingDirectories>
<SDLCheck>true</SDLCheck>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
+1 -1
View File
@@ -82,7 +82,7 @@ TEST(RedirectTest, YouTubeNoSSLBasic) {
RedirectProxyText(cli, "/", true);
}
TEST(RedirectTest, YouTubeNoSSLDigest) {
TEST(RedirectTest, DISABLED_YouTubeNoSSLDigest) {
Client cli("youtube.com");
RedirectProxyText(cli, "/", false);
}