Compare commits

...

6 Commits

Author SHA1 Message Date
yhirose fc9b223acc Updated copyright year 2021-06-11 14:45:35 -04:00
CncGpp ba824089d7 Fix code err code 401 when the password is empty in base_auth. (#958) 2021-06-11 14:39:33 -04:00
Andrea Pappacoda 1a2faf09e0 Add header-only Meson support (#955)
* Add header-only Meson support
This allows users to call `dependency('httplib')` and have the include
directory automatically configured

* Rename `httplib` to `cpp-httplib`
2021-06-05 16:45:00 -04:00
yhirose 5a43bb8149 Implemented #946 in a different way 2021-06-02 13:45:47 -04:00
yhirose 0104614656 Code refactoring 2021-06-02 08:11:31 -04:00
yhirose 77a77f6d2d Added set_default_headers on Server 2021-05-23 19:06:28 -04:00
5 changed files with 151 additions and 59 deletions
+2 -2
View File
@@ -266,7 +266,7 @@ svr.Get("/stream", [&](const Request &req, Response &res) {
sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
return true; // return 'false' if you want to cancel the process.
},
[data] { delete data; });
[data](bool success) { delete data; });
});
```
@@ -781,7 +781,7 @@ Note: Windows 8 or lower and Cygwin on Windows are not supported.
License
-------
MIT license (© 2020 Yuji Hirose)
MIT license (© 2021 Yuji Hirose)
Special Thanks To
-----------------
-7
View File
@@ -1,10 +1,3 @@
//
// sse.cc
//
// Copyright (c) 2020 Yuji Hirose. All rights reserved.
// MIT License
//
#include <atomic>
#include <chrono>
#include <condition_variable>
+39 -19
View File
@@ -1,7 +1,7 @@
//
// httplib.h
//
// Copyright (c) 2020 Yuji Hirose. All rights reserved.
// Copyright (c) 2021 Yuji Hirose. All rights reserved.
// MIT License
//
@@ -337,6 +337,8 @@ using ContentProvider =
using ContentProviderWithoutLength =
std::function<bool(size_t offset, DataSink &sink)>;
using ContentProviderResourceReleaser = std::function<void(bool success)>;
using ContentReceiverWithProgress =
std::function<bool(const char *data, size_t data_length, uint64_t offset,
uint64_t total_length)>;
@@ -446,15 +448,15 @@ struct Response {
void set_content_provider(
size_t length, const char *content_type, ContentProvider provider,
const std::function<void()> &resource_releaser = nullptr);
ContentProviderResourceReleaser resource_releaser = nullptr);
void set_content_provider(
const char *content_type, ContentProviderWithoutLength provider,
const std::function<void()> &resource_releaser = nullptr);
ContentProviderResourceReleaser resource_releaser = nullptr);
void set_chunked_content_provider(
const char *content_type, ContentProviderWithoutLength provider,
const std::function<void()> &resource_releaser = nullptr);
ContentProviderResourceReleaser resource_releaser = nullptr);
Response() = default;
Response(const Response &) = default;
@@ -463,15 +465,16 @@ struct Response {
Response &operator=(Response &&) = default;
~Response() {
if (content_provider_resource_releaser_) {
content_provider_resource_releaser_();
content_provider_resource_releaser_(content_provider_success_);
}
}
// private members...
size_t content_length_ = 0;
ContentProvider content_provider_;
std::function<void()> content_provider_resource_releaser_;
ContentProviderResourceReleaser content_provider_resource_releaser_;
bool is_chunked_content_provider_ = false;
bool content_provider_success_ = false;
};
class Stream {
@@ -667,6 +670,8 @@ public:
Server &set_tcp_nodelay(bool on);
Server &set_socket_options(SocketOptions socket_options);
Server &set_default_headers(Headers headers);
Server &set_keep_alive_max_count(size_t count);
Server &set_keep_alive_timeout(time_t sec);
@@ -786,6 +791,8 @@ private:
int address_family_ = AF_UNSPEC;
bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
SocketOptions socket_options_ = default_socket_options;
Headers default_headers_;
};
enum class Error {
@@ -4026,10 +4033,9 @@ inline void Response::set_content(const std::string &s,
set_content(s.data(), s.size(), content_type);
}
inline void
Response::set_content_provider(size_t in_length, const char *content_type,
ContentProvider provider,
const std::function<void()> &resource_releaser) {
inline void Response::set_content_provider(
size_t in_length, const char *content_type, ContentProvider provider,
ContentProviderResourceReleaser resource_releaser) {
assert(in_length > 0);
set_header("Content-Type", content_type);
content_length_ = in_length;
@@ -4038,10 +4044,9 @@ Response::set_content_provider(size_t in_length, const char *content_type,
is_chunked_content_provider_ = false;
}
inline void
Response::set_content_provider(const char *content_type,
ContentProviderWithoutLength provider,
const std::function<void()> &resource_releaser) {
inline void Response::set_content_provider(
const char *content_type, ContentProviderWithoutLength provider,
ContentProviderResourceReleaser resource_releaser) {
set_header("Content-Type", content_type);
content_length_ = 0;
content_provider_ = detail::ContentProviderAdapter(std::move(provider));
@@ -4051,7 +4056,7 @@ Response::set_content_provider(const char *content_type,
inline void Response::set_chunked_content_provider(
const char *content_type, ContentProviderWithoutLength provider,
const std::function<void()> &resource_releaser) {
ContentProviderResourceReleaser resource_releaser) {
set_header("Content-Type", content_type);
content_length_ = 0;
content_provider_ = detail::ContentProviderAdapter(std::move(provider));
@@ -4427,6 +4432,11 @@ inline Server &Server::set_socket_options(SocketOptions socket_options) {
return *this;
}
inline Server &Server::set_default_headers(Headers headers) {
default_headers_ = std::move(headers);
return *this;
}
inline Server &Server::set_keep_alive_max_count(size_t count) {
keep_alive_max_count_ = count;
return *this;
@@ -4605,8 +4615,11 @@ inline bool Server::write_response_core(Stream &strm, bool close_connection,
if (!res.body.empty()) {
if (!strm.write(res.body)) { ret = false; }
} else if (res.content_provider_) {
if (!write_content_with_provider(strm, req, res, boundary,
content_type)) {
if (write_content_with_provider(strm, req, res, boundary,
content_type)) {
res.content_provider_success_ = true;
} else {
res.content_provider_success_ = false;
ret = false;
}
}
@@ -5131,6 +5144,12 @@ Server::process_request(Stream &strm, bool close_connection,
res.version = "HTTP/1.1";
for (const auto &header : default_headers_) {
if (res.headers.find(header.first) == res.headers.end()) {
res.headers.insert(header);
}
}
#ifdef _WIN32
// TODO: Increase FD_SETSIZE statically (libzmq), dynamically (MySQL).
#else
@@ -5677,7 +5696,7 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
}
}
if (!basic_auth_password_.empty()) {
if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {
req.headers.insert(make_basic_authentication_header(
basic_auth_username_, basic_auth_password_, false));
}
@@ -7043,7 +7062,8 @@ inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
shutdown_ssl_impl(socket, shutdown_gracefully);
}
inline void SSLClient::shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully) {
inline void SSLClient::shutdown_ssl_impl(Socket &socket,
bool shutdown_gracefully) {
if (socket.sock == INVALID_SOCKET) {
assert(socket.ssl == nullptr);
return;
+7
View File
@@ -0,0 +1,7 @@
project('cpp-httplib', 'cpp', license: 'MIT')
cpp_httplib_dep = declare_dependency(include_directories: include_directories('.'))
if meson.version().version_compare('>=0.54.0')
meson.override_dependency('cpp-httplib', cpp_httplib_dep)
endif
+103 -31
View File
@@ -5,9 +5,9 @@
#include <atomic>
#include <chrono>
#include <future>
#include <sstream>
#include <stdexcept>
#include <thread>
#include <sstream>
#define SERVER_CERT_FILE "./cert.pem"
#define SERVER_CERT2_FILE "./cert2.pem"
@@ -437,26 +437,6 @@ TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver) {
EXPECT_EQ(out, body);
}
TEST(DefaultHeadersTest, FromHTTPBin) {
Client cli("httpbin.org");
cli.set_default_headers({make_range_header({{1, 10}})});
cli.set_connection_timeout(5);
{
auto res = cli.Get("/range/32");
ASSERT_TRUE(res);
EXPECT_EQ("bcdefghijk", res->body);
EXPECT_EQ(206, res->status);
}
{
auto res = cli.Get("/range/32");
ASSERT_TRUE(res);
EXPECT_EQ("bcdefghijk", res->body);
EXPECT_EQ(206, res->status);
}
}
TEST(RangeTest, FromHTTPBin) {
auto host = "httpbin.org";
@@ -968,7 +948,7 @@ TEST(RedirectFromPageWithContent, Redirect) {
TEST(PathUrlEncodeTest, PathUrlEncode) {
Server svr;
svr.Get("/foo", [](const Request & req, Response &res) {
svr.Get("/foo", [](const Request &req, Response &res) {
auto a = req.params.find("a");
if (a != req.params.end()) {
res.set_content((*a).second, "text/plain");
@@ -1398,7 +1378,10 @@ protected:
(*i)++;
return true;
},
[i] { delete i; });
[i](bool success) {
EXPECT_TRUE(success);
delete i;
});
})
.Get("/streamed",
[&](const Request & /*req*/, Response &res) {
@@ -1420,11 +1403,15 @@ protected:
const auto &d = *data;
auto out_len =
std::min(static_cast<size_t>(length), DATA_CHUNK_SIZE);
auto ret = sink.write(&d[static_cast<size_t>(offset)], out_len);
auto ret =
sink.write(&d[static_cast<size_t>(offset)], out_len);
EXPECT_TRUE(ret);
return true;
},
[data] { delete data; });
[data](bool success) {
EXPECT_TRUE(success);
delete data;
});
})
.Get("/streamed-cancel",
[&](const Request & /*req*/, Response &res) {
@@ -3199,12 +3186,11 @@ static bool send_request(time_t read_timeout_sec, const std::string &req,
std::string *resp = nullptr) {
auto error = Error::Success;
auto client_sock =
detail::create_client_socket(HOST, PORT, AF_UNSPEC, false, nullptr,
/*connection_timeout_sec=*/5, 0,
/*read_timeout_sec=*/5, 0,
/*write_timeout_sec=*/5, 0,
std::string(), error);
auto client_sock = detail::create_client_socket(
HOST, PORT, AF_UNSPEC, false, nullptr,
/*connection_timeout_sec=*/5, 0,
/*read_timeout_sec=*/5, 0,
/*write_timeout_sec=*/5, 0, std::string(), error);
if (client_sock == INVALID_SOCKET) { return false; }
@@ -3587,6 +3573,44 @@ TEST(KeepAliveTest, ReadTimeout) {
ASSERT_FALSE(svr.is_running());
}
TEST(ClientProblemDetectionTest, ContentProvider) {
Server svr;
size_t content_length = 1024 * 1024;
svr.Get("/hi", [&](const Request & /*req*/, Response &res) {
res.set_content_provider(
content_length, "text/plain",
[&](size_t offset, size_t length, DataSink &sink) {
auto out_len = std::min(length, static_cast<size_t>(1024));
std::string out(out_len, '@');
sink.write(out.data(), out_len);
return offset < 4096;
},
[](bool success) { ASSERT_FALSE(success); });
});
auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
Client cli("localhost", PORT);
auto res = cli.Get("/hi", [&](const char * /*data*/, size_t /*data_length*/) {
return false;
});
ASSERT_FALSE(res);
svr.stop();
listen_thread.join();
ASSERT_FALSE(svr.is_running());
}
TEST(ErrorHandlerWithContentProviderTest, ErrorHandler) {
Server svr;
@@ -3684,6 +3708,54 @@ TEST(GetWithParametersTest, GetWithParameters2) {
ASSERT_FALSE(svr.is_running());
}
TEST(ClientDefaultHeadersTest, DefaultHeaders) {
Client cli("httpbin.org");
cli.set_default_headers({make_range_header({{1, 10}})});
cli.set_connection_timeout(5);
{
auto res = cli.Get("/range/32");
ASSERT_TRUE(res);
EXPECT_EQ("bcdefghijk", res->body);
EXPECT_EQ(206, res->status);
}
{
auto res = cli.Get("/range/32");
ASSERT_TRUE(res);
EXPECT_EQ("bcdefghijk", res->body);
EXPECT_EQ(206, res->status);
}
}
TEST(ServerDefaultHeadersTest, DefaultHeaders) {
Server svr;
svr.set_default_headers({{"Hello", "World"}});
svr.Get("/", [&](const Request & /*req*/, Response &res) {
res.set_content("ok", "text/plain");
});
auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
std::this_thread::sleep_for(std::chrono::seconds(1));
Client cli("localhost", PORT);
auto res = cli.Get("/");
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
EXPECT_EQ("ok", res->body);
EXPECT_EQ("World", res->get_header_value("Hello"));
svr.stop();
listen_thread.join();
ASSERT_FALSE(svr.is_running());
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
TEST(KeepAliveTest, ReadTimeoutSSL) {
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);