Compare commits

..

35 Commits

Author SHA1 Message Date
yhirose
dcdb0d047b Fixed PRI request problem 2019-10-03 13:44:18 -04:00
yhirose
1f86e41d97 Changed back to select as default 2019-10-03 13:44:18 -04:00
yhirose
6d8302313c Fixed warning 2019-10-03 13:44:18 -04:00
yhirose
89440ec322 Merge pull request #232 from sux2mfgj/fix_the_invalidhost_test
Fix a test, ConnectionErrorTest::InvalidPort.
2019-10-03 13:43:48 -04:00
yhirose
5edf455d72 Merge pull request #231 from sux2mfgj/fix_the_sample
Fix a sample code of multipart/form-data POST data in the README.md.
2019-10-03 13:42:31 -04:00
Shunsuke Mie
5f49c13f95 Fix a test, ConnectionErrorTest::InvalidPort. currently, the abcde.com is valid, so I change it. The first byte doesn't permit a hyphen. 2019-10-03 21:49:11 +09:00
Shunsuke Mie
760bccc3ad fix a sample code of multipart/form-data POST data in README.md. 2019-10-03 21:23:10 +09:00
yhirose
a99e02aeb3 Add HTTP/2 Connection Preface check test 2019-10-01 06:28:45 -04:00
yhirose
4aae1dcc42 Merge pull request #224 from Zefz/configuration
Allow configuration to be overriden without source editing
2019-09-30 17:07:21 -04:00
Johan Jansen
f23f9a06a9 Allow configuration to be overriden without source editing 2019-09-30 22:00:17 +02:00
yhirose
46466b1e28 Merge pull request #227 from ha11owed/master
Don't exit if accept fails due to no more file descriptors
2019-09-30 07:47:27 -04:00
Alin Gherman
224119a60a Retry in case of too many sockets opened instead of stopping the server. 2019-09-30 11:48:02 +02:00
yhirose
c02849e269 Removed CPPHTTPLIB_USE_POLL, added CPPHTTPLIB_USE_SELECT 2019-09-29 19:43:22 -04:00
yhirose
71979b1e88 Merge pull request #226 from Zefz/mingw-compile-fix
Fix compilation on Mingw-64
2019-09-27 17:29:33 -04:00
yhirose
a62d1f79f4 Merge pull request #225 from TangHuaiZhe/master
Fix compile error in android ndk
2019-09-27 17:29:07 -04:00
zefz
b14b7b0f8f Fix compilation on Mingw-64 2019-09-27 20:23:16 +02:00
Tang Huaizhe
9dbe0d855c Fix compile error in android ndk 2019-09-27 13:32:23 +08:00
yhirose
2cef8df6ae Merge pull request #223 from Zefz/cpp-style-cast
Fix several -Wold-style-cast warnings in Clang-9
2019-09-26 19:00:45 -04:00
Johan Jansen
94fc229c44 Add missing explicit const_cast 2019-09-26 22:20:33 +02:00
Johan Jansen
a7052cba22 Fix several -Wold-style-cast warnings in Clang-9 2019-09-26 22:03:18 +02:00
yhirose
c47c6b3910 Updated test.vcxproj 2019-09-26 13:20:53 -04:00
yhirose
c946eb7699 Fixed warnings on Windows 2019-09-26 08:13:20 -04:00
yhirose
1f99ad5d6e Updated vcxproj for test 2019-09-25 08:16:15 -04:00
yhirose
96e372bad2 Changed test.yaml to use actions/checkout@v1 2019-09-20 01:07:09 -04:00
yhirose
fc56f39d17 Fixed Github Actions badge problem 2019-09-20 00:43:02 -04:00
yhirose
5844432a7b Add badge for Github Actions 2019-09-20 00:31:26 -04:00
yhirose
b97420f363 Add test.yaml 2019-09-20 00:01:05 -04:00
yhirose
81610ee080 Merge pull request #218 from p0lloloco/master
Add ssl_context member function to SSLClient
2019-09-18 23:08:14 -04:00
PolloLoco
c7f8561472 Added ssl_context member function to SSLClient in
order to allow access to the SSL_CTX struct, for
example to load the windows cert store
2019-09-18 15:10:15 +02:00
yhirose
47bc7456d2 Merge pull request #217 from yhirose/poll
Use 'poll' instead of 'select'
2019-09-18 08:46:20 -04:00
yhirose
4ab9270660 Use 'poll' as default instead of select (Fix #215) 2019-09-18 08:42:18 -04:00
yhirose
d599a36c2a Format code 2019-09-16 17:48:17 -04:00
yhirose
6f8f51496d Merge branch 'gulrak-feature-response-handler-with-content-receiver' 2019-09-15 09:17:26 -04:00
yhirose
0c293887d0 Fixed problem with redirect 2019-09-15 09:15:21 -04:00
Steffen Schuemann
7e92ffec48 Added new Client::Get variant that combines a ContentReceiver with a new ResponseHandler
While trying to implement streaming of internet radio, where a ContentReceiver is needed to handle the audio data, I had the problem, that important information about the stream data is part of the HTTP header (e.g. size of audio chunks between meta data), so I added a ResponseHandler and a new Get variant, to gain access to the header before handling the first chunk of data.

The ResponseHandler can abort the request by returning false, in the same way as the ContentReceiver.

A test case was also added.
2019-09-14 14:55:12 +02:00
5 changed files with 250 additions and 57 deletions

17
.github/workflows/test.yaml vendored Normal file
View File

@@ -0,0 +1,17 @@
name: test
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macOS-latest, ubuntu-latest]
steps:
- name: checkout
uses: actions/checkout@v1
- name: make
run: cd test && make

