Compare commits

..

27 Commits

Author SHA1 Message Date
yhirose d73395e1dc Release v0.10.3 2022-02-22 10:21:27 -05:00
au-ee 64d001162b CPPHTTPLIB_NO_DEFAULT_USER_AGENT skips default user agent (#1201) 2022-02-22 09:39:26 -05:00
yhirose bb00a23116 Apply clangformat 2022-02-09 17:16:47 -05:00
yhirose 63d6e9b91b Removed up.sh and down.sh 2022-02-09 17:16:18 -05:00
Edwin Kofler 66eed5681a Fix typo in Dockerfile (#1187) 2022-02-06 09:15:15 -05:00
Andrea Pappacoda 8ecdb11979 build(meson): always install a pkg-config file (#1182)
A pkg-config file was previously installed only if cpp-httplib was being
built as a compiled library.

Since architecture-independent .pc files
can exist in /usr/share/pkgconfig, it can be useful to install one even
when installing the header-only version (for example, it could be used
by third party projects to easily find out if cpp-httplib is installed
and its version, using something like Meson's `dependency()` or CMake's
`pkg_check_modules()`).

The change makes the Meson build behave a bit more like the CMake one,
as it also always installs a CMake Config file, but here the pkg-config
file gets installed to the correct architecture-independent directory
(`datadir` represents /usr/share on Linux and simiar systems).

Lastly, I made some minor cleanups.
2022-02-03 19:50:49 -05:00
Andrea Pappacoda 894fcc8e02 test: add missing "_Online" suffix (#1183)
This test fails reproducibly in a Debian build chroot, and they generally don't have internet access
2022-01-30 12:34:52 -05:00
Rockybilly 7f43f0f3ff User-Agent update cpp-httplib/0.10.2 (#1181) 2022-01-28 12:27:27 -05:00
Gregor Jasny 87e03dd1ce Report connection timeout as separate event (#1171) 2022-01-26 17:32:40 -05:00
yhirose e5cacb465d Fix #1172 (#1173)
This change is based on RFC7230, § 3.5 'Message Parsing Robustness': "Although the line terminator for the start-line and header fields is the sequence CRLF, a recipient MAY recognize a single LF as a line terminator and ignore any preceding CR."
2022-01-26 13:34:23 -05:00
yhirose ee8371f753 Added 'PostLarge' unit test for #1169 2022-01-22 09:52:27 -05:00
yhirose 081723f983 Add another fuzz test corpus 2022-01-20 15:27:26 -05:00
yhirose b61f36579c Fix #1166 2022-01-20 15:24:09 -05:00
ArnaudBienner 33f53aa458 Fix set_content_provider example in README.md (#1163) 2022-01-13 12:26:34 -05:00
yhirose 412ab5f063 Added example/Dockerfile.hello 2022-01-11 00:18:20 -05:00
yhirose 11e02e901c Fixed unit test 2021-12-31 16:10:57 -05:00
yhirose 65a8f4cf44 Added hosted_at. (Resolve #1113) 2021-12-31 15:35:52 -05:00
yhirose 27d128bbb4 Fix problems in #1154 2021-12-31 14:55:40 -05:00
yhirose 070f9bec58 Code cleanup 2021-12-31 13:27:47 -05:00
c00c f817032513 fix socket option setting for windows (#1154)
* fix socket option setting for windows

* misc

Co-authored-by: zhangsen <zhangsen@cyberkl.com>
2021-12-31 10:07:59 -05:00
yhirose 17abe221c0 Fix is_file problem on Windows (#1153) 2021-12-30 14:54:57 -05:00
vitaly-ivanov 4a7a81e039 Work around silly Win defines to support BoringSSL (#1148)
* Work around silly Win defines to support BoringSSL

* changes wrapped into ifdef(_WIN32) just in case
2021-12-30 11:08:51 -05:00
yhirose 37fd4eb643 Code cleanup 2021-12-26 07:53:56 -05:00
yhirose 865b0e4c03 Resolve #1145 2021-12-26 07:53:06 -05:00
yhirose b324921c1a Release v0.10.1 2021-12-24 21:01:06 -05:00
yhirose 63f72caf30 Fix "Issue 42689 in oss-fuzz: cpp-httplib:server_fuzzer: Timeout in server_fuzzer" 2021-12-24 20:58:09 -05:00
yhirose 99ac17b90a Fix #1140 2021-12-23 23:19:14 -05:00
11 changed files with 256 additions and 58 deletions
+1 -1
View File
@@ -303,7 +303,7 @@ Without content length:
svr.Get("/stream", [&](const Request &req, Response &res) {
res.set_content_provider(
"text/plain", // Content type
[&](size_t offset, size_t length, DataSink &sink) {
[&](size_t offset, DataSink &sink) {
if (/* there is still data */) {
std::vector<char> data;
// prepare data...
+12
View File
@@ -0,0 +1,12 @@
FROM alpine as builder
WORKDIR /src/example
RUN apk add g++ make openssl-dev zlib-dev brotli-dev
COPY ./httplib.h /src
COPY ./example/hello.cc /src/example
COPY ./example/Makefile /src/example
RUN make hello
FROM alpine
RUN apk --no-cache add brotli libstdc++
COPY --from=builder /src/example/hello /bin/hello
CMD ["/bin/hello"]
+1 -1
View File
@@ -1,5 +1,5 @@
#CXX = clang++
CXXFLAGS = -std=c++11 -I.. -Wall -Wextra -pthread
CXXFLAGS = -O2 -std=c++11 -I.. -Wall -Wextra -pthread
PREFIX = /usr/local
#PREFIX = $(shell brew --prefix)
+1 -1
View File
@@ -15,5 +15,5 @@ int main(void) {
res.set_content("Hello World!", "text/plain");
});
svr.listen("localhost", 8080);
svr.listen("0.0.0.0", 8080);
}
+169 -47
View File
@@ -60,6 +60,10 @@
#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
#endif
#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH
#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
#endif
#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
#endif
@@ -213,6 +217,15 @@ using socket_t = int;
#include <thread>
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
// these are defined in wincrypt.h and it breaks compilation if BoringSSL is
// used
#ifdef _WIN32
#undef X509_NAME
#undef X509_CERT_PAIR
#undef X509_EXTENSIONS
#undef PKCS7_SIGNER_INFO
#endif
#include <openssl/err.h>
#include <openssl/md5.h>
#include <openssl/ssl.h>
@@ -786,6 +799,7 @@ enum class Error {
SSLServerVerification,
UnsupportedMultipartBoundaryChars,
Compression,
ConnectionTimeout,
};
std::string to_string(const Error error);
@@ -1581,6 +1595,7 @@ inline std::string to_string(const Error error) {
case Error::UnsupportedMultipartBoundaryChars:
return "UnsupportedMultipartBoundaryChars";
case Error::Compression: return "Compression";
case Error::ConnectionTimeout: return "ConnectionTimeout";
case Error::Unknown: return "Unknown";
default: break;
}
@@ -1644,6 +1659,10 @@ Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
* .h + .cc.
*/
std::string hosted_at(const char *hostname);
void hosted_at(const char *hostname, std::vector<std::string> &addrs);
std::string append_query_params(const char *path, const Params &params);
std::pair<std::string, std::string> make_range_header(Ranges ranges);
@@ -1657,6 +1676,8 @@ namespace detail {
std::string encode_query_param(const std::string &value);
std::string decode_url(const std::string &s, bool convert_plus_to_space);
void read_file(const std::string &path, std::string &out);
std::string trim_copy(const std::string &s);
@@ -1936,8 +1957,12 @@ inline std::string base64_encode(const std::string &in) {
}
inline bool is_file(const std::string &path) {
#ifdef _WIN32
return _access_s(path.c_str(), 0) == 0;
#else
struct stat st;
return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode);
#endif
}
inline bool is_dir(const std::string &path) {
@@ -2290,7 +2315,8 @@ inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
#endif
}
inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
inline Error wait_until_socket_is_ready(socket_t sock, time_t sec,
time_t usec) {
#ifdef CPPHTTPLIB_USE_POLL
struct pollfd pfd_read;
pfd_read.fd = sock;
@@ -2300,17 +2326,21 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
if (poll_res == 0) { return Error::ConnectionTimeout; }
if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
int error = 0;
socklen_t len = sizeof(error);
auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char *>(&error), &len);
return res >= 0 && !error;
auto successful = res >= 0 && !error;
return successful ? Error::Success : Error::Connection;
}
return false;
return Error::Connection;
#else
#ifndef _WIN32
if (sock >= FD_SETSIZE) { return false; }
if (sock >= FD_SETSIZE) { return Error::Connection; }
#endif
fd_set fdsr;
@@ -2328,14 +2358,17 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
return select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv);
});
if (ret == 0) { return Error::ConnectionTimeout; }
if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
int error = 0;
socklen_t len = sizeof(error);
return getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char *>(&error), &len) >= 0 &&
!error;
auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char *>(&error), &len);
auto successful = res >= 0 && !error;
return successful ? Error::Success : Error::Connection;
}
return false;
return Error::Connection;
#endif
}
@@ -2480,25 +2513,28 @@ socket_t create_socket(const char *host, const char *ip, int port,
SocketOptions socket_options,
BindOrConnect bind_or_connect) {
// Get address info
const char *node = nullptr;
struct addrinfo hints;
struct addrinfo *result;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = address_family;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = socket_flags;
hints.ai_protocol = 0;
// Ask getaddrinfo to convert IP in c-string to address
if (ip[0] != '\0') {
node = ip;
// Ask getaddrinfo to convert IP in c-string to address
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_NUMERICHOST;
} else {
node = host;
hints.ai_family = address_family;
hints.ai_flags = socket_flags;
}
auto service = std::to_string(port);
if (ip[0] != '\0' ? getaddrinfo(ip, service.c_str(), &hints, &result)
: getaddrinfo(host, service.c_str(), &hints, &result)) {
if (getaddrinfo(node, service.c_str(), &hints, &result)) {
#if defined __linux__ && !defined __ANDROID__
res_init();
#endif
@@ -2658,27 +2694,43 @@ inline socket_t create_client_socket(
::connect(sock2, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
if (ret < 0) {
if (is_connection_error() ||
!wait_until_socket_is_ready(sock2, connection_timeout_sec,
connection_timeout_usec)) {
if (is_connection_error()) {
error = Error::Connection;
return false;
}
error = wait_until_socket_is_ready(sock2, connection_timeout_sec,
connection_timeout_usec);
if (error != Error::Success) { return false; }
}
set_nonblocking(sock2, false);
{
#ifdef _WIN32
auto timeout = static_cast<uint32_t>(read_timeout_sec * 1000 +
read_timeout_usec / 1000);
setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
sizeof(timeout));
#else
timeval tv;
tv.tv_sec = static_cast<long>(read_timeout_sec);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec);
setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
#endif
}
{
#ifdef _WIN32
auto timeout = static_cast<uint32_t>(write_timeout_sec * 1000 +
write_timeout_usec / 1000);
setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
sizeof(timeout));
#else
timeval tv;
tv.tv_sec = static_cast<long>(write_timeout_sec);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec);
setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv));
#endif
}
error = Error::Success;
@@ -2694,7 +2746,7 @@ inline socket_t create_client_socket(
return sock;
}
inline void get_remote_ip_and_port(const struct sockaddr_storage &addr,
inline bool get_remote_ip_and_port(const struct sockaddr_storage &addr,
socklen_t addr_len, std::string &ip,
int &port) {
if (addr.ss_family == AF_INET) {
@@ -2702,14 +2754,19 @@ inline void get_remote_ip_and_port(const struct sockaddr_storage &addr,
} else if (addr.ss_family == AF_INET6) {
port =
ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);
} else {
return false;
}
std::array<char, NI_MAXHOST> ipstr{};
if (!getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,
ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,
0, NI_NUMERICHOST)) {
ip = ipstr.data();
if (getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,
ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,
0, NI_NUMERICHOST)) {
return false;
}
ip = ipstr.data();
return true;
}
inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
@@ -3171,15 +3228,26 @@ inline bool read_headers(Stream &strm, Headers &headers) {
if (!line_reader.getline()) { return false; }
// Check if the line ends with CRLF.
auto line_terminator_len = 2;
if (line_reader.end_with_crlf()) {
// Blank line indicates end of headers.
if (line_reader.size() == 2) { break; }
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
} else {
// Blank line indicates end of headers.
if (line_reader.size() == 1) { break; }
line_terminator_len = 1;
}
#else
} else {
continue; // Skip invalid line.
}
#endif
// Exclude CRLF
auto end = line_reader.ptr() + line_reader.size() - 2;
if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
// Exclude line terminator
auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
parse_header(line_reader.ptr(), end,
[&](std::string &&key, std::string &&val) {
@@ -3703,6 +3771,7 @@ public:
}
case 2: { // Headers
auto pos = buf_find(crlf_);
if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
while (pos < buf_size()) {
// Empty line
if (pos == 0) {
@@ -3866,7 +3935,7 @@ private:
void buf_append(const char *data, size_t n) {
auto remaining_size = buf_size();
if (remaining_size > 0) {
if (remaining_size > 0 && buf_spos_ > 0) {
for (size_t i = 0; i < remaining_size; i++) {
buf_[i] = buf_[buf_spos_ + i];
}
@@ -3874,9 +3943,7 @@ private:
buf_spos_ = 0;
buf_epos_ = remaining_size;
if (remaining_size + n > buf_.size()) {
buf_.resize(remaining_size + n);
}
if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); }
for (size_t i = 0; i < n; i++) {
buf_[buf_epos_ + i] = data[i];
@@ -3909,6 +3976,7 @@ inline std::string make_multipart_data_boundary() {
// platforms, but due to lack of support in the c++ standard library,
// doing better requires either some ugly hacks or breaking portability.
std::random_device seed_gen;
// Request 128 bits of entropy for initialization
std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
std::mt19937 engine(seed_sequence);
@@ -4191,14 +4259,16 @@ inline std::pair<std::string, std::string> make_digest_authentication_header(
}
}
auto field =
"Digest username=\"" + username + "\", realm=\"" + auth.at("realm") +
"\", nonce=\"" + auth.at("nonce") + "\", uri=\"" + req.path +
"\", algorithm=" + algo +
(qop.empty() ? ", response=\""
: ", qop=" + qop + ", nc=\"" + nc + "\", cnonce=\"" +
cnonce + "\", response=\"") +
response + "\"";
auto opaque = (auth.find("opaque") != auth.end()) ? auth.at("opaque") : "";
auto field = "Digest username=\"" + username + "\", realm=\"" +
auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +
"\", uri=\"" + req.path + "\", algorithm=" + algo +
(qop.empty() ? ", response=\""
: ", qop=" + qop + ", nc=" + nc + ", cnonce=\"" +
cnonce + "\", response=\"") +
response + "\"" +
(opaque.empty() ? "" : ", opaque=\"" + opaque + "\"");
auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
return std::make_pair(key, field);
@@ -4268,6 +4338,41 @@ private:
} // namespace detail
inline std::string hosted_at(const char *hostname) {
std::vector<std::string> addrs;
hosted_at(hostname, addrs);
if (addrs.empty()) { return std::string(); }
return addrs[0];
}
inline void hosted_at(const char *hostname, std::vector<std::string> &addrs) {
struct addrinfo hints;
struct addrinfo *result;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
if (getaddrinfo(hostname, nullptr, &hints, &result)) {
#if defined __linux__ && !defined __ANDROID__
res_init();
#endif
return;
}
for (auto rp = result; rp; rp = rp->ai_next) {
const auto &addr =
*reinterpret_cast<struct sockaddr_storage *>(rp->ai_addr);
std::string ip;
int dummy = -1;
if (detail::get_remote_ip_and_port(addr, sizeof(struct sockaddr_storage),
ip, dummy)) {
addrs.push_back(ip);
}
}
}
inline std::string append_query_params(const char *path, const Params &params) {
std::string path_with_query = path;
const static std::regex re("[^?]+\\?.*");
@@ -5224,16 +5329,31 @@ inline bool Server::listen_internal() {
}
{
#ifdef _WIN32
auto timeout = static_cast<uint32_t>(read_timeout_sec_ * 1000 +
read_timeout_usec_ / 1000);
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
sizeof(timeout));
#else
timeval tv;
tv.tv_sec = static_cast<long>(read_timeout_sec_);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec_);
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
#endif
}
{
#ifdef _WIN32
auto timeout = static_cast<uint32_t>(write_timeout_sec_ * 1000 +
write_timeout_usec_ / 1000);
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
sizeof(timeout));
#else
timeval tv;
tv.tv_sec = static_cast<long>(write_timeout_sec_);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec_);
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv));
#endif
}
#if __cplusplus > 201703L
@@ -5676,6 +5796,7 @@ inline socket_t ClientImpl::create_client_socket(Error &error) const {
read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
write_timeout_usec_, interface_, error);
}
// Check is custom IP specified for host_
std::string ip;
auto it = addr_map_.find(host_);
@@ -5736,7 +5857,11 @@ inline bool ClientImpl::read_response_line(Stream &strm, const Request &req,
if (!line_reader.getline()) { return false; }
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
#else
const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
#endif
std::cmatch m;
if (!std::regex_match(line_reader.ptr(), m, re)) {
@@ -6028,9 +6153,11 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
if (!req.has_header("Accept")) { req.headers.emplace("Accept", "*/*"); }
#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
if (!req.has_header("User-Agent")) {
req.headers.emplace("User-Agent", "cpp-httplib/0.10.0");
req.headers.emplace("User-Agent", "cpp-httplib/0.10.3");
}
#endif
if (req.body.empty()) {
if (req.content_provider_) {
@@ -6123,11 +6250,6 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
ContentProviderWithoutLength content_provider_without_length,
const char *content_type, Error &error) {
// Request req;
// req.method = method;
// req.headers = headers;
// req.path = path;
if (content_type) { req.headers.emplace("Content-Type", content_type); }
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
@@ -7063,9 +7185,9 @@ inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
auto err = SSL_get_error(ssl_, ret);
int n = 1000;
#ifdef _WIN32
while (--n >= 0 &&
(err == SSL_ERROR_WANT_READ ||
err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT)) {
while (--n >= 0 && (err == SSL_ERROR_WANT_READ ||
(err == SSL_ERROR_SYSCALL &&
WSAGetLastError() == WSAETIMEDOUT))) {
#else
while (--n >= 0 && err == SSL_ERROR_WANT_READ) {
#endif
@@ -7093,9 +7215,9 @@ inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
auto err = SSL_get_error(ssl_, ret);
int n = 1000;
#ifdef _WIN32
while (--n >= 0 &&
(err == SSL_ERROR_WANT_WRITE ||
err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT)) {
while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE ||
(err == SSL_ERROR_SYSCALL &&
WSAGetLastError() == WSAETIMEDOUT))) {
#else
while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {
#endif
+12 -3
View File
@@ -19,12 +19,13 @@ project(
# Check just in case downstream decides to edit the source
# and add a project version
version = meson.project_version()
python = import('python').find_installation('python3')
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(
python, '-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))'
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()
endif
@@ -68,7 +69,7 @@ if get_option('cpp-httplib_compile')
'split',
input: 'httplib.h',
output: ['httplib.cc', 'httplib.h'],
command: [python, files('split.py'), '--out', meson.current_build_dir()],
command: [python3, files('split.py'), '--out', meson.current_build_dir()],
install: true,
install_dir: [false, get_option('includedir')]
)
@@ -92,6 +93,14 @@ if get_option('cpp-httplib_compile')
else
install_headers('httplib.h')
cpp_httplib_dep = declare_dependency(compile_args: args, dependencies: deps, include_directories: include_directories('.'))
import('pkgconfig').generate(
name: 'cpp-httplib',
description: 'A C++ HTTP/HTTPS server and client library',
install_dir: join_paths(get_option('datadir'), 'pkgconfig'),
url: 'https://github.com/yhirose/cpp-httplib',
version: version
)
endif
if meson.version().version_compare('>=0.54.0')
+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 (requires Python 3)')
option('cpp-httplib_compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file')
option('cpp-httplib_test', type: 'boolean', value: false, description: 'Build tests')
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
docker-compose down --rmi all
-1
View File
@@ -1 +0,0 @@
docker-compose up -d
+52 -2
View File
@@ -58,6 +58,14 @@ TEST(StartupTest, WSAStartup) {
}
#endif
TEST(DecodeURLTest, PercentCharacter) {
EXPECT_EQ(
detail::decode_url(
R"(descrip=Gastos%20%C3%A1%C3%A9%C3%AD%C3%B3%C3%BA%C3%B1%C3%91%206)",
false),
R"(descrip=Gastos áéíóúñÑ 6)");
}
TEST(EncodeQueryParamTest, ParseUnescapedChararactersTest) {
string unescapedCharacters = "-_.!~*'()";
@@ -393,6 +401,31 @@ TEST(ChunkedEncodingTest, FromHTTPWatch_Online) {
EXPECT_EQ(out, res->body);
}
TEST(HostnameToIPConversionTest, HTTPWatch_Online) {
auto host = "www.httpwatch.com";
auto ip = hosted_at(host);
EXPECT_EQ("191.236.16.12", ip);
std::vector<std::string> addrs;
hosted_at(host, addrs);
EXPECT_EQ(1u, addrs.size());
}
#if 0 // It depends on each test environment...
TEST(HostnameToIPConversionTest, YouTube_Online) {
auto host = "www.youtube.com";
std::vector<std::string> addrs;
hosted_at(host, addrs);
EXPECT_EQ(20u, addrs.size());
auto it = std::find(addrs.begin(), addrs.end(), "2607:f8b0:4006:809::200e");
EXPECT_TRUE(it != addrs.end());
}
#endif
TEST(ChunkedEncodingTest, WithContentReceiver_Online) {
auto host = "www.httpwatch.com";
@@ -577,7 +610,7 @@ TEST(ConnectionErrorTest, InvalidPort) {
EXPECT_EQ(Error::Connection, res.error());
}
TEST(ConnectionErrorTest, Timeout) {
TEST(ConnectionErrorTest, Timeout_Online) {
auto host = "google.com";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -589,9 +622,14 @@ TEST(ConnectionErrorTest, Timeout) {
#endif
cli.set_connection_timeout(std::chrono::seconds(2));
// only probe one address type so that the error reason
// correlates to the timed-out IPv4, not the unsupported
// IPv6 connection attempt
cli.set_address_family(AF_INET);
auto res = cli.Get("/");
ASSERT_TRUE(!res);
EXPECT_TRUE(res.error() == Error::Connection);
EXPECT_EQ(Error::ConnectionTimeout, res.error());
}
TEST(CancelTest, NoCancel_Online) {
@@ -1630,6 +1668,11 @@ protected:
EXPECT_EQ("0", req.get_header_value("Content-Length"));
res.set_content("empty-no-content-type", "text/plain");
})
.Post("/post-large",
[&](const Request &req, Response &res) {
EXPECT_EQ(req.body, LARGE_DATA);
res.set_content(req.body, "text/plain");
})
.Put("/empty-no-content-type",
[&](const Request &req, Response &res) {
EXPECT_EQ(req.body, "");
@@ -2035,6 +2078,13 @@ TEST_F(ServerTest, PostEmptyContentWithNoContentType) {
ASSERT_EQ("empty-no-content-type", res->body);
}
TEST_F(ServerTest, PostLarge) {
auto res = cli_.Post("/post-large", LARGE_DATA, "text/plain");
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
EXPECT_EQ(LARGE_DATA, res->body);
}
TEST_F(ServerTest, PutEmptyContentWithNoContentType) {
auto res = cli_.Put("/empty-no-content-type");
ASSERT_TRUE(res);