Compare commits

...

25 Commits

Author SHA1 Message Date
yhirose d45250fd88 Appled HANDLE_EINTR to send and select system calls 2020-05-01 21:38:23 -04:00
yhirose 528cacdc0d Changed CPPHTTPLIB_THREAD_POOL_COUNT back to 8. (#454) 2020-05-01 21:23:02 -04:00
Matthew DeVore ed1b6afa10 Fix crash caused by header field regex complexity (#457) 2020-05-01 12:44:13 -04:00
yhirose 08fc7085e5 Fixed #456 2020-04-30 19:40:23 -04:00
yhirose 8333340e2c Chagned to use inline function instead of macro 2020-04-27 12:36:39 -04:00
yhirose 98a0887571 Merge branch 'je-ik-sketch-eintr-handling' 2020-04-27 12:33:21 -04:00
Jan Lukavsky b0a189e50e Sketch handling EINTR errors 2020-04-27 17:36:44 +02:00
yhirose 776b3ffbf9 Code format 2020-04-25 18:01:48 -04:00
yhirose a061b97677 Adjust appveyor.yml 2020-04-25 18:01:12 -04:00
yhirose d359e3a5f7 Renave queue_adjust to on_idle (#442) 2020-04-25 17:56:55 -04:00
evg82 5928e0af1a TaskQueue method to internal size adjust (#442)
I use a custom TaskQueue, with variable number of workers, adding workers on demand is an easy task when new connection arrive (in enqueue function) however i need another funtion to be called even (or better) went no new connections arrives to reduce workers count. I only added a new virtual method in TaskQueue class to allow custom class to adjust workers size over time. Even if this methods is called frequenlty custom class can keep a "last_update" counter to check if need to adjust worker count or any other internal task. Without this function i need an external thread to make this adjust task.
2020-04-25 17:55:20 -04:00
yhirose a5005789ff Fixed Visual Studio compiler warnings with x64 platform (Resolve #440 and #446) (#448) 2020-04-25 17:13:14 -04:00
yhirose fae30af47d Updated appveyor.yml 2020-04-25 15:48:19 -04:00
Hoa Thiên Vũ 2feea0c9ab Fixed error: ‘ULONG_MAX’ was not declared in this scope on line 1921 (#445)
* Fixed error:
ULONG_MAX is defined in the limits.h header file. Putting #include <climits>
```
httplib.h: In function ‘bool httplib::detail::read_content_chunked(httplib::Stream&, httplib::ContentReceiver)’:
httplib.h:1921:22: error: ‘ULONG_MAX’ was not declared in this scope
     if (chunk_len == ULONG_MAX) { return false; }
                      ^~~~~~~~~
httplib.h:1921:22: note: suggested alternative: ‘_SC_ULONG_MAX’
     if (chunk_len == ULONG_MAX) { return false; }
                      ^~~~~~~~~
                      _SC_ULONG_MAX
```

* Move #include <climits> to after #include <cassert>
2020-04-24 12:02:19 -04:00
yhirose a2e4af54b7 Fix #399 2020-04-23 23:09:04 -04:00
yhirose d0b123be26 Support remote_addr and remote_port REMOTE_PORT header in client Request (#433) 2020-04-23 22:12:12 -04:00
Matthew DeVore df138366e4 Fail to read a chunk if its length is >= ULONG_MAX (#444)
We cannot trivially support such large chunks, and the maximum value
std::strtoul can parse accurately is ULONG_MAX-1. Error out early if the
length is longer than that.
2020-04-23 10:59:15 -04:00
Matthew DeVore c49441ae64 Do not throw exceptions when parsing request chunks (#441)
detail::read_content_chunked was using std::stoul to parse the
hexadecimal chunk lengths for "Transfer-Encoding: chunked" requests.
This throws an exception if the string does not begin with any valid
digits. read_content_chunked is not called in the context of a try block
so this caused the process to terminate.

Rather than use exceptions, I opted for std::stroul, which is similar to
std::stoul but does not throw exceptions. Since malformed user input is
not particularly exceptional, and some projects are compiled without
exception support, this approach seems both more portable and more
correct.
2020-04-23 09:05:45 -04:00
yhirose e1506fa186 Code cleanup 2020-04-22 21:43:16 -04:00
yhirose ad9fd3bd93 Fix #436 2020-04-22 21:42:58 -04:00
yhirose 05e0253195 Fixed test error 2020-04-21 23:07:51 -04:00
yhirose da26b517a3 Added url::Get interface 2020-04-21 23:00:39 -04:00
yhirose 2b7a968468 Added a unit test for URL interface 2020-04-21 21:21:31 -04:00
yhirose 240cc85ccb Fixed regex problem for recirect location 2020-04-21 21:18:29 -04:00
yhirose 129e2f00b8 Removed unnecessary noexcept 2020-04-20 19:42:05 -04:00
8 changed files with 497 additions and 124 deletions
+1
View File
@@ -3,6 +3,7 @@ tags
example/server
example/client
example/hello
example/simplecli
example/simplesvr
example/benchmark
example/redirect
+12 -7
View File
@@ -1,9 +1,14 @@
version: 1.0.{build}
image: Visual Studio 2017
build_script:
- cmd: >-
cd test
image:
- Visual Studio 2019
platform:
- x64
build_script:
- cmd: >-
cd test
msbuild.exe test.sln /verbosity:minimal /t:Build /p:Configuration=Release;Platform=%PLATFORM%
msbuild.exe test.sln /verbosity:minimal /t:Build /p:Configuration=Debug;Platform=Win32
test_script:
- cmd: Debug\test.exe
- cmd: x64\Release\test.exe
+5 -2
View File
@@ -5,7 +5,7 @@ OPENSSL_DIR = /usr/local/opt/openssl
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
all: server client hello simplesvr upload redirect sse benchmark
all: server client hello simplecli simplesvr upload redirect sse benchmark
server : server.cc ../httplib.h Makefile
$(CXX) -o server $(CXXFLAGS) server.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
@@ -16,6 +16,9 @@ client : client.cc ../httplib.h Makefile
hello : hello.cc ../httplib.h Makefile
$(CXX) -o hello $(CXXFLAGS) hello.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
simplecli : simplecli.cc ../httplib.h Makefile
$(CXX) -o simplecli $(CXXFLAGS) simplecli.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
simplesvr : simplesvr.cc ../httplib.h Makefile
$(CXX) -o simplesvr $(CXXFLAGS) simplesvr.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
@@ -36,4 +39,4 @@ pem:
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
clean:
rm server client hello simplesvr upload redirect sse benchmark *.pem
rm server client hello simplecli simplesvr upload redirect sse benchmark *.pem
+1 -1
View File
@@ -15,5 +15,5 @@ int main(void) {
res.set_content("Hello World!", "text/plain");
});
svr.listen("localhost", 1234);
svr.listen("localhost", 8080);
}
+33
View File
@@ -0,0 +1,33 @@
//
// simplecli.cc
//
// Copyright (c) 2019 Yuji Hirose. All rights reserved.
// MIT License
//
#include <httplib.h>
#include <iostream>
#define CA_CERT_FILE "./ca-bundle.crt"
using namespace std;
int main(void) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
httplib::url::Options options;
options.ca_cert_file_path = CA_CERT_FILE;
// options.server_certificate_verification = true;
auto res = httplib::url::Get("https://localhost:8080/hi", options);
#else
auto res = httplib::url::Get("http://localhost:8080/hi");
#endif
if (res) {
cout << res->status << endl;
cout << res->get_header_value("Content-Type") << endl;
cout << res->body << endl;
}
return 0;
}
+243 -84
View File
@@ -50,7 +50,7 @@
#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
#define CPPHTTPLIB_THREAD_POOL_COUNT \
((std::max)(1u, std::thread::hardware_concurrency() - 1))
((std::max)(8u, std::thread::hardware_concurrency() - 1))
#endif
/*
@@ -134,6 +134,7 @@ using socket_t = int;
#include <array>
#include <atomic>
#include <cassert>
#include <climits>
#include <condition_variable>
#include <errno.h>
#include <fcntl.h>
@@ -174,7 +175,6 @@ inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) {
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
#include <zlib.h>
#endif
/*
* Declaration
*/
@@ -262,6 +262,9 @@ struct Request {
Headers headers;
std::string body;
std::string remote_addr;
int remote_port = -1;
// for server
std::string version;
std::string target;
@@ -352,7 +355,7 @@ public:
virtual ssize_t read(char *ptr, size_t size) = 0;
virtual ssize_t write(const char *ptr, size_t size) = 0;
virtual std::string get_remote_addr() const = 0;
virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;
template <typename... Args>
ssize_t write_format(const char *fmt, const Args &... args);
@@ -364,8 +367,11 @@ class TaskQueue {
public:
TaskQueue() = default;
virtual ~TaskQueue() = default;
virtual void enqueue(std::function<void()> fn) = 0;
virtual void shutdown() = 0;
virtual void on_idle(){};
};
class ThreadPool : public TaskQueue {
@@ -604,6 +610,8 @@ public:
std::shared_ptr<Response> Head(const char *path, const Headers &headers);
std::shared_ptr<Response> Post(const char *path);
std::shared_ptr<Response> Post(const char *path, const std::string &body,
const char *content_type);
@@ -631,6 +639,8 @@ public:
std::shared_ptr<Response> Post(const char *path, const Headers &headers,
const MultipartFormDataItems &items);
std::shared_ptr<Response> Put(const char *path);
std::shared_ptr<Response> Put(const char *path, const std::string &body,
const char *content_type);
@@ -688,6 +698,8 @@ public:
bool send(const std::vector<Request> &requests,
std::vector<Response> &responses);
void stop();
void set_timeout_sec(time_t timeout_sec);
void set_read_timeout(time_t sec, time_t usec);
@@ -720,6 +732,8 @@ protected:
bool process_request(Stream &strm, const Request &req, Response &res,
bool last_connection, bool &connection_close);
std::atomic<socket_t> sock_;
const std::string host_;
const int port_;
const std::string host_and_port_;
@@ -831,7 +845,7 @@ inline void Post(std::vector<Request> &requests, const char *path,
req.method = "POST";
req.path = path;
req.headers = headers;
req.headers.emplace("Content-Type", content_type);
if (content_type) { req.headers.emplace("Content-Type", content_type); }
req.body = body;
requests.emplace_back(std::move(req));
}
@@ -884,7 +898,7 @@ public:
long get_openssl_verify_result() const;
SSL_CTX *ssl_context() const noexcept;
SSL_CTX *ssl_context() const;
private:
bool process_and_close_socket(
@@ -1184,7 +1198,22 @@ inline int close_socket(socket_t sock) {
#endif
}
inline int select_read(socket_t sock, time_t sec, time_t usec) {
template <typename T>
inline ssize_t handle_EINTR(T fn) {
ssize_t res = false;
while (true) {
res = fn();
if (res < 0 && errno == EINTR) {
continue;
}
break;
}
return res;
}
#define HANDLE_EINTR(method, ...) (handle_EINTR([&]() { return method(__VA_ARGS__); }))
inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
#ifdef CPPHTTPLIB_USE_POLL
struct pollfd pfd_read;
pfd_read.fd = sock;
@@ -1192,7 +1221,7 @@ inline int select_read(socket_t sock, time_t sec, time_t usec) {
auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
return poll(&pfd_read, 1, timeout);
return HANDLE_EINTR(poll, &pfd_read, 1, timeout);
#else
fd_set fds;
FD_ZERO(&fds);
@@ -1202,11 +1231,11 @@ inline int select_read(socket_t sock, time_t sec, time_t usec) {
tv.tv_sec = static_cast<long>(sec);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv);
return HANDLE_EINTR(select, static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv);
#endif
}
inline int select_write(socket_t sock, time_t sec, time_t usec) {
inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
#ifdef CPPHTTPLIB_USE_POLL
struct pollfd pfd_read;
pfd_read.fd = sock;
@@ -1214,7 +1243,7 @@ inline int select_write(socket_t sock, time_t sec, time_t usec) {
auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
return poll(&pfd_read, 1, timeout);
return HANDLE_EINTR(poll, &pfd_read, 1, timeout);
#else
fd_set fds;
FD_ZERO(&fds);
@@ -1224,7 +1253,7 @@ inline int select_write(socket_t sock, time_t sec, time_t usec) {
tv.tv_sec = static_cast<long>(sec);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv);
return HANDLE_EINTR(select, static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv);
#endif
}
@@ -1236,13 +1265,13 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
if (poll(&pfd_read, 1, timeout) > 0 &&
pfd_read.revents & (POLLIN | POLLOUT)) {
auto poll_res = HANDLE_EINTR(poll, &pfd_read, 1, timeout);
if (poll_res > 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;
auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char *>(&error), &len);
return res >= 0 && !error;
}
return false;
#else
@@ -1257,7 +1286,7 @@ 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<decltype(tv.tv_usec)>(usec);
if (select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv) > 0 &&
if (HANDLE_EINTR(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);
@@ -1279,7 +1308,7 @@ public:
bool is_writable() const override;
ssize_t read(char *ptr, size_t size) override;
ssize_t write(const char *ptr, size_t size) override;
std::string get_remote_addr() const override;
void get_remote_ip_and_port(std::string &ip, int &port) const override;
private:
socket_t sock_;
@@ -1298,7 +1327,7 @@ public:
bool is_writable() const override;
ssize_t read(char *ptr, size_t size) override;
ssize_t write(const char *ptr, size_t size) override;
std::string get_remote_addr() const override;
void get_remote_ip_and_port(std::string &ip, int &port) const override;
private:
socket_t sock_;
@@ -1317,7 +1346,7 @@ public:
bool is_writable() const override;
ssize_t read(char *ptr, size_t size) override;
ssize_t write(const char *ptr, size_t size) override;
std::string get_remote_addr() const override;
void get_remote_ip_and_port(std::string &ip, int &port) const override;
const std::string &get_buffer() const;
@@ -1501,8 +1530,8 @@ inline bool bind_ip_address(socket_t sock, const char *host) {
return ret;
}
inline std::string if2ip(const std::string &ifn) {
#ifndef _WIN32
inline std::string if2ip(const std::string &ifn) {
struct ifaddrs *ifap;
getifaddrs(&ifap);
for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
@@ -1518,9 +1547,9 @@ inline std::string if2ip(const std::string &ifn) {
}
}
freeifaddrs(ifap);
#endif
return std::string();
}
#endif
inline socket_t create_client_socket(const char *host, int port,
time_t timeout_sec,
@@ -1528,9 +1557,11 @@ inline socket_t create_client_socket(const char *host, int port,
return create_socket(
host, port, [&](socket_t sock, struct addrinfo &ai) -> bool {
if (!intf.empty()) {
#ifndef _WIN32
auto ip = if2ip(intf);
if (ip.empty()) { ip = intf; }
if (!bind_ip_address(sock, ip.c_str())) { return false; }
#endif
}
set_nonblocking(sock, true);
@@ -1550,21 +1581,32 @@ inline socket_t create_client_socket(const char *host, int port,
});
}
inline std::string get_remote_addr(socket_t sock) {
struct sockaddr_storage addr;
socklen_t len = sizeof(addr);
if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr), &len)) {
std::array<char, NI_MAXHOST> ipstr{};
if (!getnameinfo(reinterpret_cast<struct sockaddr *>(&addr), len,
ipstr.data(), static_cast<socklen_t>(ipstr.size()),
nullptr, 0, NI_NUMERICHOST)) {
return ipstr.data();
}
inline void 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) {
port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);
} else if (addr.ss_family == AF_INET6) {
port =
ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);
}
return std::string();
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();
}
}
inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
struct sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr),
&addr_len)) {
get_remote_ip_and_port(addr, addr_len, ip, port);
}
}
inline const char *
@@ -1820,7 +1862,7 @@ inline bool read_headers(Stream &strm, Headers &headers) {
// the left or right side of the header value:
// - https://stackoverflow.com/questions/50179659/
// - https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html
static const std::regex re(R"(([^:]+):[\t ]*(.+))");
static const std::regex re(R"(([^:]+):[\t ]*([^\t ].*))");
std::cmatch m;
if (std::regex_match(line_reader.ptr(), end, m, re)) {
@@ -1889,9 +1931,17 @@ inline bool read_content_chunked(Stream &strm, ContentReceiver out) {
if (!line_reader.getline()) { return false; }
auto chunk_len = std::stoul(line_reader.ptr(), 0, 16);
unsigned long chunk_len;
while (true) {
char *end_ptr;
chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);
if (end_ptr == line_reader.ptr()) { return false; }
if (chunk_len == ULONG_MAX) { return false; }
if (chunk_len == 0) { break; }
while (chunk_len > 0) {
if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
return false;
}
@@ -1901,8 +1951,6 @@ inline bool read_content_chunked(Stream &strm, ContentReceiver out) {
if (strcmp(line_reader.ptr(), "\r\n")) { break; }
if (!line_reader.getline()) { return false; }
chunk_len = std::stoul(line_reader.ptr(), 0, 16);
}
if (chunk_len == 0) {
@@ -2803,11 +2851,11 @@ inline void Response::set_header(const char *key, const std::string &val) {
}
}
inline void Response::set_redirect(const char *url, int status) {
inline void Response::set_redirect(const char *url, int stat) {
if (!detail::has_crlf(url)) {
set_header("Location", url);
if (300 <= status && status < 400) {
this->status = status;
if (300 <= stat && stat < 400) {
this->status = stat;
} else {
this->status = 302;
}
@@ -2900,25 +2948,42 @@ inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec,
inline SocketStream::~SocketStream() {}
inline bool SocketStream::is_readable() const {
return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
}
inline bool SocketStream::is_writable() const {
return detail::select_write(sock_, 0, 0) > 0;
return select_write(sock_, 0, 0) > 0;
}
inline ssize_t SocketStream::read(char *ptr, size_t size) {
if (is_readable()) { return recv(sock_, ptr, size, 0); }
return -1;
if (!is_readable()) { return -1; }
#ifdef _WIN32
if (size > static_cast<size_t>(std::numeric_limits<int>::max())) {
return -1;
}
return recv(sock_, ptr, static_cast<int>(size), 0);
#else
return HANDLE_EINTR(recv, sock_, ptr, size, 0);
#endif
}
inline ssize_t SocketStream::write(const char *ptr, size_t size) {
if (is_writable()) { return send(sock_, ptr, size, 0); }
return -1;
if (!is_writable()) { return -1; }
#ifdef _WIN32
if (size > static_cast<size_t>(std::numeric_limits<int>::max())) {
return -1;
}
return send(sock_, ptr, static_cast<int>(size), 0);
#else
return HANDLE_EINTR(send, sock_, ptr, size, 0);
#endif
}
inline std::string SocketStream::get_remote_addr() const {
return detail::get_remote_addr(sock_);
inline void SocketStream::get_remote_ip_and_port(std::string &ip,
int &port) const {
return detail::get_remote_ip_and_port(sock_, ip, port);
}
// Buffer stream implementation
@@ -2941,7 +3006,8 @@ inline ssize_t BufferStream::write(const char *ptr, size_t size) {
return static_cast<ssize_t>(size);
}
inline std::string BufferStream::get_remote_addr() const { return ""; }
inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/,
int & /*port*/) const {}
inline const std::string &BufferStream::get_buffer() const { return buffer; }
@@ -3421,17 +3487,16 @@ inline int Server::bind_internal(const char *host, int port, int socket_flags) {
if (svr_sock_ == INVALID_SOCKET) { return -1; }
if (port == 0) {
struct sockaddr_storage address;
socklen_t len = sizeof(address);
if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&address),
&len) == -1) {
struct sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),
&addr_len) == -1) {
return -1;
}
if (address.ss_family == AF_INET) {
return ntohs(reinterpret_cast<struct sockaddr_in *>(&address)->sin_port);
} else if (address.ss_family == AF_INET6) {
return ntohs(
reinterpret_cast<struct sockaddr_in6 *>(&address)->sin6_port);
if (addr.ss_family == AF_INET) {
return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);
} else if (addr.ss_family == AF_INET6) {
return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);
} else {
return -1;
}
@@ -3456,6 +3521,7 @@ inline bool Server::listen_internal() {
auto val = detail::select_read(svr_sock_, 0, 100000);
if (val == 0) { // Timeout
task_queue->on_idle();
continue;
}
@@ -3636,7 +3702,9 @@ Server::process_request(Stream &strm, bool last_connection,
connection_close = true;
}
req.set_header("REMOTE_ADDR", strm.get_remote_addr());
strm.get_remote_ip_and_port(req.remote_addr, req.remote_port);
req.set_header("REMOTE_ADDR", req.remote_addr);
req.set_header("REMOTE_PORT", std::to_string(req.remote_port));
if (req.has_header("Range")) {
const auto &range_header_value = req.get_header_value("Range");
@@ -3687,7 +3755,7 @@ inline bool Server::process_and_close_socket(socket_t sock) {
inline Client::Client(const std::string &host, int port,
const std::string &client_cert_path,
const std::string &client_key_path)
: host_(host), port_(port),
: sock_(INVALID_SOCKET), host_(host), port_(port),
host_and_port_(host_ + ":" + std::to_string(port_)),
client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
@@ -3723,18 +3791,19 @@ inline bool Client::read_response_line(Stream &strm, Response &res) {
}
inline bool Client::send(const Request &req, Response &res) {
auto sock = create_client_socket();
if (sock == INVALID_SOCKET) { return false; }
sock_ = create_client_socket();
if (sock_ == INVALID_SOCKET) { return false; }
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
if (is_ssl() && !proxy_host_.empty()) {
bool error;
if (!connect(sock, res, error)) { return error; }
if (!connect(sock_, res, error)) { return error; }
}
#endif
return process_and_close_socket(
sock, 1, [&](Stream &strm, bool last_connection, bool &connection_close) {
sock_, 1,
[&](Stream &strm, bool last_connection, bool &connection_close) {
return handle_request(strm, req, res, last_connection,
connection_close);
});
@@ -3744,18 +3813,18 @@ inline bool Client::send(const std::vector<Request> &requests,
std::vector<Response> &responses) {
size_t i = 0;
while (i < requests.size()) {
auto sock = create_client_socket();
if (sock == INVALID_SOCKET) { return false; }
sock_ = create_client_socket();
if (sock_ == INVALID_SOCKET) { return false; }
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
if (is_ssl() && !proxy_host_.empty()) {
Response res;
bool error;
if (!connect(sock, res, error)) { return false; }
if (!connect(sock_, res, error)) { return false; }
}
#endif
if (!process_and_close_socket(sock, requests.size() - i,
if (!process_and_close_socket(sock_, requests.size() - i,
[&](Stream &strm, bool last_connection,
bool &connection_close) -> bool {
auto &req = requests[i++];
@@ -3885,34 +3954,42 @@ inline bool Client::redirect(const Request &req, Response &res) {
if (location.empty()) { return false; }
const static std::regex re(
R"(^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
R"(^(?:(https?):)?(?://([^:/?#]*)(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
std::smatch m;
if (!regex_match(location, m, re)) { return false; }
if (!std::regex_match(location, m, re)) { return false; }
auto scheme = is_ssl() ? "https" : "http";
auto next_scheme = m[1].str();
auto next_host = m[2].str();
auto next_path = m[3].str();
if (next_scheme.empty()) { next_scheme = scheme; }
auto port_str = m[3].str();
auto next_path = m[4].str();
auto next_port = port_;
if (!port_str.empty()) {
next_port = std::stoi(port_str);
} else if (!next_scheme.empty()) {
next_port = next_scheme == "https" ? 443 : 80;
}
if (next_scheme.empty()) { next_scheme = scheme; }
if (next_host.empty()) { next_host = host_; }
if (next_path.empty()) { next_path = "/"; }
if (next_scheme == scheme && next_host == host_) {
if (next_scheme == scheme && next_host == host_ && next_port == port_) {
return detail::redirect(*this, req, res, next_path);
} else {
if (next_scheme == "https") {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
SSLClient cli(next_host.c_str());
SSLClient cli(next_host.c_str(), next_port);
cli.copy_settings(*this);
return detail::redirect(cli, req, res, next_path);
#else
return false;
#endif
} else {
Client cli(next_host.c_str());
Client cli(next_host.c_str(), next_port);
cli.copy_settings(*this);
return detail::redirect(cli, req, res, next_path);
}
@@ -4022,7 +4099,7 @@ inline std::shared_ptr<Response> Client::send_with_content_provider(
req.headers = headers;
req.path = path;
req.headers.emplace("Content-Type", content_type);
if (content_type) { req.headers.emplace("Content-Type", content_type); }
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
if (compress_) {
@@ -4214,6 +4291,10 @@ inline std::shared_ptr<Response> Client::Head(const char *path,
return send(req, *res) ? res : nullptr;
}
inline std::shared_ptr<Response> Client::Post(const char *path) {
return Post(path, std::string(), nullptr);
}
inline std::shared_ptr<Response> Client::Post(const char *path,
const std::string &body,
const char *content_type) {
@@ -4286,6 +4367,10 @@ Client::Post(const char *path, const Headers &headers,
return Post(path, headers, body, content_type.c_str());
}
inline std::shared_ptr<Response> Client::Put(const char *path) {
return Put(path, std::string(), nullptr);
}
inline std::shared_ptr<Response> Client::Put(const char *path,
const std::string &body,
const char *content_type) {
@@ -4403,6 +4488,14 @@ inline std::shared_ptr<Response> Client::Options(const char *path,
return send(req, *res) ? res : nullptr;
}
inline void Client::stop() {
if (sock_ != INVALID_SOCKET) {
std::atomic<socket_t> sock(sock_.exchange(INVALID_SOCKET));
detail::shutdown_socket(sock);
detail::close_socket(sock);
}
}
inline void Client::set_timeout_sec(time_t timeout_sec) {
timeout_sec_ = timeout_sec;
}
@@ -4501,8 +4594,8 @@ inline bool process_and_close_socket_ssl(
auto count = keep_alive_max_count;
while (count > 0 &&
(is_client_request ||
detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND,
CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) {
select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND,
CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) {
SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec);
auto last_connection = count == 1;
auto connection_close = false;
@@ -4613,8 +4706,9 @@ inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
return -1;
}
inline std::string SSLSocketStream::get_remote_addr() const {
return detail::get_remote_addr(sock_);
inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
int &port) const {
detail::get_remote_ip_and_port(sock_, ip, port);
}
static SSLInit sslinit_;
@@ -4768,7 +4862,7 @@ inline long SSLClient::get_openssl_verify_result() const {
return verify_result_;
}
inline SSL_CTX *SSLClient::ssl_context() const noexcept { return ctx_; }
inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; }
inline bool SSLClient::process_and_close_socket(
socket_t sock, size_t request_count,
@@ -4959,8 +5053,73 @@ inline bool SSLClient::check_host_name(const char *pattern,
}
#endif
namespace url {
struct Options {
// TODO: support more options...
bool follow_location = false;
std::string client_cert_path;
std::string client_key_path;
std::string ca_cert_file_path;
std::string ca_cert_dir_path;
bool server_certificate_verification = false;
};
inline std::shared_ptr<Response> Get(const char *url, Options &options) {
const static std::regex re(
R"(^(https?)://([^:/?#]+)(?::(\d+))?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
std::cmatch m;
if (!std::regex_match(url, m, re)) { return nullptr; }
auto next_scheme = m[1].str();
auto next_host = m[2].str();
auto port_str = m[3].str();
auto next_path = m[4].str();
auto next_port = !port_str.empty() ? std::stoi(port_str)
: (next_scheme == "https" ? 443 : 80);
if (next_path.empty()) { next_path = "/"; }
if (next_scheme == "https") {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
SSLClient cli(next_host.c_str(), next_port, options.client_cert_path,
options.client_key_path);
cli.set_follow_location(options.follow_location);
cli.set_ca_cert_path(options.ca_cert_file_path.c_str(),
options.ca_cert_dir_path.c_str());
cli.enable_server_certificate_verification(
options.server_certificate_verification);
return cli.Get(next_path.c_str());
#else
return nullptr;
#endif
} else {
Client cli(next_host.c_str(), next_port, options.client_cert_path,
options.client_key_path);
cli.set_follow_location(options.follow_location);
return cli.Get(next_path.c_str());
}
}
inline std::shared_ptr<Response> Get(const char *url) {
Options options;
return Get(url, options);
}
} // namespace url
namespace detail {
#undef HANDLE_EINTR
} // namespace detail
// ----------------------------------------------------------------------------
} // namespace httplib
#endif // CPPHTTPLIB_HTTPLIB_H
+189 -17
View File
@@ -649,12 +649,78 @@ TEST(YahooRedirectTest, Redirect) {
EXPECT_EQ(200, res->status);
}
TEST(YahooRedirectTestWithURL, Redirect) {
auto res = httplib::url::Get("http://yahoo.com");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(301, res->status);
httplib::url::Options options;
options.follow_location = true;
res = httplib::url::Get("http://yahoo.com", options);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
}
TEST(HttpsToHttpRedirectTest, Redirect) {
httplib::SSLClient cli("httpbin.org");
cli.set_follow_location(true);
auto res =
cli.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
}
TEST(HttpsToHttpRedirectTestWithURL, Redirect) {
httplib::url::Options options;
options.follow_location = true;
auto res = httplib::url::Get(
"https://httpbin.org/"
"redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302",
options);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
}
TEST(RedirectToDifferentPort, Redirect) {
Server svr8080;
Server svr8081;
svr8080.Get("/1", [&](const Request & /*req*/, Response &res) {
res.set_redirect("http://localhost:8081/2");
});
svr8081.Get("/2", [&](const Request & /*req*/, Response &res) {
res.set_content("Hello World!", "text/plain");
});
auto thread8080 = std::thread([&]() { svr8080.listen("localhost", 8080); });
auto thread8081 = std::thread([&]() { svr8081.listen("localhost", 8081); });
while (!svr8080.is_running() || !svr8081.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
Client cli("localhost", 8080);
cli.set_follow_location(true);
auto res = cli.Get("/1");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
EXPECT_EQ(res->body, "Hello World!");
svr8080.stop();
svr8081.stop();
thread8080.join();
thread8081.join();
ASSERT_FALSE(svr8080.is_running());
ASSERT_FALSE(svr8081.is_running());
}
#endif
@@ -735,6 +801,10 @@ protected:
.Get("/remote_addr",
[&](const Request &req, Response &res) {
auto remote_addr = req.headers.find("REMOTE_ADDR")->second;
EXPECT_TRUE(req.has_header("REMOTE_PORT"));
EXPECT_EQ(req.remote_addr, req.get_header_value("REMOTE_ADDR"));
EXPECT_EQ(req.remote_port,
std::stoi(req.get_header_value("REMOTE_PORT")));
res.set_content(remote_addr.c_str(), "text/plain");
})
.Get("/endwith%",
@@ -871,13 +941,13 @@ protected:
{
const auto &file = req.get_file_value("text1");
EXPECT_EQ("", file.filename);
EXPECT_TRUE(file.filename.empty());
EXPECT_EQ("text default", file.content);
}
{
const auto &file = req.get_file_value("text2");
EXPECT_EQ("", file.filename);
EXPECT_TRUE(file.filename.empty());
EXPECT_EQ("aωb", file.content);
}
@@ -890,7 +960,7 @@ protected:
{
const auto &file = req.get_file_value("file3");
EXPECT_EQ("", file.filename);
EXPECT_TRUE(file.filename.empty());
EXPECT_EQ("application/octet-stream", file.content_type);
EXPECT_EQ(0u, file.content.size());
}
@@ -898,8 +968,24 @@ protected:
.Post("/empty",
[&](const Request &req, Response &res) {
EXPECT_EQ(req.body, "");
EXPECT_EQ("text/plain", req.get_header_value("Content-Type"));
EXPECT_EQ("0", req.get_header_value("Content-Length"));
res.set_content("empty", "text/plain");
})
.Post("/empty-no-content-type",
[&](const Request &req, Response &res) {
EXPECT_EQ(req.body, "");
EXPECT_FALSE(req.has_header("Content-Type"));
EXPECT_EQ("0", req.get_header_value("Content-Length"));
res.set_content("empty-no-content-type", "text/plain");
})
.Put("/empty-no-content-type",
[&](const Request &req, Response &res) {
EXPECT_EQ(req.body, "");
EXPECT_FALSE(req.has_header("Content-Type"));
EXPECT_EQ("0", req.get_header_value("Content-Length"));
res.set_content("empty-no-content-type", "text/plain");
})
.Put("/put",
[&](const Request &req, Response &res) {
EXPECT_EQ(req.body, "PUT");
@@ -970,13 +1056,13 @@ protected:
{
const auto &file = get_file_value(files, "text1");
EXPECT_EQ("", file.filename);
EXPECT_TRUE(file.filename.empty());
EXPECT_EQ("text default", file.content);
}
{
const auto &file = get_file_value(files, "text2");
EXPECT_EQ("", file.filename);
EXPECT_TRUE(file.filename.empty());
EXPECT_EQ("aωb", file.content);
}
@@ -989,7 +1075,7 @@ protected:
{
const auto &file = get_file_value(files, "file3");
EXPECT_EQ("", file.filename);
EXPECT_TRUE(file.filename.empty());
EXPECT_EQ("application/octet-stream", file.content_type);
EXPECT_EQ(0u, file.content.size());
}
@@ -1054,13 +1140,13 @@ protected:
{
const auto &file = req.get_file_value("key1");
EXPECT_EQ("", file.filename);
EXPECT_TRUE(file.filename.empty());
EXPECT_EQ("test", file.content);
}
{
const auto &file = req.get_file_value("key2");
EXPECT_EQ("", file.filename);
EXPECT_TRUE(file.filename.empty());
EXPECT_EQ("--abcdefg123", file.content);
}
})
@@ -1142,7 +1228,7 @@ TEST_F(ServerTest, HeadMethod200) {
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
EXPECT_EQ("", res->body);
EXPECT_TRUE(res->body.empty());
}
TEST_F(ServerTest, HeadMethod200Static) {
@@ -1151,14 +1237,14 @@ TEST_F(ServerTest, HeadMethod200Static) {
EXPECT_EQ(200, res->status);
EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
EXPECT_EQ(104, std::stoi(res->get_header_value("Content-Length")));
EXPECT_EQ("", res->body);
EXPECT_TRUE(res->body.empty());
}
TEST_F(ServerTest, HeadMethod404) {
auto res = cli_.Head("/invalid");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(404, res->status);
EXPECT_EQ("", res->body);
EXPECT_TRUE(res->body.empty());
}
TEST_F(ServerTest, GetMethodPersonJohn) {
@@ -1244,6 +1330,20 @@ TEST_F(ServerTest, PostEmptyContent) {
ASSERT_EQ("empty", res->body);
}
TEST_F(ServerTest, PostEmptyContentWithNoContentType) {
auto res = cli_.Post("/empty-no-content-type");
ASSERT_TRUE(res != nullptr);
ASSERT_EQ(200, res->status);
ASSERT_EQ("empty-no-content-type", res->body);
}
TEST_F(ServerTest, PutEmptyContentWithNoContentType) {
auto res = cli_.Put("/empty-no-content-type");
ASSERT_TRUE(res != nullptr);
ASSERT_EQ(200, res->status);
ASSERT_EQ("empty-no-content-type", res->body);
}
TEST_F(ServerTest, GetMethodDir) {
auto res = cli_.Get("/dir/");
ASSERT_TRUE(res != nullptr);
@@ -1647,6 +1747,18 @@ TEST_F(ServerTest, GetStreamedEndless) {
ASSERT_TRUE(res == nullptr);
}
TEST_F(ServerTest, ClientStop) {
thread t = thread([&]() {
auto res =
cli_.Get("/streamed-cancel",
[&](const char *, uint64_t) { return true; });
ASSERT_TRUE(res == nullptr);
});
std::this_thread::sleep_for(std::chrono::seconds(1));
cli_.stop();
t.join();
}
TEST_F(ServerTest, GetWithRange1) {
auto res = cli_.Get("/with-range", {{make_range_header({{3, 5}})}});
ASSERT_TRUE(res != nullptr);
@@ -1992,7 +2104,7 @@ TEST_F(ServerTest, GzipWithoutAcceptEncoding) {
auto res = cli_.Get("/gzip", headers);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ("", res->get_header_value("Content-Encoding"));
EXPECT_TRUE(res->get_header_value("Content-Encoding").empty());
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
EXPECT_EQ("100", res->get_header_value("Content-Length"));
EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456"
@@ -2033,7 +2145,7 @@ TEST_F(ServerTest, GzipWithContentReceiverWithoutAcceptEncoding) {
});
ASSERT_TRUE(res != nullptr);
EXPECT_EQ("", res->get_header_value("Content-Encoding"));
EXPECT_TRUE(res->get_header_value("Content-Encoding").empty());
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
EXPECT_EQ("100", res->get_header_value("Content-Length"));
EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456"
@@ -2093,7 +2205,8 @@ TEST_F(ServerTest, MultipartFormDataGzip) {
#endif
// Sends a raw request to a server listening at HOST:PORT.
static bool send_request(time_t read_timeout_sec, const std::string &req) {
static bool send_request(time_t read_timeout_sec, const std::string &req,
std::string *resp = nullptr) {
auto client_sock = detail::create_client_socket(HOST, PORT, /*timeout_sec=*/5,
std::string());
@@ -2111,7 +2224,9 @@ static bool send_request(time_t read_timeout_sec, const std::string &req) {
char buf[512];
detail::stream_line_reader line_reader(strm, buf, sizeof(buf));
while (line_reader.getline()) {}
while (line_reader.getline()) {
if (resp) { *resp += line_reader.ptr(); }
}
return true;
});
}
@@ -2143,11 +2258,15 @@ TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
}
// Sends a raw request and verifies that there isn't a crash or exception.
static void test_raw_request(const std::string &req) {
static void test_raw_request(const std::string &req,
std::string *out = nullptr) {
Server svr;
svr.Get("/hi", [&](const Request & /*req*/, Response &res) {
res.set_content("ok", "text/plain");
});
svr.Put("/put_hi", [&](const Request & /*req*/, Response &res) {
res.set_content("ok", "text/plain");
});
// Server read timeout must be longer than the client read timeout for the
// bug to reproduce, probably to force the server to process a request
@@ -2160,7 +2279,7 @@ static void test_raw_request(const std::string &req) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_TRUE(send_request(client_read_timeout_sec, req));
ASSERT_TRUE(send_request(client_read_timeout_sec, req, out));
svr.stop();
t.join();
EXPECT_TRUE(listen_thread_ok);
@@ -2214,6 +2333,59 @@ TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity2) {
"&&&%%%");
}
TEST(ServerRequestParsingTest, ExcessiveWhitespaceInUnparseableHeaderLine) {
// Make sure this doesn't crash the server.
// In a previous version of the header line regex, the "\r" rendered the line
// unparseable and the regex engine repeatedly backtracked, trying to look for
// a new position where the leading white space ended and the field value
// began.
// The crash occurs with libc++ but not libstdc++.
test_raw_request("GET /hi HTTP/1.1\r\n"
"a:" + std::string(2000, ' ') + '\r' + std::string(20, 'z') +
"\r\n"
"\r\n");
}
TEST(ServerRequestParsingTest, InvalidFirstChunkLengthInRequest) {
std::string out;
test_raw_request("PUT /put_hi HTTP/1.1\r\n"
"Content-Type: text/plain\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"nothex\r\n",
&out);
EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24));
}
TEST(ServerRequestParsingTest, InvalidSecondChunkLengthInRequest) {
std::string out;
test_raw_request("PUT /put_hi HTTP/1.1\r\n"
"Content-Type: text/plain\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"3\r\n"
"xyz\r\n"
"NaN\r\n",
&out);
EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24));
}
TEST(ServerRequestParsingTest, ChunkLengthTooHighInRequest) {
std::string out;
test_raw_request("PUT /put_hi HTTP/1.1\r\n"
"Content-Type: text/plain\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
// Length is too large for 64 bits.
"1ffffffffffffffff\r\n"
"xyz\r\n",
&out);
EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24));
}
TEST(ServerStopTest, StopServerWithChunkedTransmission) {
Server svr;
+13 -13
View File
@@ -28,7 +28,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
@@ -40,7 +40,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
@@ -69,13 +69,13 @@
<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>
<IncludePath>$(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>
<IncludePath>$(IncludePath)</IncludePath>
<LibraryPath>$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
@@ -84,14 +84,14 @@
</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>
<IncludePath>$(IncludePath)</IncludePath>
<LibraryPath>$(LibraryPath)</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
@@ -108,7 +108,7 @@
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
@@ -118,12 +118,12 @@
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>Ws2_32.lib;libssl.lib;libcrypto.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
@@ -144,7 +144,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
@@ -160,7 +160,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>Ws2_32.lib;libssl.lib;libcrypto.lib;libssl.lib;libcrypto.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>