Compare commits

..

38 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
yhirose
531708816a Update README 2019-09-06 18:29:22 -04:00
yhirose
bfec81998b Code cleanup 2019-09-06 18:16:42 -04:00
yhirose
c9238434e1 Added redirect support (Fix #211) 2019-09-06 18:07:35 -04:00
5 changed files with 568 additions and 125 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
@@ -333,6 +335,19 @@ if (cli.send(requests, responses)) {
}
```
### Redirect
```cpp
httplib::Client cli("yahoo.com");
auto res = cli.Get("/");
res->status; // 301
cli.follow_location(true);
res = cli.Get("/");
res->status; // 200
```
OpenSSL Support
---------------

502
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,19 +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_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 {
@@ -146,12 +191,15 @@ typedef std::function<void(size_t offset, size_t length, DataSink sink,
Done done)>
ContentProvider;
typedef std::function<bool(const char *data, size_t data_length,
size_t offset, uint64_t content_length)>
typedef std::function<bool(const char *data, size_t data_length, size_t offset,
uint64_t content_length)>
ContentReceiver;
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;
@@ -172,17 +220,22 @@ typedef std::pair<ssize_t, ssize_t> Range;
typedef std::vector<Range> Ranges;
struct Request {
std::string version;
std::string method;
std::string target;
std::string path;
Headers headers;
std::string body;
// for server
std::string version;
std::string target;
Params params;
MultipartFiles files;
Ranges ranges;
Match matches;
// for client
size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT;
ResponseHandler response_handler;
ContentReceiver content_receiver;
Progress progress;
@@ -349,7 +402,7 @@ private:
pool_.jobs_.pop_front();
}
assert(true == (bool)fn);
assert(true == static_cast<bool>(fn));
fn();
}
}
@@ -491,77 +544,113 @@ public:
virtual bool is_valid() const;
std::shared_ptr<Response> Get(const char *path, Progress progress = nullptr);
std::shared_ptr<Response> Get(const char *path);
std::shared_ptr<Response> Get(const char *path, const Headers &headers);
std::shared_ptr<Response> Get(const char *path, Progress progress);
std::shared_ptr<Response> Get(const char *path, const Headers &headers,
Progress progress = nullptr);
Progress progress);
std::shared_ptr<Response> Get(const char *path,
ContentReceiver content_receiver,
Progress progress = nullptr);
ContentReceiver content_receiver);
std::shared_ptr<Response> Get(const char *path, const Headers &headers,
ContentReceiver content_receiver);
std::shared_ptr<Response>
Get(const char *path, ContentReceiver content_receiver, Progress progress);
std::shared_ptr<Response> Get(const char *path, const Headers &headers,
ContentReceiver content_receiver,
Progress progress = nullptr);
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);
std::shared_ptr<Response> Post(const char *path, const std::string &body,
const char *content_type);
std::shared_ptr<Response> Post(const char *path, const Headers &headers,
const std::string &body,
const char *content_type);
std::shared_ptr<Response> Post(const char *path, const Params &params);
std::shared_ptr<Response> Post(const char *path, const Headers &headers,
const Params &params);
std::shared_ptr<Response> Post(const char *path,
const MultipartFormDataItems &items);
std::shared_ptr<Response> Post(const char *path, const Headers &headers,
const MultipartFormDataItems &items);
std::shared_ptr<Response> Put(const char *path, const std::string &body,
const char *content_type);
std::shared_ptr<Response> Put(const char *path, const Headers &headers,
const std::string &body,
const char *content_type);
std::shared_ptr<Response> Patch(const char *path, const std::string &body,
const char *content_type);
std::shared_ptr<Response> Patch(const char *path, const Headers &headers,
const std::string &body,
const char *content_type);
std::shared_ptr<Response> Delete(const char *path,
const std::string &body = std::string(),
const char *content_type = nullptr);
std::shared_ptr<Response> Delete(const char *path);
std::shared_ptr<Response> Delete(const char *path, const std::string &body,
const char *content_type);
std::shared_ptr<Response> Delete(const char *path, const Headers &headers);
std::shared_ptr<Response> Delete(const char *path, const Headers &headers,
const std::string &body = std::string(),
const char *content_type = nullptr);
const std::string &body,
const char *content_type);
std::shared_ptr<Response> Options(const char *path);
std::shared_ptr<Response> Options(const char *path, const Headers &headers);
bool send(Request &req, Response &res);
bool send(const Request &req, Response &res);
bool send(std::vector<Request> &requests, std::vector<Response>& responses);
bool send(const std::vector<Request> &requests,
std::vector<Response> &responses);
void set_keep_alive_max_count(size_t count);
void follow_location(bool on);
protected:
bool process_request(Stream &strm, Request &req, Response &res,
bool &connection_close);
bool process_request(Stream &strm, const Request &req, Response &res,
bool last_connection, bool &connection_close);
const std::string host_;
const int port_;
time_t timeout_sec_;
const std::string host_and_port_;
size_t keep_alive_max_count_;
size_t follow_location_;
private:
socket_t create_client_socket() const;
bool read_response_line(Stream &strm, Response &res);
void write_request(Stream &strm, Request &req);
void write_request(Stream &strm, const Request &req, bool last_connection);
bool redirect(const Request &req, Response &res);
virtual bool process_and_close_socket(
socket_t sock, size_t request_count,
@@ -572,7 +661,8 @@ private:
virtual bool is_ssl() const;
};
inline void Get(std::vector<Request> &requests, const char *path, const Headers &headers) {
inline void Get(std::vector<Request> &requests, const char *path,
const Headers &headers) {
Request req;
req.method = "GET";
req.path = path;
@@ -584,7 +674,9 @@ inline void Get(std::vector<Request> &requests, const char *path) {
Get(requests, path, Headers());
}
inline void Post(std::vector<Request> &requests, const char *path, const Headers &headers, const std::string &body, const char *content_type) {
inline void Post(std::vector<Request> &requests, const char *path,
const Headers &headers, const std::string &body,
const char *content_type) {
Request req;
req.method = "POST";
req.path = path;
@@ -594,7 +686,8 @@ inline void Post(std::vector<Request> &requests, const char *path, const Headers
requests.emplace_back(std::move(req));
}
inline void Post(std::vector<Request> &requests, const char *path, const std::string &body, const char *content_type) {
inline void Post(std::vector<Request> &requests, const char *path,
const std::string &body, const char *content_type) {
Post(requests, path, Headers(), body, content_type);
}
@@ -648,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,
@@ -937,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);
@@ -946,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);
@@ -960,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>
@@ -1060,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
@@ -1101,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;
}
@@ -1186,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;
@@ -1194,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);
@@ -1231,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);
@@ -1443,7 +1559,8 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
return ret;
}
template <typename T> inline int write_headers(Stream &strm, const T &info) {
template <typename T>
inline int write_headers(Stream &strm, const T &info, const Headers &headers) {
auto write_len = 0;
for (const auto &x : info.headers) {
auto len =
@@ -1451,6 +1568,12 @@ template <typename T> inline int write_headers(Stream &strm, const T &info) {
if (len < 0) { return len; }
write_len += len;
}
for (const auto &x : headers) {
auto len =
strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str());
if (len < 0) { return len; }
write_len += len;
}
auto len = strm.write("\r\n");
if (len < 0) { return len; }
write_len += len;
@@ -1458,7 +1581,7 @@ template <typename T> inline int write_headers(Stream &strm, const T &info) {
}
inline ssize_t write_content(Stream &strm, ContentProvider content_provider,
size_t offset, size_t length) {
size_t offset, size_t length) {
size_t begin_offset = offset;
size_t end_offset = offset + length;
while (offset < end_offset) {
@@ -1476,7 +1599,7 @@ inline ssize_t write_content(Stream &strm, ContentProvider content_provider,
}
inline ssize_t write_content_chunked(Stream &strm,
ContentProvider content_provider) {
ContentProvider content_provider) {
size_t offset = 0;
auto data_available = true;
ssize_t total_written_length = 0;
@@ -1503,6 +1626,25 @@ inline ssize_t write_content_chunked(Stream &strm,
return total_written_length;
}
template <typename T>
inline bool redirect(T &cli, const Request &req, Response &res,
const std::string &path) {
Request new_req;
new_req.method = req.method;
new_req.path = path;
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;
Response new_res;
auto ret = cli.send(new_req, new_res);
if (ret) { res = new_res; }
return ret;
}
inline std::string encode_url(const std::string &s) {
std::string result;
@@ -1674,23 +1816,27 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) {
if (std::regex_match(s, m, re)) {
auto pos = m.position(1);
auto len = m.length(1);
detail::split(
&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
static auto re = std::regex(R"(\s*(\d*)-(\d*))");
std::cmatch m;
if (std::regex_match(b, e, m, re)) {
ssize_t first = -1;
if (!m.str(1).empty()) { first = static_cast<ssize_t>(std::stoll(m.str(1))); }
detail::split(&s[pos], &s[pos + len], ',',
[&](const char *b, const char *e) {
static auto re = std::regex(R"(\s*(\d*)-(\d*))");
std::cmatch m;
if (std::regex_match(b, e, m, re)) {
ssize_t first = -1;
if (!m.str(1).empty()) {
first = static_cast<ssize_t>(std::stoll(m.str(1)));
}
ssize_t last = -1;
if (!m.str(2).empty()) { last = static_cast<ssize_t>(std::stoll(m.str(2))); }
ssize_t last = -1;
if (!m.str(2).empty()) {
last = static_cast<ssize_t>(std::stoll(m.str(2)));
}
if (first != -1 && last != -1 && first > last) {
throw std::runtime_error("invalid range error");
}
ranges.emplace_back(std::make_pair(first, last));
}
});
if (first != -1 && last != -1 && first > last) {
throw std::runtime_error("invalid range error");
}
ranges.emplace_back(std::make_pair(first, last));
}
});
return true;
}
return false;
@@ -1742,8 +1888,7 @@ get_range_offset_and_length(const Request &req, size_t content_length,
return std::make_pair(r.first, r.second - r.first + 1);
}
inline std::string make_content_range_header_field(size_t offset,
size_t length,
inline std::string make_content_range_header_field(size_t offset, size_t length,
size_t content_length) {
std::string field = "bytes ";
field += std::to_string(offset);
@@ -1988,9 +2133,8 @@ inline void Response::set_chunked_content_provider(
std::function<void(size_t offset, DataSink sink, Done done)> provider,
std::function<void()> resource_releaser) {
content_provider_resource_length = 0;
content_provider = [provider](size_t offset, size_t, DataSink sink, Done done) {
provider(offset, sink, done);
};
content_provider = [provider](size_t offset, size_t, DataSink sink,
Done done) { provider(offset, sink, done); };
content_provider_resource_releaser = resource_releaser;
}
@@ -2178,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;
@@ -2300,7 +2444,7 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
res.set_header("Content-Length", length);
}
if (!detail::write_headers(strm, res)) { return false; }
if (!detail::write_headers(strm, res, Headers())) { return false; }
// Body
if (req.method != "HEAD") {
@@ -2436,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;
@@ -2464,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;
}
@@ -2532,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()) {
@@ -2570,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);
@@ -2591,7 +2743,8 @@ inline bool Server::process_and_close_socket(socket_t sock) {
inline Client::Client(const char *host, int port, time_t timeout_sec)
: host_(host), port_(port), timeout_sec_(timeout_sec),
host_and_port_(host_ + ":" + std::to_string(port_)),
keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT) {}
keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT),
follow_location_(false) {}
inline Client::~Client() {}
@@ -2635,20 +2788,27 @@ inline bool Client::read_response_line(Stream &strm, Response &res) {
return true;
}
inline bool Client::send(Request &req, Response &res) {
inline bool Client::send(const Request &req, Response &res) {
if (req.path.empty()) { return false; }
auto sock = create_client_socket();
if (sock == INVALID_SOCKET) { return false; }
return process_and_close_socket(
sock, 1,
[&](Stream &strm, bool /*last_connection*/, bool &connection_close) {
return process_request(strm, req, res, connection_close);
auto ret = process_and_close_socket(
sock, 1, [&](Stream &strm, bool last_connection, bool &connection_close) {
return process_request(strm, req, res, last_connection,
connection_close);
});
if (ret && follow_location_ && (300 < res.status && res.status < 400)) {
ret = redirect(req, res);
}
return ret;
}
inline bool Client::send(std::vector<Request> &requests, std::vector<Response>& responses) {
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();
@@ -2656,15 +2816,22 @@ inline bool Client::send(std::vector<Request> &requests, std::vector<Response>&
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++;
if (req.path.empty()) { return false; }
if (last_connection) { req.set_header("Connection", "close"); }
auto ret = process_request(strm, req, res, connection_close);
auto ret = process_request(strm, req, res, last_connection,
connection_close);
if (ret && follow_location_ &&
(300 < res.status && res.status < 400)) {
ret = redirect(req, res);
}
if (ret) { responses.emplace_back(std::move(res)); }
return ret;
})) {
return false;
@@ -2674,7 +2841,48 @@ inline bool Client::send(std::vector<Request> &requests, std::vector<Response>&
return true;
}
inline void Client::write_request(Stream &strm, Request &req) {
inline bool Client::redirect(const Request &req, Response &res) {
if (req.redirect_count == 0) { return false; }
auto location = res.get_header_value("location");
if (location.empty()) { return false; }
std::regex re(
R"(^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
auto scheme = is_ssl() ? "https" : "http";
std::smatch m;
if (regex_match(location, m, re)) {
auto next_scheme = m[1].str();
auto next_host = m[2].str();
auto next_path = m[3].str();
if (next_host.empty()) { next_host = host_; }
if (next_path.empty()) { next_path = "/"; }
if (next_scheme == scheme && next_host == host_) {
return detail::redirect(*this, req, res, next_path);
} else {
if (next_scheme == "https") {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
SSLClient cli(next_host.c_str());
cli.follow_location(true);
return detail::redirect(cli, req, res, next_path);
#else
return false;
#endif
} else {
Client cli(next_host.c_str());
cli.follow_location(true);
return detail::redirect(cli, req, res, next_path);
}
}
}
return false;
}
inline void Client::write_request(Stream &strm, const Request &req,
bool last_connection) {
BufferStream bstrm;
// Request line
@@ -2682,45 +2890,48 @@ inline void Client::write_request(Stream &strm, Request &req) {
bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str());
// Headers
// Additonal headers
Headers headers;
if (last_connection) { headers.emplace("Connection", "close"); }
if (!req.has_header("Host")) {
if (is_ssl()) {
if (port_ == 443) {
req.set_header("Host", host_);
headers.emplace("Host", host_);
} else {
req.set_header("Host", host_and_port_);
headers.emplace("Host", host_and_port_);
}
} else {
if (port_ == 80) {
req.set_header("Host", host_);
headers.emplace("Host", host_);
} else {
req.set_header("Host", host_and_port_);
headers.emplace("Host", host_and_port_);
}
}
}
if (!req.has_header("Accept")) { req.set_header("Accept", "*/*"); }
if (!req.has_header("Accept")) { headers.emplace("Accept", "*/*"); }
if (!req.has_header("User-Agent")) {
req.set_header("User-Agent", "cpp-httplib/0.2");
headers.emplace("User-Agent", "cpp-httplib/0.2");
}
if (req.body.empty()) {
if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH") {
req.set_header("Content-Length", "0");
headers.emplace("Content-Length", "0");
}
} else {
if (!req.has_header("Content-Type")) {
req.set_header("Content-Type", "text/plain");
headers.emplace("Content-Type", "text/plain");
}
if (!req.has_header("Content-Length")) {
auto length = std::to_string(req.body.size());
req.set_header("Content-Length", length);
headers.emplace("Content-Length", length);
}
}
detail::write_headers(bstrm, req);
detail::write_headers(bstrm, req, headers);
// Body
if (!req.body.empty()) { bstrm.write(req.body); }
@@ -2730,10 +2941,11 @@ inline void Client::write_request(Stream &strm, Request &req) {
strm.write(data.data(), data.size());
}
inline bool Client::process_request(Stream &strm, Request &req, Response &res,
inline bool Client::process_request(Stream &strm, const Request &req,
Response &res, bool last_connection,
bool &connection_close) {
// Send request
write_request(strm, req);
write_request(strm, req, last_connection);
// Receive response and headers
if (!read_response_line(strm, res) ||
@@ -2746,12 +2958,14 @@ inline bool Client::process_request(Stream &strm, Request &req, Response &res,
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) {
if (res.body.size() + n > res.body.max_size()) {
return false;
}
if (res.body.size() + n > res.body.max_size()) { return false; }
res.body.append(buf, n);
return true;
};
@@ -2788,11 +3002,22 @@ inline bool Client::process_and_close_socket(
inline bool Client::is_ssl() const { return false; }
inline std::shared_ptr<Response> Client::Get(const char *path) {
Progress dummy;
return Get(path, Headers(), dummy);
}
inline std::shared_ptr<Response> Client::Get(const char *path,
Progress progress) {
return Get(path, Headers(), progress);
}
inline std::shared_ptr<Response> Client::Get(const char *path,
const Headers &headers) {
Progress dummy;
return Get(path, headers, dummy);
}
inline std::shared_ptr<Response>
Client::Get(const char *path, const Headers &headers, Progress progress) {
Request req;
@@ -2805,20 +3030,50 @@ Client::Get(const char *path, const Headers &headers, Progress progress) {
return send(req, *res) ? res : nullptr;
}
inline std::shared_ptr<Response> Client::Get(const char *path,
ContentReceiver content_receiver) {
Progress 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, 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;
@@ -2968,12 +3223,21 @@ inline std::shared_ptr<Response> Client::Patch(const char *path,
return send(req, *res) ? res : nullptr;
}
inline std::shared_ptr<Response> Client::Delete(const char *path) {
return Delete(path, Headers(), std::string(), nullptr);
}
inline std::shared_ptr<Response> Client::Delete(const char *path,
const std::string &body,
const char *content_type) {
return Delete(path, Headers(), body, content_type);
}
inline std::shared_ptr<Response> Client::Delete(const char *path,
const Headers &headers) {
return Delete(path, headers, std::string(), nullptr);
}
inline std::shared_ptr<Response> Client::Delete(const char *path,
const Headers &headers,
const std::string &body,
@@ -3011,6 +3275,8 @@ inline void Client::set_keep_alive_max_count(size_t count) {
keep_alive_max_count_ = count;
}
inline void Client::follow_location(bool on) { follow_location_ = on; }
/*
* SSL Implementation
*/
@@ -3036,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)) {
@@ -3142,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) {
@@ -3261,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
@@ -431,6 +463,87 @@ TEST(BaseAuthTest, FromHTTPWatch) {
}
}
TEST(AbsoluteRedirectTest, Redirect) {
auto host = "httpbin.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
httplib::SSLClient cli(host);
#else
httplib::Client cli(host);
#endif
cli.follow_location(true);
auto res = cli.Get("/absolute-redirect/3");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
}
TEST(RedirectTest, Redirect) {
auto host = "httpbin.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
httplib::SSLClient cli(host);
#else
httplib::Client cli(host);
#endif
cli.follow_location(true);
auto res = cli.Get("/redirect/3");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
}
TEST(RelativeRedirectTest, Redirect) {
auto host = "httpbin.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
httplib::SSLClient cli(host);
#else
httplib::Client cli(host);
#endif
cli.follow_location(true);
auto res = cli.Get("/relative-redirect/3");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
}
TEST(TooManyRedirectTest, Redirect) {
auto host = "httpbin.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
httplib::SSLClient cli(host);
#else
httplib::Client cli(host);
#endif
cli.follow_location(true);
auto res = cli.Get("/redirect/21");
ASSERT_TRUE(res == nullptr);
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
TEST(YahooRedirectTest, Redirect) {
httplib::Client cli("yahoo.com");
auto res = cli.Get("/");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(301, res->status);
cli.follow_location(true);
res = cli.Get("/");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
}
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");
ASSERT_TRUE(res != nullptr);
}
#endif
TEST(Server, BindAndListenSeparately) {
Server svr;
int port = svr.bind_to_any_port("localhost");
@@ -524,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; });
})
@@ -1280,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>