View File

@@ -1,6 +1,7 @@
cpp-httplib
===========
[![](https://github.com/yhirose/cpp-httplib/workflows/test/badge.svg)](https://github.com/yhirose/cpp-httplib/actions)
[![Build Status](https://travis-ci.org/yhirose/cpp-httplib.svg?branch=master)](https://travis-ci.org/yhirose/cpp-httplib)
[![Bulid Status](https://ci.appveyor.com/api/projects/status/github/yhirose/cpp-httplib?branch=master&svg=true)](https://ci.appveyor.com/project/yhirose/cpp-httplib)
@@ -76,12 +77,13 @@ svr.set_error_handler([](const auto& req, auto& res) {
```cpp
svr.Post("/multipart", [&](const auto& req, auto& res) {
auto size = req.files.size();
auto ret = req.has_file("name1"));
auto ret = req.has_file("name1");
const auto& file = req.get_file_value("name1");
// file.filename;
// file.content_type;
auto body = req.body.substr(file.offset, file.length));
})
auto body = req.body.substr(file.offset, file.length);
});
```
### Stream content with Content provider

208
httplib.h
View File

@@ -8,6 +8,49 @@
#ifndef CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_HTTPLIB_H
/*
* Configuration
*/
#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
#endif
#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0
#endif
#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5
#endif
#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND
#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5
#endif
#ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND
#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0
#endif
#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
#endif
#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
#endif
#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (std::numeric_limits<size_t>::max)()
#endif
#ifndef CPPHTTPLIB_RECV_BUFSIZ
#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u)
#endif
#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
#define CPPHTTPLIB_THREAD_POOL_COUNT 8
#endif
#ifdef _WIN32
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
@@ -45,18 +88,32 @@ typedef int ssize_t;
#include <winsock2.h>
#include <ws2tcpip.h>
#ifndef WSA_FLAG_NO_HANDLE_INHERIT
#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
#endif
#ifdef _MSC_VER
#pragma comment(lib, "ws2_32.lib")
#endif
#ifndef strcasecmp
#define strcasecmp _stricmp
#endif // strcasecmp
typedef SOCKET socket_t;
#else
#ifdef CPPHTTPLIB_USE_POLL
#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
#endif
#else // not _WIN32
#include <arpa/inet.h>
#include <cstring>
#include <netdb.h>
#include <netinet/in.h>
#ifdef CPPHTTPLIB_USE_POLL
#include <poll.h>
#endif
#include <pthread.h>
#include <signal.h>
#include <sys/select.h>
@@ -70,6 +127,7 @@ typedef int socket_t;
#include <assert.h>
#include <atomic>
#include <condition_variable>
#include <errno.h>
#include <fcntl.h>
#include <fstream>
#include <functional>
@@ -104,20 +162,6 @@ inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) {
#include <zlib.h>
#endif
/*
* Configuration
*/
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0
#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5
#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5
#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0
#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (std::numeric_limits<size_t>::max)()
#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u)
#define CPPHTTPLIB_THREAD_POOL_COUNT 8
namespace httplib {
namespace detail {
@@ -153,6 +197,9 @@ typedef std::function<bool(const char *data, size_t data_length, size_t offset,
typedef std::function<bool(uint64_t current, uint64_t total)> Progress;
struct Response;
typedef std::function<bool(const Response &response)> ResponseHandler;
struct MultipartFile {
std::string filename;
std::string content_type;
@@ -188,6 +235,7 @@ struct Request {
// for client
size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT;
ResponseHandler response_handler;
ContentReceiver content_receiver;
Progress progress;
@@ -354,7 +402,7 @@ private:
pool_.jobs_.pop_front();
}
assert(true == (bool)fn);
assert(true == static_cast<bool>(fn));
fn();
}
}
@@ -518,6 +566,15 @@ public:
ContentReceiver content_receiver,
Progress progress);
std::shared_ptr<Response> Get(const char *path, const Headers &headers,
ResponseHandler response_handler,
ContentReceiver content_receiver);
std::shared_ptr<Response> Get(const char *path, const Headers &headers,
ResponseHandler response_handler,
ContentReceiver content_receiver,
Progress progress);
std::shared_ptr<Response> Head(const char *path);
std::shared_ptr<Response> Head(const char *path, const Headers &headers);
@@ -684,6 +741,8 @@ public:
long get_openssl_verify_result() const;
SSL_CTX* ssl_context() const noexcept;
private:
virtual bool process_and_close_socket(
socket_t sock, size_t request_count,
@@ -973,6 +1032,15 @@ inline int close_socket(socket_t sock) {
}
inline int select_read(socket_t sock, time_t sec, time_t usec) {
#ifdef CPPHTTPLIB_USE_POLL
struct pollfd pfd_read;
pfd_read.fd = sock;
pfd_read.events = POLLIN;
auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
return poll(&pfd_read, 1, timeout);
#else
fd_set fds;
FD_ZERO(&fds);
FD_SET(sock, &fds);
@@ -982,9 +1050,26 @@ inline int select_read(socket_t sock, time_t sec, time_t usec) {
tv.tv_usec = static_cast<long>(usec);
return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv);
#endif
}
inline bool 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;
pfd_read.events = POLLIN | POLLOUT;
auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
if (poll(&pfd_read, 1, timeout) > 0 &&
pfd_read.revents & (POLLIN | POLLOUT)) {
int error = 0;
socklen_t len = sizeof(error);
return getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&error), &len) >= 0 &&
!error;
}
return false;
#else
fd_set fdsr;
FD_ZERO(&fdsr);
FD_SET(sock, &fdsr);
@@ -996,20 +1081,15 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
tv.tv_sec = static_cast<long>(sec);
tv.tv_usec = static_cast<long>(usec);
if (select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv) < 0) {
return false;
} else if (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw)) {
if (select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv) > 0 &&
(FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
int error = 0;
socklen_t len = sizeof(error);
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&error, &len) < 0 ||
error) {
return false;
}
} else {
return false;
return getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&error, &len) >= 0 &&
!error;
}
return true;
return false;
#endif
}
template <typename T>
@@ -1096,9 +1176,9 @@ socket_t create_socket(const char *host, int port, Fn fn,
// Make 'reuse address' option available
int yes = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes));
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&yes), sizeof(yes));
#ifdef SO_REUSEPORT
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char *)&yes, sizeof(yes));
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<char*>(&yes), sizeof(yes));
#endif
// bind or connect
@@ -1137,10 +1217,10 @@ inline std::string get_remote_addr(socket_t sock) {
struct sockaddr_storage addr;
socklen_t len = sizeof(addr);
if (!getpeername(sock, (struct sockaddr *)&addr, &len)) {
if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr), &len)) {
char ipstr[NI_MAXHOST];
if (!getnameinfo((struct sockaddr *)&addr, len, ipstr, sizeof(ipstr),
if (!getnameinfo(reinterpret_cast<struct sockaddr *>(&addr), len, ipstr, sizeof(ipstr),
nullptr, 0, NI_NUMERICHOST)) {
return ipstr;
}
@@ -1222,7 +1302,7 @@ inline bool compress(std::string &content) {
if (ret != Z_OK) { return false; }
strm.avail_in = content.size();
strm.next_in = (Bytef *)content.data();
strm.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(content.data()));
std::string compressed;
@@ -1230,7 +1310,7 @@ inline bool compress(std::string &content) {
char buff[bufsiz];
do {
strm.avail_out = bufsiz;
strm.next_out = (Bytef *)buff;
strm.next_out = reinterpret_cast<Bytef*>(buff);
ret = deflate(&strm, Z_FINISH);
assert(ret != Z_STREAM_ERROR);
compressed.append(buff, bufsiz - strm.avail_out);
@@ -1267,13 +1347,13 @@ public:
int ret = Z_OK;
strm.avail_in = data_length;
strm.next_in = (Bytef *)data;
strm.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef *>(data));
const auto bufsiz = 16384;
char buff[bufsiz];
do {
strm.avail_out = bufsiz;
strm.next_out = (Bytef *)buff;
strm.next_out = reinterpret_cast<Bytef*>(buff);
ret = inflate(&strm, Z_NO_FLUSH);
assert(ret != Z_STREAM_ERROR);
@@ -1555,6 +1635,7 @@ inline bool redirect(T &cli, const Request &req, Response &res,
new_req.headers = req.headers;
new_req.body = req.body;
new_req.redirect_count = req.redirect_count - 1;
new_req.response_handler = req.response_handler;
new_req.content_receiver = req.content_receiver;
new_req.progress = req.progress;
@@ -2241,7 +2322,7 @@ inline void Server::stop() {
}
inline bool Server::parse_request_line(const char *s, Request &req) {
static std::regex re("(GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS) "
static std::regex re("(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) "
"(([^?]+)(?:\\?(.+?))?) (HTTP/1\\.[01])\r\n");
std::cmatch m;
@@ -2499,6 +2580,12 @@ inline bool Server::listen_internal() {
socket_t sock = accept(svr_sock_, nullptr, nullptr);
if (sock == INVALID_SOCKET) {
if (errno == EMFILE) {
// The per-process limit of open file descriptors has been reached.
// Try to accept new connections after a short sleep.
std::this_thread::sleep_for(std::chrono::milliseconds(1));
continue;
}
if (svr_sock_ != INVALID_SOCKET) {
detail::close_socket(svr_sock_);
ret = false;
@@ -2527,13 +2614,15 @@ inline bool Server::routing(Request &req, Response &res) {
return dispatch_request(req, res, post_handlers_);
} else if (req.method == "PUT") {
return dispatch_request(req, res, put_handlers_);
} else if (req.method == "PATCH") {
return dispatch_request(req, res, patch_handlers_);
} else if (req.method == "DELETE") {
return dispatch_request(req, res, delete_handlers_);
} else if (req.method == "OPTIONS") {
return dispatch_request(req, res, options_handlers_);
} else if (req.method == "PATCH") {
return dispatch_request(req, res, patch_handlers_);
}
res.status = 400;
return false;
}
@@ -2595,7 +2684,7 @@ Server::process_request(Stream &strm, bool last_connection,
req.set_header("REMOTE_ADDR", strm.get_remote_addr());
// Body
if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH") {
if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || req.method == "PRI") {
if (!detail::read_content(strm, req, payload_max_length_, res.status,
Progress(), [&](const char *buf, size_t n) {
if (req.body.size() + n > req.body.max_size()) {
@@ -2633,7 +2722,7 @@ Server::process_request(Stream &strm, bool last_connection,
if (routing(req, res)) {
if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; }
} else {
res.status = 404;
if (res.status == -1) { res.status = 404; }
}
return write_response(strm, last_connection, req, res);
@@ -2727,7 +2816,7 @@ inline bool Client::send(const std::vector<Request> &requests,
if (!process_and_close_socket(
sock, requests.size() - i,
[&](Stream &strm, bool last_connection, bool &connection_close) {
[&](Stream &strm, bool last_connection, bool &connection_close) -> bool {
auto &req = requests[i];
auto res = Response();
i++;
@@ -2869,6 +2958,10 @@ inline bool Client::process_request(Stream &strm, const Request &req,
connection_close = true;
}
if (req.response_handler) {
if (!req.response_handler(res)) { return false; }
}
// Body
if (req.method != "HEAD") {
detail::ContentReceiverCore out = [&](const char *buf, size_t n) {
@@ -2940,30 +3033,47 @@ Client::Get(const char *path, const Headers &headers, Progress progress) {
inline std::shared_ptr<Response> Client::Get(const char *path,
ContentReceiver content_receiver) {
Progress dummy;
return Get(path, Headers(), content_receiver, dummy);
return Get(path, Headers(), nullptr, content_receiver, dummy);
}
inline std::shared_ptr<Response> Client::Get(const char *path,
ContentReceiver content_receiver,
Progress progress) {
return Get(path, Headers(), content_receiver, progress);
return Get(path, Headers(), nullptr, content_receiver, progress);
}
inline std::shared_ptr<Response> Client::Get(const char *path,
const Headers &headers,
ContentReceiver content_receiver) {
Progress dummy;
return Get(path, headers, content_receiver, dummy);
return Get(path, headers, nullptr, content_receiver, dummy);
}
inline std::shared_ptr<Response> Client::Get(const char *path,
const Headers &headers,
ContentReceiver content_receiver,
Progress progress) {
return Get(path, headers, nullptr, content_receiver, progress);
}
inline std::shared_ptr<Response> Client::Get(const char *path,
const Headers &headers,
ResponseHandler response_handler,
ContentReceiver content_receiver) {
Progress dummy;
return Get(path, headers, response_handler, content_receiver, dummy);
}
inline std::shared_ptr<Response> Client::Get(const char *path,
const Headers &headers,
ResponseHandler response_handler,
ContentReceiver content_receiver,
Progress progress) {
Request req;
req.method = "GET";
req.path = path;
req.headers = headers;
req.response_handler = response_handler;
req.content_receiver = content_receiver;
req.progress = progress;
@@ -3192,7 +3302,7 @@ inline bool process_and_close_socket_ssl(bool is_client_request, socket_t sock,
return false;
}
auto bio = BIO_new_socket(sock, BIO_NOCLOSE);
auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
SSL_set_bio(ssl, bio, bio);
if (!setup(ssl)) {
@@ -3298,13 +3408,13 @@ inline int SSLSocketStream::read(char *ptr, size_t size) {
if (SSL_pending(ssl_) > 0 ||
detail::select_read(sock_, CPPHTTPLIB_READ_TIMEOUT_SECOND,
CPPHTTPLIB_READ_TIMEOUT_USECOND) > 0) {
return SSL_read(ssl_, ptr, size);
return SSL_read(ssl_, ptr, static_cast<int>(size));
}
return -1;
}
inline int SSLSocketStream::write(const char *ptr, size_t size) {
return SSL_write(ssl_, ptr, size);
return SSL_write(ssl_, ptr, static_cast<int>(size));
}
inline int SSLSocketStream::write(const char *ptr) {
@@ -3417,6 +3527,10 @@ inline long SSLClient::get_openssl_verify_result() const {
return verify_result_;
}
inline SSL_CTX* SSLClient::ssl_context() const noexcept {
return ctx_;
}
inline bool SSLClient::process_and_close_socket(
socket_t sock, size_t request_count,
std::function<bool(Stream &strm, bool last_connection,

View File

@@ -242,6 +242,38 @@ TEST(ChunkedEncodingTest, WithContentReceiver) {
EXPECT_EQ(out, body);
}
TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver) {
auto host = "www.httpwatch.com";
auto sec = 2;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port, sec);
#else
auto port = 80;
httplib::Client cli(host, port, sec);
#endif
std::string body;
auto res =
cli.Get("/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137", Headers(),
[&](const Response& response) {
EXPECT_EQ(200, response.status);
return true;
},
[&](const char *data, size_t data_length, uint64_t, uint64_t) {
body.append(data, data_length);
return true;
});
ASSERT_TRUE(res != nullptr);
std::string out;
httplib::detail::read_file("./image.jpg", out);
EXPECT_EQ(200, res->status);
EXPECT_EQ(out, body);
}
TEST(RangeTest, FromHTTPBin) {
auto host = "httpbin.org";
auto sec = 5;
@@ -303,7 +335,7 @@ TEST(RangeTest, FromHTTPBin) {
}
TEST(ConnectionErrorTest, InvalidHost) {
auto host = "abcde.com";
auto host = "-abcde.com";
auto sec = 2;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -504,7 +536,7 @@ TEST(YahooRedirectTest, Redirect) {
EXPECT_EQ(200, res->status);
}
TEST(Https2HttpRedirectTest, Redirect) {
TEST(HttpsToHttpRedirectTest, Redirect) {
httplib::SSLClient cli("httpbin.org");
cli.follow_location(true);
auto res = cli.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
@@ -605,7 +637,7 @@ protected:
size_t DATA_CHUNK_SIZE = 4;
const auto &d = *data;
auto out_len = std::min(static_cast<size_t>(length), DATA_CHUNK_SIZE);
sink(&d[offset], out_len);
sink(&d[static_cast<size_t>(offset)], out_len);
},
[data] { delete data; });
})
@@ -1361,6 +1393,18 @@ TEST_F(ServerTest, NoMultipleHeaders) {
EXPECT_EQ(200, res->status);
}
TEST_F(ServerTest, HTTP2Magic) {
Request req;
req.method = "PRI";
req.path = "*";
req.body = "SM";
auto res = std::make_shared<Response>();
auto ret = cli_.send(req, *res);
ASSERT_TRUE(ret);
EXPECT_EQ(400, res->status);
}
TEST_F(ServerTest, KeepAlive) {
cli_.set_keep_alive_max_count(4);

View File

@@ -34,7 +34,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
@@ -47,7 +47,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
@@ -69,15 +69,23 @@
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>C:\Program Files\OpenSSL-Win64\lib\VC;C:\Program Files\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
<LibraryPath>$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>C:\Program Files\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
<LibraryPath>C:\Program Files\OpenSSL-Win64\lib;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(IncludePath)</IncludePath>
<LibraryPath>$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>C:\Program Files\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
<LibraryPath>C:\Program Files\OpenSSL-Win64\lib;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@@ -87,6 +95,8 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
<AdditionalUsingDirectories>
</AdditionalUsingDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@@ -102,11 +112,13 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
<AdditionalUsingDirectories>
</AdditionalUsingDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Ws2_32.lib;libssl.lib;libcrypto.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -119,6 +131,8 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
<AdditionalUsingDirectories>
</AdditionalUsingDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@@ -138,13 +152,15 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
<AdditionalUsingDirectories>
</AdditionalUsingDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Ws2_32.lib;libssl.lib;libcrypto.lib;libssl.lib;libcrypto.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>