mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-06-12 09:37:15 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39c7bba7b9 | ||
|
|
f2476f21fc | ||
|
|
c776454c84 | ||
|
|
82a5ac735f | ||
|
|
08bf806e92 | ||
|
|
9a41b16cbb | ||
|
|
10759f0a38 | ||
|
|
58b2814fda | ||
|
|
260422b7d7 | ||
|
|
d2c7b447d5 | ||
|
|
72b20c08da | ||
|
|
afd6d5f9dc | ||
|
|
e5827ad16f |
5
.clang-format
Normal file
5
.clang-format
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
BasedOnStyle: LLVM
|
||||||
|
AllowShortBlocksOnASingleLine: true
|
||||||
|
AllowShortCaseLabelsOnASingleLine: true
|
||||||
|
AllowShortIfStatementsOnASingleLine: true
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,7 @@ example/hello
|
|||||||
example/simplesvr
|
example/simplesvr
|
||||||
example/benchmark
|
example/benchmark
|
||||||
example/redirect
|
example/redirect
|
||||||
|
example/upload
|
||||||
example/*.pem
|
example/*.pem
|
||||||
test/test
|
test/test
|
||||||
test/test.xcodeproj/xcuser*
|
test/test.xcodeproj/xcuser*
|
||||||
|
|||||||
26
README.md
26
README.md
@@ -387,11 +387,19 @@ httplib::Client cli("yahoo.com");
|
|||||||
auto res = cli.Get("/");
|
auto res = cli.Get("/");
|
||||||
res->status; // 301
|
res->status; // 301
|
||||||
|
|
||||||
cli.follow_location(true);
|
cli.set_follow_location(true);
|
||||||
res = cli.Get("/");
|
res = cli.Get("/");
|
||||||
res->status; // 200
|
res->status; // 200
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Use a specitic network interface
|
||||||
|
|
||||||
|
NOTE: This feature is not available on Windows, yet.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
cli.set_interface("eth0"); // Interface name, IP address or host name
|
||||||
|
```
|
||||||
|
|
||||||
OpenSSL Support
|
OpenSSL Support
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
@@ -423,6 +431,22 @@ The server applies gzip compression to the following MIME type contents:
|
|||||||
* application/xml
|
* application/xml
|
||||||
* application/xhtml+xml
|
* application/xhtml+xml
|
||||||
|
|
||||||
|
### Compress content on client
|
||||||
|
|
||||||
|
```c++
|
||||||
|
cli.set_compress(true);
|
||||||
|
res = cli.Post("/resource/foo", "...", "text/plain");
|
||||||
|
```
|
||||||
|
|
||||||
|
Split httplib.h into .h and .cc
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
```bash
|
||||||
|
> python3 split.py
|
||||||
|
> ls out
|
||||||
|
httplib.h httplib.cc
|
||||||
|
```
|
||||||
|
|
||||||
NOTE
|
NOTE
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|||||||
@@ -44,14 +44,10 @@ int main(void) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Run servers
|
// Run servers
|
||||||
auto httpThread = std::thread([&]() {
|
auto httpThread = std::thread([&]() { http.listen("localhost", 8080); });
|
||||||
http.listen("localhost", 8080);
|
|
||||||
});
|
|
||||||
|
|
||||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
auto httpsThread = std::thread([&]() {
|
auto httpsThread = std::thread([&]() { https.listen("localhost", 8081); });
|
||||||
https.listen("localhost", 8081);
|
|
||||||
});
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
httpThread.join();
|
httpThread.join();
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ string dump_headers(const Headers &headers) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
string dump_multipart_files(const MultipartFiles &files) {
|
string dump_multipart_files(const MultipartFormDataMap &files) {
|
||||||
string s;
|
string s;
|
||||||
char buf[BUFSIZ];
|
char buf[BUFSIZ];
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
// MIT License
|
// MIT License
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
#include <httplib.h>
|
#include <httplib.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
|
||||||
using namespace httplib;
|
using namespace httplib;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@@ -37,7 +37,8 @@ int main(void) {
|
|||||||
|
|
||||||
svr.Post("/post", [](const Request &req, Response &res) {
|
svr.Post("/post", [](const Request &req, Response &res) {
|
||||||
auto file = req.get_file_value("file");
|
auto file = req.get_file_value("file");
|
||||||
cout << "file length: " << file.content.length() << ":" << file.filename << endl;
|
cout << "file length: " << file.content.length() << ":" << file.filename
|
||||||
|
<< endl;
|
||||||
|
|
||||||
ofstream ofs(file.filename, ios::binary);
|
ofstream ofs(file.filename, ios::binary);
|
||||||
ofs << file.content;
|
ofs << file.content;
|
||||||
|
|||||||
375
httplib.h
375
httplib.h
@@ -114,6 +114,7 @@ using socket_t = SOCKET;
|
|||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <ifaddrs.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#ifdef CPPHTTPLIB_USE_POLL
|
#ifdef CPPHTTPLIB_USE_POLL
|
||||||
@@ -229,18 +230,18 @@ using MultipartContentHeader =
|
|||||||
class ContentReader {
|
class ContentReader {
|
||||||
public:
|
public:
|
||||||
using Reader = std::function<bool(ContentReceiver receiver)>;
|
using Reader = std::function<bool(ContentReceiver receiver)>;
|
||||||
using MultipartReader = std::function<bool(MultipartContentHeader header, ContentReceiver receiver)>;
|
using MultipartReader = std::function<bool(MultipartContentHeader header,
|
||||||
|
ContentReceiver receiver)>;
|
||||||
|
|
||||||
ContentReader(Reader reader, MultipartReader muitlpart_reader)
|
ContentReader(Reader reader, MultipartReader muitlpart_reader)
|
||||||
: reader_(reader), muitlpart_reader_(muitlpart_reader) {}
|
: reader_(reader), muitlpart_reader_(muitlpart_reader) {}
|
||||||
|
|
||||||
bool operator()(MultipartContentHeader header, ContentReceiver receiver) const {
|
bool operator()(MultipartContentHeader header,
|
||||||
|
ContentReceiver receiver) const {
|
||||||
return muitlpart_reader_(header, receiver);
|
return muitlpart_reader_(header, receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator()(ContentReceiver receiver) const {
|
bool operator()(ContentReceiver receiver) const { return reader_(receiver); }
|
||||||
return reader_(receiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
Reader reader_;
|
Reader reader_;
|
||||||
MultipartReader muitlpart_reader_;
|
MultipartReader muitlpart_reader_;
|
||||||
@@ -583,14 +584,12 @@ private:
|
|||||||
const std::string &content_type);
|
const std::string &content_type);
|
||||||
bool read_content(Stream &strm, bool last_connection, Request &req,
|
bool read_content(Stream &strm, bool last_connection, Request &req,
|
||||||
Response &res);
|
Response &res);
|
||||||
bool read_content_with_content_receiver(Stream &strm, bool last_connection,
|
bool read_content_with_content_receiver(
|
||||||
Request &req, Response &res,
|
Stream &strm, bool last_connection, Request &req, Response &res,
|
||||||
ContentReceiver receiver,
|
ContentReceiver receiver, MultipartContentHeader multipart_header,
|
||||||
MultipartContentHeader multipart_header,
|
|
||||||
ContentReceiver multipart_receiver);
|
ContentReceiver multipart_receiver);
|
||||||
bool read_content_core(Stream &strm, bool last_connection,
|
bool read_content_core(Stream &strm, bool last_connection, Request &req,
|
||||||
Request &req, Response &res,
|
Response &res, ContentReceiver receiver,
|
||||||
ContentReceiver receiver,
|
|
||||||
MultipartContentHeader mulitpart_header,
|
MultipartContentHeader mulitpart_header,
|
||||||
ContentReceiver multipart_receiver);
|
ContentReceiver multipart_receiver);
|
||||||
|
|
||||||
@@ -657,78 +656,63 @@ public:
|
|||||||
std::shared_ptr<Response> Head(const char *path, const Headers &headers);
|
std::shared_ptr<Response> Head(const char *path, const Headers &headers);
|
||||||
|
|
||||||
std::shared_ptr<Response> Post(const char *path, const std::string &body,
|
std::shared_ptr<Response> Post(const char *path, const std::string &body,
|
||||||
const char *content_type,
|
const char *content_type);
|
||||||
bool compress = false);
|
|
||||||
|
|
||||||
std::shared_ptr<Response> Post(const char *path, const Headers &headers,
|
std::shared_ptr<Response> Post(const char *path, const Headers &headers,
|
||||||
const std::string &body,
|
const std::string &body,
|
||||||
const char *content_type,
|
const char *content_type);
|
||||||
bool compress = false);
|
|
||||||
|
|
||||||
std::shared_ptr<Response> Post(const char *path, size_t content_length,
|
std::shared_ptr<Response> Post(const char *path, size_t content_length,
|
||||||
ContentProvider content_provider,
|
ContentProvider content_provider,
|
||||||
const char *content_type,
|
const char *content_type);
|
||||||
bool compress = false);
|
|
||||||
|
|
||||||
std::shared_ptr<Response> Post(const char *path, const Headers &headers,
|
std::shared_ptr<Response> Post(const char *path, const Headers &headers,
|
||||||
size_t content_length,
|
size_t content_length,
|
||||||
ContentProvider content_provider,
|
ContentProvider content_provider,
|
||||||
const char *content_type,
|
const char *content_type);
|
||||||
bool compress = false);
|
|
||||||
|
|
||||||
std::shared_ptr<Response> Post(const char *path, const Params ¶ms,
|
std::shared_ptr<Response> Post(const char *path, const Params ¶ms);
|
||||||
bool compress = false);
|
|
||||||
|
|
||||||
std::shared_ptr<Response> Post(const char *path, const Headers &headers,
|
std::shared_ptr<Response> Post(const char *path, const Headers &headers,
|
||||||
const Params ¶ms, bool compress = false);
|
const Params ¶ms);
|
||||||
|
|
||||||
std::shared_ptr<Response> Post(const char *path,
|
std::shared_ptr<Response> Post(const char *path,
|
||||||
const MultipartFormDataItems &items,
|
const MultipartFormDataItems &items);
|
||||||
bool compress = false);
|
|
||||||
|
|
||||||
std::shared_ptr<Response> Post(const char *path, const Headers &headers,
|
std::shared_ptr<Response> Post(const char *path, const Headers &headers,
|
||||||
const MultipartFormDataItems &items,
|
const MultipartFormDataItems &items);
|
||||||
bool compress = false);
|
|
||||||
|
|
||||||
std::shared_ptr<Response> Put(const char *path, const std::string &body,
|
std::shared_ptr<Response> Put(const char *path, const std::string &body,
|
||||||
const char *content_type,
|
const char *content_type);
|
||||||
bool compress = false);
|
|
||||||
|
|
||||||
std::shared_ptr<Response> Put(const char *path, const Headers &headers,
|
std::shared_ptr<Response> Put(const char *path, const Headers &headers,
|
||||||
const std::string &body,
|
const std::string &body,
|
||||||
const char *content_type,
|
const char *content_type);
|
||||||
bool compress = false);
|
|
||||||
|
|
||||||
std::shared_ptr<Response> Put(const char *path, size_t content_length,
|
std::shared_ptr<Response> Put(const char *path, size_t content_length,
|
||||||
ContentProvider content_provider,
|
ContentProvider content_provider,
|
||||||
const char *content_type,
|
const char *content_type);
|
||||||
bool compress = false);
|
|
||||||
|
|
||||||
std::shared_ptr<Response> Put(const char *path, const Headers &headers,
|
std::shared_ptr<Response> Put(const char *path, const Headers &headers,
|
||||||
size_t content_length,
|
size_t content_length,
|
||||||
ContentProvider content_provider,
|
ContentProvider content_provider,
|
||||||
const char *content_type,
|
const char *content_type);
|
||||||
bool compress = false);
|
|
||||||
|
|
||||||
std::shared_ptr<Response> Patch(const char *path, const std::string &body,
|
std::shared_ptr<Response> Patch(const char *path, const std::string &body,
|
||||||
const char *content_type,
|
const char *content_type);
|
||||||
bool compress = false);
|
|
||||||
|
|
||||||
std::shared_ptr<Response> Patch(const char *path, const Headers &headers,
|
std::shared_ptr<Response> Patch(const char *path, const Headers &headers,
|
||||||
const std::string &body,
|
const std::string &body,
|
||||||
const char *content_type,
|
const char *content_type);
|
||||||
bool compress = false);
|
|
||||||
|
|
||||||
std::shared_ptr<Response> Patch(const char *path, size_t content_length,
|
std::shared_ptr<Response> Patch(const char *path, size_t content_length,
|
||||||
ContentProvider content_provider,
|
ContentProvider content_provider,
|
||||||
const char *content_type,
|
const char *content_type);
|
||||||
bool compress = false);
|
|
||||||
|
|
||||||
std::shared_ptr<Response> Patch(const char *path, const Headers &headers,
|
std::shared_ptr<Response> Patch(const char *path, const Headers &headers,
|
||||||
size_t content_length,
|
size_t content_length,
|
||||||
ContentProvider content_provider,
|
ContentProvider content_provider,
|
||||||
const char *content_type,
|
const char *content_type);
|
||||||
bool compress = false);
|
|
||||||
|
|
||||||
std::shared_ptr<Response> Delete(const char *path);
|
std::shared_ptr<Response> Delete(const char *path);
|
||||||
|
|
||||||
@@ -754,10 +738,14 @@ public:
|
|||||||
|
|
||||||
void set_read_timeout(time_t sec, time_t usec);
|
void set_read_timeout(time_t sec, time_t usec);
|
||||||
|
|
||||||
void follow_location(bool on);
|
|
||||||
|
|
||||||
void set_auth(const char *username, const char *password);
|
void set_auth(const char *username, const char *password);
|
||||||
|
|
||||||
|
void set_follow_location(bool on);
|
||||||
|
|
||||||
|
void set_compress(bool on);
|
||||||
|
|
||||||
|
void set_interface(const char *intf);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool process_request(Stream &strm, const Request &req, Response &res,
|
bool process_request(Stream &strm, const Request &req, Response &res,
|
||||||
bool last_connection, bool &connection_close);
|
bool last_connection, bool &connection_close);
|
||||||
@@ -769,22 +757,22 @@ protected:
|
|||||||
size_t keep_alive_max_count_;
|
size_t keep_alive_max_count_;
|
||||||
time_t read_timeout_sec_;
|
time_t read_timeout_sec_;
|
||||||
time_t read_timeout_usec_;
|
time_t read_timeout_usec_;
|
||||||
size_t follow_location_;
|
bool follow_location_;
|
||||||
std::string username_;
|
std::string username_;
|
||||||
std::string password_;
|
std::string password_;
|
||||||
|
bool compress_;
|
||||||
|
std::string interface_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
socket_t create_client_socket() const;
|
socket_t create_client_socket() const;
|
||||||
bool read_response_line(Stream &strm, Response &res);
|
bool read_response_line(Stream &strm, Response &res);
|
||||||
void write_request(Stream &strm, const Request &req, bool last_connection);
|
bool write_request(Stream &strm, const Request &req, bool last_connection);
|
||||||
bool redirect(const Request &req, Response &res);
|
bool redirect(const Request &req, Response &res);
|
||||||
|
|
||||||
std::shared_ptr<Response>
|
std::shared_ptr<Response> send_with_content_provider(
|
||||||
send_with_content_provider(const char *method, const char *path,
|
const char *method, const char *path, const Headers &headers,
|
||||||
const Headers &headers, const std::string &body,
|
const std::string &body, size_t content_length,
|
||||||
size_t content_length,
|
ContentProvider content_provider, const char *content_type);
|
||||||
ContentProvider content_provider,
|
|
||||||
const char *content_type, bool compress);
|
|
||||||
|
|
||||||
virtual bool process_and_close_socket(
|
virtual bool process_and_close_socket(
|
||||||
socket_t sock, size_t request_count,
|
socket_t sock, size_t request_count,
|
||||||
@@ -1072,7 +1060,7 @@ inline void read_file(const std::string &path, std::string &out) {
|
|||||||
|
|
||||||
inline std::string file_extension(const std::string &path) {
|
inline std::string file_extension(const std::string &path) {
|
||||||
std::smatch m;
|
std::smatch m;
|
||||||
auto re = std::regex("\\.([a-zA-Z0-9]+)$");
|
static auto re = std::regex("\\.([a-zA-Z0-9]+)$");
|
||||||
if (std::regex_search(path, m, re)) { return m[1].str(); }
|
if (std::regex_search(path, m, re)) { return m[1].str(); }
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
@@ -1364,10 +1352,62 @@ inline bool is_connection_error() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline socket_t create_client_socket(
|
inline bool bind_ip_address(socket_t sock, const char *host) {
|
||||||
const char *host, int port, time_t timeout_sec) {
|
struct addrinfo hints;
|
||||||
|
struct addrinfo *result;
|
||||||
|
|
||||||
|
memset(&hints, 0, sizeof(struct addrinfo));
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
hints.ai_protocol = 0;
|
||||||
|
|
||||||
|
if (getaddrinfo(host, "0", &hints, &result)) { return false; }
|
||||||
|
|
||||||
|
bool ret = false;
|
||||||
|
for (auto rp = result; rp; rp = rp->ai_next) {
|
||||||
|
const auto &ai = *rp;
|
||||||
|
if (!::bind(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen))) {
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(result);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string if2ip(const std::string &ifn) {
|
||||||
|
#ifndef _WIN32
|
||||||
|
struct ifaddrs *ifap;
|
||||||
|
getifaddrs(&ifap);
|
||||||
|
for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
|
||||||
|
if (ifa->ifa_addr && ifn == ifa->ifa_name) {
|
||||||
|
if (ifa->ifa_addr->sa_family == AF_INET) {
|
||||||
|
auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);
|
||||||
|
char buf[INET_ADDRSTRLEN];
|
||||||
|
if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
|
||||||
|
freeifaddrs(ifap);
|
||||||
|
return std::string(buf, INET_ADDRSTRLEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
freeifaddrs(ifap);
|
||||||
|
#endif
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline socket_t create_client_socket(const char *host, int port,
|
||||||
|
time_t timeout_sec,
|
||||||
|
const std::string &intf) {
|
||||||
return create_socket(
|
return create_socket(
|
||||||
host, port, [=](socket_t sock, struct addrinfo &ai) -> bool {
|
host, port, [&](socket_t sock, struct addrinfo &ai) -> bool {
|
||||||
|
if (!intf.empty()) {
|
||||||
|
auto ip = if2ip(intf);
|
||||||
|
if (ip.empty()) { ip = intf; }
|
||||||
|
if (!bind_ip_address(sock, ip.c_str())) { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
set_nonblocking(sock, true);
|
set_nonblocking(sock, true);
|
||||||
|
|
||||||
auto ret = ::connect(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen));
|
auto ret = ::connect(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen));
|
||||||
@@ -1926,8 +1966,7 @@ inline bool parse_multipart_boundary(const std::string &content_type,
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline bool parse_range_header(const std::string &s, Ranges &ranges) {
|
inline bool parse_range_header(const std::string &s, Ranges &ranges) {
|
||||||
static auto re_first_range =
|
static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
|
||||||
std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
|
|
||||||
std::smatch m;
|
std::smatch m;
|
||||||
if (std::regex_match(s, m, re_first_range)) {
|
if (std::regex_match(s, m, re_first_range)) {
|
||||||
auto pos = m.position(1);
|
auto pos = m.position(1);
|
||||||
@@ -1965,9 +2004,7 @@ class MultipartFormDataParser {
|
|||||||
public:
|
public:
|
||||||
MultipartFormDataParser() {}
|
MultipartFormDataParser() {}
|
||||||
|
|
||||||
void set_boundary(const std::string &boundary) {
|
void set_boundary(const std::string &boundary) { boundary_ = boundary; }
|
||||||
boundary_ = boundary;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_valid() const { return is_valid_; }
|
bool is_valid() const { return is_valid_; }
|
||||||
|
|
||||||
@@ -2040,9 +2077,7 @@ public:
|
|||||||
{
|
{
|
||||||
auto pattern = crlf_ + dash_;
|
auto pattern = crlf_ + dash_;
|
||||||
auto pos = buf_.find(pattern);
|
auto pos = buf_.find(pattern);
|
||||||
if (pos == std::string::npos) {
|
if (pos == std::string::npos) { pos = buf_.size(); }
|
||||||
pos = buf_.size();
|
|
||||||
}
|
|
||||||
if (!content_callback(buf_.data(), pos)) {
|
if (!content_callback(buf_.data(), pos)) {
|
||||||
is_valid_ = false;
|
is_valid_ = false;
|
||||||
is_done_ = false;
|
is_done_ = false;
|
||||||
@@ -2297,11 +2332,11 @@ inline std::string message_digest(const std::string &s, Init init,
|
|||||||
size_t digest_length) {
|
size_t digest_length) {
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
unsigned char md[digest_length];
|
std::vector<unsigned char> md(digest_length, 0);
|
||||||
CTX ctx;
|
CTX ctx;
|
||||||
init(&ctx);
|
init(&ctx);
|
||||||
update(&ctx, s.data(), s.size());
|
update(&ctx, s.data(), s.size());
|
||||||
final(md, &ctx);
|
final(md.data(), &ctx);
|
||||||
|
|
||||||
stringstream ss;
|
stringstream ss;
|
||||||
for (auto c : md) {
|
for (auto c : md) {
|
||||||
@@ -2366,10 +2401,9 @@ make_basic_authentication_header(const std::string &username,
|
|||||||
|
|
||||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
inline std::pair<std::string, std::string> make_digest_authentication_header(
|
inline std::pair<std::string, std::string> make_digest_authentication_header(
|
||||||
const Request &req,
|
const Request &req, const std::map<std::string, std::string> &auth,
|
||||||
const std::map<std::string, std::string> &auth,
|
size_t cnonce_count, const std::string &cnonce, const std::string &username,
|
||||||
size_t cnonce_count, const std::string &cnonce,
|
const std::string &password) {
|
||||||
const std::string &username, const std::string &password) {
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
string nc;
|
string nc;
|
||||||
@@ -2397,9 +2431,7 @@ inline std::pair<std::string, std::string> make_digest_authentication_header(
|
|||||||
auto A1 = username + ":" + auth.at("realm") + ":" + password;
|
auto A1 = username + ":" + auth.at("realm") + ":" + password;
|
||||||
|
|
||||||
auto A2 = req.method + ":" + req.path;
|
auto A2 = req.method + ":" + req.path;
|
||||||
if (qop == "auth-int") {
|
if (qop == "auth-int") { A2 += ":" + H(req.body); }
|
||||||
A2 += ":" + H(req.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +
|
response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +
|
||||||
":" + qop + ":" + H(A2));
|
":" + qop + ":" + H(A2));
|
||||||
@@ -2407,15 +2439,16 @@ inline std::pair<std::string, std::string> make_digest_authentication_header(
|
|||||||
|
|
||||||
auto field = "Digest username=\"hello\", realm=\"" + auth.at("realm") +
|
auto field = "Digest username=\"hello\", realm=\"" + auth.at("realm") +
|
||||||
"\", nonce=\"" + auth.at("nonce") + "\", uri=\"" + req.path +
|
"\", nonce=\"" + auth.at("nonce") + "\", uri=\"" + req.path +
|
||||||
"\", algorithm=" + auth.at("algorithm") + ", qop=" + qop + ", nc=\"" +
|
"\", algorithm=" + auth.at("algorithm") + ", qop=" + qop +
|
||||||
nc + "\", cnonce=\"" + cnonce + "\", response=\"" + response +
|
", nc=\"" + nc + "\", cnonce=\"" + cnonce + "\", response=\"" +
|
||||||
"\"";
|
response + "\"";
|
||||||
|
|
||||||
return make_pair("Authorization", field);
|
return make_pair("Authorization", field);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
inline int parse_www_authenticate(const httplib::Response &res,
|
inline int
|
||||||
|
parse_www_authenticate(const httplib::Response &res,
|
||||||
std::map<std::string, std::string> &digest_auth) {
|
std::map<std::string, std::string> &digest_auth) {
|
||||||
if (res.has_header("WWW-Authenticate")) {
|
if (res.has_header("WWW-Authenticate")) {
|
||||||
static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
|
static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
|
||||||
@@ -2790,7 +2823,7 @@ inline void Server::stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline bool Server::parse_request_line(const char *s, Request &req) {
|
inline bool Server::parse_request_line(const char *s, Request &req) {
|
||||||
static std::regex re(
|
const static std::regex re(
|
||||||
"(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) "
|
"(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) "
|
||||||
"(([^?]+)(?:\\?(.*?))?) (HTTP/1\\.[01])\r\n");
|
"(([^?]+)(?:\\?(.*?))?) (HTTP/1\\.[01])\r\n");
|
||||||
|
|
||||||
@@ -2969,7 +3002,8 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
|
|||||||
inline bool Server::read_content(Stream &strm, bool last_connection,
|
inline bool Server::read_content(Stream &strm, bool last_connection,
|
||||||
Request &req, Response &res) {
|
Request &req, Response &res) {
|
||||||
MultipartFormDataMap::iterator cur;
|
MultipartFormDataMap::iterator cur;
|
||||||
auto ret = read_content_core(strm, last_connection, req, res,
|
auto ret = read_content_core(
|
||||||
|
strm, last_connection, req, res,
|
||||||
// Regular
|
// Regular
|
||||||
[&](const char *buf, size_t n) {
|
[&](const char *buf, size_t n) {
|
||||||
if (req.body.size() + n > req.body.max_size()) { return false; }
|
if (req.body.size() + n > req.body.max_size()) { return false; }
|
||||||
@@ -2986,8 +3020,7 @@ inline bool Server::read_content(Stream &strm, bool last_connection,
|
|||||||
if (content.size() + n > content.max_size()) { return false; }
|
if (content.size() + n > content.max_size()) { return false; }
|
||||||
content.append(buf, n);
|
content.append(buf, n);
|
||||||
return true;
|
return true;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const auto &content_type = req.get_header_value("Content-Type");
|
const auto &content_type = req.get_header_value("Content-Type");
|
||||||
if (!content_type.find("application/x-www-form-urlencoded")) {
|
if (!content_type.find("application/x-www-form-urlencoded")) {
|
||||||
@@ -2997,18 +3030,15 @@ inline bool Server::read_content(Stream &strm, bool last_connection,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool Server::read_content_with_content_receiver(
|
||||||
Server::read_content_with_content_receiver(Stream &strm, bool last_connection,
|
Stream &strm, bool last_connection, Request &req, Response &res,
|
||||||
Request &req, Response &res,
|
ContentReceiver receiver, MultipartContentHeader multipart_header,
|
||||||
ContentReceiver receiver,
|
|
||||||
MultipartContentHeader multipart_header,
|
|
||||||
ContentReceiver multipart_receiver) {
|
ContentReceiver multipart_receiver) {
|
||||||
return read_content_core(strm, last_connection, req, res,
|
return read_content_core(strm, last_connection, req, res, receiver,
|
||||||
receiver, multipart_header, multipart_receiver);
|
multipart_header, multipart_receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool Server::read_content_core(Stream &strm, bool last_connection,
|
||||||
Server::read_content_core(Stream &strm, bool last_connection,
|
|
||||||
Request &req, Response &res,
|
Request &req, Response &res,
|
||||||
ContentReceiver receiver,
|
ContentReceiver receiver,
|
||||||
MultipartContentHeader mulitpart_header,
|
MultipartContentHeader mulitpart_header,
|
||||||
@@ -3026,7 +3056,8 @@ Server::read_content_core(Stream &strm, bool last_connection,
|
|||||||
|
|
||||||
multipart_form_data_parser.set_boundary(boundary);
|
multipart_form_data_parser.set_boundary(boundary);
|
||||||
out = [&](const char *buf, size_t n) {
|
out = [&](const char *buf, size_t n) {
|
||||||
return multipart_form_data_parser.parse(buf, n, multipart_receiver, mulitpart_header);
|
return multipart_form_data_parser.parse(buf, n, multipart_receiver,
|
||||||
|
mulitpart_header);
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
out = receiver;
|
out = receiver;
|
||||||
@@ -3172,14 +3203,13 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm,
|
|||||||
{
|
{
|
||||||
ContentReader reader(
|
ContentReader reader(
|
||||||
[&](ContentReceiver receiver) {
|
[&](ContentReceiver receiver) {
|
||||||
return read_content_with_content_receiver(strm, last_connection, req, res,
|
return read_content_with_content_receiver(
|
||||||
receiver, nullptr, nullptr);
|
strm, last_connection, req, res, receiver, nullptr, nullptr);
|
||||||
},
|
},
|
||||||
[&](MultipartContentHeader header, ContentReceiver receiver) {
|
[&](MultipartContentHeader header, ContentReceiver receiver) {
|
||||||
return read_content_with_content_receiver(strm, last_connection, req, res,
|
return read_content_with_content_receiver(
|
||||||
nullptr, header, receiver);
|
strm, last_connection, req, res, nullptr, header, receiver);
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
if (req.method == "POST") {
|
if (req.method == "POST") {
|
||||||
if (dispatch_request_for_content_reader(
|
if (dispatch_request_for_content_reader(
|
||||||
@@ -3331,14 +3361,15 @@ inline Client::Client(const char *host, int port, time_t timeout_sec)
|
|||||||
keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT),
|
keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT),
|
||||||
read_timeout_sec_(CPPHTTPLIB_READ_TIMEOUT_SECOND),
|
read_timeout_sec_(CPPHTTPLIB_READ_TIMEOUT_SECOND),
|
||||||
read_timeout_usec_(CPPHTTPLIB_READ_TIMEOUT_USECOND),
|
read_timeout_usec_(CPPHTTPLIB_READ_TIMEOUT_USECOND),
|
||||||
follow_location_(false) {}
|
follow_location_(false), compress_(false) {}
|
||||||
|
|
||||||
inline Client::~Client() {}
|
inline Client::~Client() {}
|
||||||
|
|
||||||
inline bool Client::is_valid() const { return true; }
|
inline bool Client::is_valid() const { return true; }
|
||||||
|
|
||||||
inline socket_t Client::create_client_socket() const {
|
inline socket_t Client::create_client_socket() const {
|
||||||
return detail::create_client_socket(host_.c_str(), port_, timeout_sec_);
|
return detail::create_client_socket(host_.c_str(), port_, timeout_sec_,
|
||||||
|
interface_);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Client::read_response_line(Stream &strm, Response &res) {
|
inline bool Client::read_response_line(Stream &strm, Response &res) {
|
||||||
@@ -3456,46 +3487,51 @@ inline bool Client::redirect(const Request &req, Response &res) {
|
|||||||
auto location = res.get_header_value("location");
|
auto location = res.get_header_value("location");
|
||||||
if (location.empty()) { return false; }
|
if (location.empty()) { return false; }
|
||||||
|
|
||||||
std::regex re(
|
const static std::regex re(
|
||||||
R"(^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
|
R"(^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
|
||||||
|
|
||||||
auto scheme = is_ssl() ? "https" : "http";
|
|
||||||
|
|
||||||
std::smatch m;
|
std::smatch m;
|
||||||
if (regex_match(location, m, re)) {
|
if (!regex_match(location, m, re)) { return false; }
|
||||||
|
|
||||||
auto next_scheme = m[1].str();
|
auto next_scheme = m[1].str();
|
||||||
auto next_host = m[2].str();
|
auto next_host = m[2].str();
|
||||||
auto next_path = m[3].str();
|
auto next_path = m[3].str();
|
||||||
if (next_host.empty()) { next_host = host_; }
|
if (next_host.empty()) { next_host = host_; }
|
||||||
if (next_path.empty()) { next_path = "/"; }
|
if (next_path.empty()) { next_path = "/"; }
|
||||||
|
|
||||||
|
auto scheme = is_ssl() ? "https" : "http";
|
||||||
|
|
||||||
if (next_scheme == scheme && next_host == host_) {
|
if (next_scheme == scheme && next_host == host_) {
|
||||||
return detail::redirect(*this, req, res, next_path);
|
return detail::redirect(*this, req, res, next_path);
|
||||||
} else {
|
} else {
|
||||||
if (next_scheme == "https") {
|
if (next_scheme == "https") {
|
||||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
SSLClient cli(next_host.c_str());
|
SSLClient cli(next_host.c_str());
|
||||||
cli.follow_location(true);
|
cli.set_follow_location(true);
|
||||||
return detail::redirect(cli, req, res, next_path);
|
return detail::redirect(cli, req, res, next_path);
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
Client cli(next_host.c_str());
|
Client cli(next_host.c_str());
|
||||||
cli.follow_location(true);
|
cli.set_follow_location(true);
|
||||||
return detail::redirect(cli, req, res, next_path);
|
return detail::redirect(cli, req, res, next_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Client::write_request(Stream &strm, const Request &req,
|
inline bool Client::write_request(Stream &strm, const Request &req,
|
||||||
bool last_connection) {
|
bool last_connection) {
|
||||||
BufferStream bstrm;
|
BufferStream bstrm;
|
||||||
|
|
||||||
// Request line
|
// Request line
|
||||||
auto path = detail::encode_url(req.path);
|
const static std::regex re(
|
||||||
|
R"(^([^:/?#]+://[^/?#]*)?([^?#]*(?:\?[^#]*)?(?:#.*)?))");
|
||||||
|
|
||||||
|
std::smatch m;
|
||||||
|
if (!regex_match(req.path, m, re)) { return false; }
|
||||||
|
|
||||||
|
auto path = m[1].str() + detail::encode_url(m[2].str());
|
||||||
|
|
||||||
bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str());
|
bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str());
|
||||||
|
|
||||||
@@ -3565,16 +3601,14 @@ inline void Client::write_request(Stream &strm, const Request &req,
|
|||||||
} else {
|
} else {
|
||||||
strm.write(req.body);
|
strm.write(req.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<Response> Client::send_with_content_provider(
|
inline std::shared_ptr<Response> Client::send_with_content_provider(
|
||||||
const char *method, const char *path, const Headers &headers,
|
const char *method, const char *path, const Headers &headers,
|
||||||
const std::string &body, size_t content_length,
|
const std::string &body, size_t content_length,
|
||||||
ContentProvider content_provider, const char *content_type, bool compress) {
|
ContentProvider content_provider, const char *content_type) {
|
||||||
#ifndef CPPHTTPLIB_ZLIB_SUPPORT
|
|
||||||
(void)compress;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Request req;
|
Request req;
|
||||||
req.method = method;
|
req.method = method;
|
||||||
req.headers = headers;
|
req.headers = headers;
|
||||||
@@ -3583,7 +3617,7 @@ inline std::shared_ptr<Response> Client::send_with_content_provider(
|
|||||||
req.headers.emplace("Content-Type", content_type);
|
req.headers.emplace("Content-Type", content_type);
|
||||||
|
|
||||||
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
||||||
if (compress) {
|
if (compress_) {
|
||||||
if (content_provider) {
|
if (content_provider) {
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
while (offset < content_length) {
|
while (offset < content_length) {
|
||||||
@@ -3619,7 +3653,7 @@ inline bool Client::process_request(Stream &strm, const Request &req,
|
|||||||
Response &res, bool last_connection,
|
Response &res, bool last_connection,
|
||||||
bool &connection_close) {
|
bool &connection_close) {
|
||||||
// Send request
|
// Send request
|
||||||
write_request(strm, req, last_connection);
|
if (!write_request(strm, req, last_connection)) { return false; }
|
||||||
|
|
||||||
// Receive response and headers
|
// Receive response and headers
|
||||||
if (!read_response_line(strm, res) ||
|
if (!read_response_line(strm, res) ||
|
||||||
@@ -3771,45 +3805,40 @@ inline std::shared_ptr<Response> Client::Head(const char *path,
|
|||||||
|
|
||||||
inline std::shared_ptr<Response> Client::Post(const char *path,
|
inline std::shared_ptr<Response> Client::Post(const char *path,
|
||||||
const std::string &body,
|
const std::string &body,
|
||||||
const char *content_type,
|
const char *content_type) {
|
||||||
bool compress) {
|
return Post(path, Headers(), body, content_type);
|
||||||
return Post(path, Headers(), body, content_type, compress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<Response>
|
inline std::shared_ptr<Response> Client::Post(const char *path,
|
||||||
Client::Post(const char *path, const Headers &headers, const std::string &body,
|
const Headers &headers,
|
||||||
const char *content_type, bool compress) {
|
const std::string &body,
|
||||||
|
const char *content_type) {
|
||||||
return send_with_content_provider("POST", path, headers, body, 0, nullptr,
|
return send_with_content_provider("POST", path, headers, body, 0, nullptr,
|
||||||
content_type, compress);
|
content_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<Response>
|
inline std::shared_ptr<Response> Client::Post(const char *path,
|
||||||
Client::Post(const char *path, const Params ¶ms, bool compress) {
|
const Params ¶ms) {
|
||||||
return Post(path, Headers(), params, compress);
|
return Post(path, Headers(), params);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<Response> Client::Post(const char *path,
|
inline std::shared_ptr<Response> Client::Post(const char *path,
|
||||||
size_t content_length,
|
size_t content_length,
|
||||||
ContentProvider content_provider,
|
ContentProvider content_provider,
|
||||||
const char *content_type,
|
const char *content_type) {
|
||||||
bool compress) {
|
return Post(path, Headers(), content_length, content_provider, content_type);
|
||||||
return Post(path, Headers(), content_length, content_provider, content_type,
|
|
||||||
compress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<Response>
|
inline std::shared_ptr<Response>
|
||||||
Client::Post(const char *path, const Headers &headers, size_t content_length,
|
Client::Post(const char *path, const Headers &headers, size_t content_length,
|
||||||
ContentProvider content_provider, const char *content_type,
|
ContentProvider content_provider, const char *content_type) {
|
||||||
bool compress) {
|
|
||||||
return send_with_content_provider("POST", path, headers, std::string(),
|
return send_with_content_provider("POST", path, headers, std::string(),
|
||||||
content_length, content_provider,
|
content_length, content_provider,
|
||||||
content_type, compress);
|
content_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<Response> Client::Post(const char *path,
|
inline std::shared_ptr<Response>
|
||||||
const Headers &headers,
|
Client::Post(const char *path, const Headers &headers, const Params ¶ms) {
|
||||||
const Params ¶ms,
|
|
||||||
bool compress) {
|
|
||||||
std::string query;
|
std::string query;
|
||||||
for (auto it = params.begin(); it != params.end(); ++it) {
|
for (auto it = params.begin(); it != params.end(); ++it) {
|
||||||
if (it != params.begin()) { query += "&"; }
|
if (it != params.begin()) { query += "&"; }
|
||||||
@@ -3818,19 +3847,17 @@ inline std::shared_ptr<Response> Client::Post(const char *path,
|
|||||||
query += detail::encode_url(it->second);
|
query += detail::encode_url(it->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Post(path, headers, query, "application/x-www-form-urlencoded",
|
return Post(path, headers, query, "application/x-www-form-urlencoded");
|
||||||
compress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<Response>
|
inline std::shared_ptr<Response>
|
||||||
Client::Post(const char *path, const MultipartFormDataItems &items,
|
Client::Post(const char *path, const MultipartFormDataItems &items) {
|
||||||
bool compress) {
|
return Post(path, Headers(), items);
|
||||||
return Post(path, Headers(), items, compress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<Response>
|
inline std::shared_ptr<Response>
|
||||||
Client::Post(const char *path, const Headers &headers,
|
Client::Post(const char *path, const Headers &headers,
|
||||||
const MultipartFormDataItems &items, bool compress) {
|
const MultipartFormDataItems &items) {
|
||||||
auto boundary = detail::make_multipart_data_boundary();
|
auto boundary = detail::make_multipart_data_boundary();
|
||||||
|
|
||||||
std::string body;
|
std::string body;
|
||||||
@@ -3852,71 +3879,65 @@ Client::Post(const char *path, const Headers &headers,
|
|||||||
body += "--" + boundary + "--\r\n";
|
body += "--" + boundary + "--\r\n";
|
||||||
|
|
||||||
std::string content_type = "multipart/form-data; boundary=" + boundary;
|
std::string content_type = "multipart/form-data; boundary=" + boundary;
|
||||||
return Post(path, headers, body, content_type.c_str(), compress);
|
return Post(path, headers, body, content_type.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<Response> Client::Put(const char *path,
|
inline std::shared_ptr<Response> Client::Put(const char *path,
|
||||||
const std::string &body,
|
const std::string &body,
|
||||||
const char *content_type,
|
const char *content_type) {
|
||||||
bool compress) {
|
return Put(path, Headers(), body, content_type);
|
||||||
return Put(path, Headers(), body, content_type, compress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<Response>
|
inline std::shared_ptr<Response> Client::Put(const char *path,
|
||||||
Client::Put(const char *path, const Headers &headers, const std::string &body,
|
const Headers &headers,
|
||||||
const char *content_type, bool compress) {
|
const std::string &body,
|
||||||
|
const char *content_type) {
|
||||||
return send_with_content_provider("PUT", path, headers, body, 0, nullptr,
|
return send_with_content_provider("PUT", path, headers, body, 0, nullptr,
|
||||||
content_type, compress);
|
content_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<Response> Client::Put(const char *path,
|
inline std::shared_ptr<Response> Client::Put(const char *path,
|
||||||
size_t content_length,
|
size_t content_length,
|
||||||
ContentProvider content_provider,
|
ContentProvider content_provider,
|
||||||
const char *content_type,
|
const char *content_type) {
|
||||||
bool compress) {
|
return Put(path, Headers(), content_length, content_provider, content_type);
|
||||||
return Put(path, Headers(), content_length, content_provider, content_type,
|
|
||||||
compress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<Response>
|
inline std::shared_ptr<Response>
|
||||||
Client::Put(const char *path, const Headers &headers, size_t content_length,
|
Client::Put(const char *path, const Headers &headers, size_t content_length,
|
||||||
ContentProvider content_provider, const char *content_type,
|
ContentProvider content_provider, const char *content_type) {
|
||||||
bool compress) {
|
|
||||||
return send_with_content_provider("PUT", path, headers, std::string(),
|
return send_with_content_provider("PUT", path, headers, std::string(),
|
||||||
content_length, content_provider,
|
content_length, content_provider,
|
||||||
content_type, compress);
|
content_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<Response> Client::Patch(const char *path,
|
inline std::shared_ptr<Response> Client::Patch(const char *path,
|
||||||
const std::string &body,
|
const std::string &body,
|
||||||
const char *content_type,
|
const char *content_type) {
|
||||||
bool compress) {
|
return Patch(path, Headers(), body, content_type);
|
||||||
return Patch(path, Headers(), body, content_type, compress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<Response>
|
inline std::shared_ptr<Response> Client::Patch(const char *path,
|
||||||
Client::Patch(const char *path, const Headers &headers, const std::string &body,
|
const Headers &headers,
|
||||||
const char *content_type, bool compress) {
|
const std::string &body,
|
||||||
|
const char *content_type) {
|
||||||
return send_with_content_provider("PATCH", path, headers, body, 0, nullptr,
|
return send_with_content_provider("PATCH", path, headers, body, 0, nullptr,
|
||||||
content_type, compress);
|
content_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<Response> Client::Patch(const char *path,
|
inline std::shared_ptr<Response> Client::Patch(const char *path,
|
||||||
size_t content_length,
|
size_t content_length,
|
||||||
ContentProvider content_provider,
|
ContentProvider content_provider,
|
||||||
const char *content_type,
|
const char *content_type) {
|
||||||
bool compress) {
|
return Patch(path, Headers(), content_length, content_provider, content_type);
|
||||||
return Patch(path, Headers(), content_length, content_provider, content_type,
|
|
||||||
compress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<Response>
|
inline std::shared_ptr<Response>
|
||||||
Client::Patch(const char *path, const Headers &headers, size_t content_length,
|
Client::Patch(const char *path, const Headers &headers, size_t content_length,
|
||||||
ContentProvider content_provider, const char *content_type,
|
ContentProvider content_provider, const char *content_type) {
|
||||||
bool compress) {
|
|
||||||
return send_with_content_provider("PATCH", path, headers, std::string(),
|
return send_with_content_provider("PATCH", path, headers, std::string(),
|
||||||
content_length, content_provider,
|
content_length, content_provider,
|
||||||
content_type, compress);
|
content_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<Response> Client::Delete(const char *path) {
|
inline std::shared_ptr<Response> Client::Delete(const char *path) {
|
||||||
@@ -3976,13 +3997,17 @@ inline void Client::set_read_timeout(time_t sec, time_t usec) {
|
|||||||
read_timeout_usec_ = usec;
|
read_timeout_usec_ = usec;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Client::follow_location(bool on) { follow_location_ = on; }
|
|
||||||
|
|
||||||
inline void Client::set_auth(const char *username, const char *password) {
|
inline void Client::set_auth(const char *username, const char *password) {
|
||||||
username_ = username;
|
username_ = username;
|
||||||
password_ = password;
|
password_ = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void Client::set_follow_location(bool on) { follow_location_ = on; }
|
||||||
|
|
||||||
|
inline void Client::set_compress(bool on) { compress_ = on; }
|
||||||
|
|
||||||
|
inline void Client::set_interface(const char *intf) { interface_ = intf; }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SSL Implementation
|
* SSL Implementation
|
||||||
*/
|
*/
|
||||||
|
|||||||
53
test/test.cc
53
test/test.cc
@@ -30,10 +30,11 @@ const std::string JSON_DATA = "{\"hello\":\"world\"}";
|
|||||||
|
|
||||||
const string LARGE_DATA = string(1024 * 1024 * 100, '@'); // 100MB
|
const string LARGE_DATA = string(1024 * 1024 * 100, '@'); // 100MB
|
||||||
|
|
||||||
MultipartFormData& get_file_value(MultipartFormDataItems &files, const char *key) {
|
MultipartFormData &get_file_value(MultipartFormDataItems &files,
|
||||||
auto it = std::find_if(files.begin(), files.end(), [&](const MultipartFormData &file) {
|
const char *key) {
|
||||||
return file.name == key;
|
auto it = std::find_if(
|
||||||
});
|
files.begin(), files.end(),
|
||||||
|
[&](const MultipartFormData &file) { return file.name == key; });
|
||||||
if (it != files.end()) { return *it; }
|
if (it != files.end()) { return *it; }
|
||||||
throw std::runtime_error("invalid mulitpart form data name error");
|
throw std::runtime_error("invalid mulitpart form data name error");
|
||||||
}
|
}
|
||||||
@@ -524,7 +525,7 @@ TEST(AbsoluteRedirectTest, Redirect) {
|
|||||||
httplib::Client cli(host);
|
httplib::Client cli(host);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cli.follow_location(true);
|
cli.set_follow_location(true);
|
||||||
auto res = cli.Get("/absolute-redirect/3");
|
auto res = cli.Get("/absolute-redirect/3");
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ(200, res->status);
|
EXPECT_EQ(200, res->status);
|
||||||
@@ -539,7 +540,7 @@ TEST(RedirectTest, Redirect) {
|
|||||||
httplib::Client cli(host);
|
httplib::Client cli(host);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cli.follow_location(true);
|
cli.set_follow_location(true);
|
||||||
auto res = cli.Get("/redirect/3");
|
auto res = cli.Get("/redirect/3");
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ(200, res->status);
|
EXPECT_EQ(200, res->status);
|
||||||
@@ -554,7 +555,7 @@ TEST(RelativeRedirectTest, Redirect) {
|
|||||||
httplib::Client cli(host);
|
httplib::Client cli(host);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cli.follow_location(true);
|
cli.set_follow_location(true);
|
||||||
auto res = cli.Get("/relative-redirect/3");
|
auto res = cli.Get("/relative-redirect/3");
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ(200, res->status);
|
EXPECT_EQ(200, res->status);
|
||||||
@@ -569,7 +570,7 @@ TEST(TooManyRedirectTest, Redirect) {
|
|||||||
httplib::Client cli(host);
|
httplib::Client cli(host);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cli.follow_location(true);
|
cli.set_follow_location(true);
|
||||||
auto res = cli.Get("/redirect/21");
|
auto res = cli.Get("/redirect/21");
|
||||||
ASSERT_TRUE(res == nullptr);
|
ASSERT_TRUE(res == nullptr);
|
||||||
}
|
}
|
||||||
@@ -582,7 +583,7 @@ TEST(YahooRedirectTest, Redirect) {
|
|||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ(301, res->status);
|
EXPECT_EQ(301, res->status);
|
||||||
|
|
||||||
cli.follow_location(true);
|
cli.set_follow_location(true);
|
||||||
res = cli.Get("/");
|
res = cli.Get("/");
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ(200, res->status);
|
EXPECT_EQ(200, res->status);
|
||||||
@@ -590,7 +591,7 @@ TEST(YahooRedirectTest, Redirect) {
|
|||||||
|
|
||||||
TEST(HttpsToHttpRedirectTest, Redirect) {
|
TEST(HttpsToHttpRedirectTest, Redirect) {
|
||||||
httplib::SSLClient cli("httpbin.org");
|
httplib::SSLClient cli("httpbin.org");
|
||||||
cli.follow_location(true);
|
cli.set_follow_location(true);
|
||||||
auto res =
|
auto res =
|
||||||
cli.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
|
cli.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
@@ -801,7 +802,8 @@ protected:
|
|||||||
EXPECT_EQ("5", req.get_header_value("Content-Length"));
|
EXPECT_EQ("5", req.get_header_value("Content-Length"));
|
||||||
})
|
})
|
||||||
.Post("/content_receiver",
|
.Post("/content_receiver",
|
||||||
[&](const Request & req, Response &res, const ContentReader &content_reader) {
|
[&](const Request &req, Response &res,
|
||||||
|
const ContentReader &content_reader) {
|
||||||
if (req.is_multipart_form_data()) {
|
if (req.is_multipart_form_data()) {
|
||||||
MultipartFormDataItems files;
|
MultipartFormDataItems files;
|
||||||
content_reader(
|
content_reader(
|
||||||
@@ -1535,12 +1537,13 @@ TEST_F(ServerTest, PutWithContentProvider) {
|
|||||||
|
|
||||||
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
||||||
TEST_F(ServerTest, PutWithContentProviderWithGzip) {
|
TEST_F(ServerTest, PutWithContentProviderWithGzip) {
|
||||||
|
cli_.set_compress(true);
|
||||||
auto res = cli_.Put(
|
auto res = cli_.Put(
|
||||||
"/put", 3,
|
"/put", 3,
|
||||||
[](size_t /*offset*/, size_t /*length*/, DataSink sink) {
|
[](size_t /*offset*/, size_t /*length*/, DataSink sink) {
|
||||||
sink("PUT", 3);
|
sink("PUT", 3);
|
||||||
},
|
},
|
||||||
"text/plain", true);
|
"text/plain");
|
||||||
|
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ(200, res->status);
|
EXPECT_EQ(200, res->status);
|
||||||
@@ -1548,7 +1551,8 @@ TEST_F(ServerTest, PutWithContentProviderWithGzip) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, PutLargeFileWithGzip) {
|
TEST_F(ServerTest, PutLargeFileWithGzip) {
|
||||||
auto res = cli_.Put("/put-large", LARGE_DATA, "text/plain", true);
|
cli_.set_compress(true);
|
||||||
|
auto res = cli_.Put("/put-large", LARGE_DATA, "text/plain");
|
||||||
|
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ(200, res->status);
|
EXPECT_EQ(200, res->status);
|
||||||
@@ -1621,7 +1625,8 @@ TEST_F(ServerTest, PostMulitpartFilsContentReceiver) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, PostContentReceiverGzip) {
|
TEST_F(ServerTest, PostContentReceiverGzip) {
|
||||||
auto res = cli_.Post("/content_receiver", "content", "text/plain", true);
|
cli_.set_compress(true);
|
||||||
|
auto res = cli_.Post("/content_receiver", "content", "text/plain");
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
ASSERT_EQ(200, res->status);
|
ASSERT_EQ(200, res->status);
|
||||||
ASSERT_EQ("content", res->body);
|
ASSERT_EQ("content", res->body);
|
||||||
@@ -1802,7 +1807,8 @@ TEST_F(ServerTest, MultipartFormDataGzip) {
|
|||||||
{"key2", "--abcdefg123", "", ""},
|
{"key2", "--abcdefg123", "", ""},
|
||||||
};
|
};
|
||||||
|
|
||||||
auto res = cli_.Post("/gzipmultipart", items, true);
|
cli_.set_compress(true);
|
||||||
|
auto res = cli_.Post("/gzipmultipart", items);
|
||||||
|
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ(200, res->status);
|
EXPECT_EQ(200, res->status);
|
||||||
@@ -1811,15 +1817,15 @@ TEST_F(ServerTest, MultipartFormDataGzip) {
|
|||||||
|
|
||||||
// Sends a raw request to a server listening at HOST:PORT.
|
// 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) {
|
||||||
auto client_sock =
|
auto client_sock = detail::create_client_socket(HOST, PORT, /*timeout_sec=*/5,
|
||||||
detail::create_client_socket(HOST, PORT, /*timeout_sec=*/5);
|
std::string());
|
||||||
|
|
||||||
if (client_sock == INVALID_SOCKET) { return false; }
|
if (client_sock == INVALID_SOCKET) { return false; }
|
||||||
|
|
||||||
return detail::process_and_close_socket(
|
return detail::process_and_close_socket(
|
||||||
true, client_sock, 1, read_timeout_sec, 0,
|
true, client_sock, 1, read_timeout_sec, 0,
|
||||||
[&](Stream& strm, bool /*last_connection*/,
|
[&](Stream &strm, bool /*last_connection*/, bool &
|
||||||
bool &/*connection_close*/) -> bool {
|
/*connection_close*/) -> bool {
|
||||||
if (req.size() !=
|
if (req.size() !=
|
||||||
static_cast<size_t>(strm.write(req.data(), req.size()))) {
|
static_cast<size_t>(strm.write(req.data(), req.size()))) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1836,8 +1842,7 @@ static bool send_request(time_t read_timeout_sec, const std::string& req) {
|
|||||||
TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
|
TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
|
||||||
Server svr;
|
Server svr;
|
||||||
std::string header_value;
|
std::string header_value;
|
||||||
svr.Get("/validate-ws-in-headers",
|
svr.Get("/validate-ws-in-headers", [&](const Request &req, Response &res) {
|
||||||
[&](const Request &req, Response &res) {
|
|
||||||
header_value = req.get_header_value("foo");
|
header_value = req.get_header_value("foo");
|
||||||
res.set_content("ok", "text/plain");
|
res.set_content("ok", "text/plain");
|
||||||
});
|
});
|
||||||
@@ -1849,8 +1854,7 @@ TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
|
|||||||
|
|
||||||
// Only space and horizontal tab are whitespace. Make sure other whitespace-
|
// Only space and horizontal tab are whitespace. Make sure other whitespace-
|
||||||
// like characters are not treated the same - use vertical tab and escape.
|
// like characters are not treated the same - use vertical tab and escape.
|
||||||
const std::string req =
|
const std::string req = "GET /validate-ws-in-headers HTTP/1.1\r\n"
|
||||||
"GET /validate-ws-in-headers HTTP/1.1\r\n"
|
|
||||||
"foo: \t \v bar \e\t \r\n"
|
"foo: \t \v bar \e\t \r\n"
|
||||||
"Connection: close\r\n"
|
"Connection: close\r\n"
|
||||||
"\r\n";
|
"\r\n";
|
||||||
@@ -1863,8 +1867,7 @@ TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
|
|||||||
|
|
||||||
TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity) {
|
TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity) {
|
||||||
Server svr;
|
Server svr;
|
||||||
svr.Get("/hi",
|
svr.Get("/hi", [&](const Request & /*req*/, Response &res) {
|
||||||
[&](const Request & /*req*/, Response &res) {
|
|
||||||
res.set_content("ok", "text/plain");
|
res.set_content("ok", "text/plain");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user