Compare commits

..

7 Commits

Author SHA1 Message Date
yhirose d92c314466 Release v0.11.1 2022-08-02 19:44:25 -04:00
yhirose b747fb111d Updated README 2022-08-02 19:42:11 -04:00
yhirose 7e0a0b2d0c Updated README 2022-08-02 19:30:15 -04:00
Changbin Park 362d064afa UNIX domain socket support (#1346)
* Add support UNIX domain socket

* `set_address_family(AF_UNIX)` is required

* add unittest for UNIX domain socket

* add support UNIX domain socket with abstract address

Abstract address of AF_UNIX begins with null(0x00) which can't be
delivered via .c_str() method.

* add unittest for UNIX domain socket with abstract address

Co-authored-by: Changbin Park <changbin.park@ahnlab.com>
2022-08-01 06:57:25 -04:00
Ata Yardımcı 1bd88de2e5 Fix test build warning (#1344)
Co-authored-by: ata.yardimci <ata.yardimci@erstream.com>
2022-07-31 16:51:06 -04:00
Rockybilly 0b541ffebc Add get_socket_fd method to Client and ClientImpl, add according unit… (#1341)
* Add get_socket_fd method to Client and ClientImpl, add according unit test

* Change name get_socket_fd to get_socket

* Change name get_socket to socket

Co-authored-by: ata.yardimci <ata.yardimci@erstream.com>
2022-07-31 08:27:38 -04:00
yhirose 106be19c3e Issue 49512: cpp-httplib:server_fuzzer: Timeout in server_fuzzer 2022-07-30 23:27:29 -04:00
4 changed files with 173 additions and 45 deletions
+2 -2
View File
@@ -7,12 +7,12 @@ A C++11 single-file header-only cross platform HTTP/HTTPS library.
It's extremely easy to setup. Just include the **httplib.h** file in your code!
NOTE: This is a multi-threaded 'blocking' HTTP library. If you are looking for a 'non-blocking' library, this is not the one that you want.
NOTE: This library uses 'blocking' socket I/O. If you are looking for a library with 'non-blocking' socket I/O, this is not the one that you want.
Simple examples
---------------
#### Server
#### Server (Multi-threaded)
```c++
#define CPPHTTPLIB_OPENSSL_SUPPORT
+66 -43
View File
@@ -8,7 +8,7 @@
#ifndef CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_VERSION "0.11.0"
#define CPPHTTPLIB_VERSION "0.11.1"
/*
* Configuration
@@ -183,6 +183,7 @@ using socket_t = SOCKET;
#include <pthread.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
using socket_t = int;
@@ -992,6 +993,8 @@ public:
size_t is_socket_open() const;
socket_t socket() const;
void stop();
void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
@@ -1344,6 +1347,8 @@ public:
size_t is_socket_open() const;
socket_t socket() const;
void stop();
void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
@@ -2566,6 +2571,30 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
hints.ai_flags = socket_flags;
}
#ifndef _WIN32
if (hints.ai_family == AF_UNIX) {
const auto addrlen = host.length();
if (addrlen > sizeof(sockaddr_un::sun_path)) return INVALID_SOCKET;
auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
if (sock != INVALID_SOCKET) {
sockaddr_un addr;
addr.sun_family = AF_UNIX;
std::copy(host.begin(), host.end(), addr.sun_path);
hints.ai_addr = reinterpret_cast<sockaddr*>(&addr);
hints.ai_addrlen = static_cast<socklen_t>(
sizeof(addr) - sizeof(addr.sun_path) + addrlen);
if (!bind_or_connect(sock, hints)) {
close_socket(sock);
sock = INVALID_SOCKET;
}
}
return sock;
}
#endif
auto service = std::to_string(port);
if (getaddrinfo(node, service.c_str(), &hints, &result)) {
@@ -3797,7 +3826,11 @@ class MultipartFormDataParser {
public:
MultipartFormDataParser() = default;
void set_boundary(std::string &&boundary) { boundary_ = boundary; }
void set_boundary(std::string &&boundary) {
boundary_ = boundary;
dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
}
bool is_valid() const { return is_valid_; }
@@ -3809,19 +3842,15 @@ public:
R"~(^Content-Disposition:\s*form-data;\s*name="(.*?)"(?:;\s*filename="(.*?)")?(?:;\s*filename\*=\S+)?\s*$)~",
std::regex_constants::icase);
static const std::string dash_ = "--";
static const std::string crlf_ = "\r\n";
buf_append(buf, n);
while (buf_size() > 0) {
switch (state_) {
case 0: { // Initial boundary
auto pattern = dash_ + boundary_ + crlf_;
buf_erase(buf_find(pattern));
if (pattern.size() > buf_size()) { return true; }
if (!buf_start_with(pattern)) { return false; }
buf_erase(pattern.size());
buf_erase(buf_find(dash_boundary_crlf_));
if (dash_boundary_crlf_.size() > buf_size()) { return true; }
if (!buf_start_with(dash_boundary_crlf_)) { return false; }
buf_erase(dash_boundary_crlf_.size());
state_ = 1;
break;
}
@@ -3856,7 +3885,6 @@ public:
file_.filename = m[2];
}
}
buf_erase(pos + crlf_.size());
pos = buf_find(crlf_);
}
@@ -3864,40 +3892,25 @@ public:
break;
}
case 3: { // Body
{
auto pattern = crlf_ + dash_;
if (pattern.size() > buf_size()) { return true; }
auto pos = buf_find(pattern);
if (crlf_dash_boundary_.size() > buf_size()) { return true; }
auto pos = buf_find(crlf_dash_boundary_);
if (pos < buf_size()) {
if (!content_callback(buf_data(), pos)) {
is_valid_ = false;
return false;
}
buf_erase(pos);
}
{
auto pattern = crlf_ + dash_ + boundary_;
if (pattern.size() > buf_size()) { return true; }
auto pos = buf_find(pattern);
if (pos < buf_size()) {
if (!content_callback(buf_data(), pos)) {
buf_erase(pos + crlf_dash_boundary_.size());
state_ = 4;
} else {
auto len = buf_size() - crlf_dash_boundary_.size();
if (len > 0) {
if (!content_callback(buf_data(), len)) {
is_valid_ = false;
return false;
}
buf_erase(pos + pattern.size());
state_ = 4;
} else {
if (!content_callback(buf_data(), pattern.size())) {
is_valid_ = false;
return false;
}
buf_erase(pattern.size());
buf_erase(len);
}
return true;
}
break;
}
@@ -3907,10 +3920,9 @@ public:
buf_erase(crlf_.size());
state_ = 1;
} else {
auto pattern = dash_ + crlf_;
if (pattern.size() > buf_size()) { return true; }
if (buf_start_with(pattern)) {
buf_erase(pattern.size());
if (dash_crlf_.size() > buf_size()) { return true; }
if (buf_start_with(dash_crlf_)) {
buf_erase(dash_crlf_.size());
is_valid_ = true;
buf_erase(buf_size()); // Remove epilogue
} else {
@@ -3941,7 +3953,12 @@ private:
return true;
}
const std::string dash_ = "--";
const std::string crlf_ = "\r\n";
const std::string dash_crlf_ = "--\r\n";
std::string boundary_;
std::string dash_boundary_crlf_;
std::string crlf_dash_boundary_;
size_t state_ = 0;
bool is_valid_ = false;
@@ -6956,6 +6973,10 @@ inline size_t ClientImpl::is_socket_open() const {
return socket_.is_open();
}
inline socket_t ClientImpl::socket() const {
return socket_.sock;
}
inline void ClientImpl::stop() {
std::lock_guard<std::mutex> guard(socket_mutex_);
@@ -7862,12 +7883,12 @@ inline Client::Client(const std::string &scheme_host_port,
if (is_ssl) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
cli_ = detail::make_unique<SSLClient>(host.c_str(), port,
cli_ = detail::make_unique<SSLClient>(host, port,
client_cert_path, client_key_path);
is_ssl_ = is_ssl;
#endif
} else {
cli_ = detail::make_unique<ClientImpl>(host.c_str(), port,
cli_ = detail::make_unique<ClientImpl>(host, port,
client_cert_path, client_key_path);
}
} else {
@@ -8163,6 +8184,8 @@ inline Result Client::send(const Request &req) { return cli_->send(req); }
inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); }
inline socket_t Client::socket() const { return cli_->socket(); }
inline void Client::stop() { cli_->stop(); }
inline void
+105
View File
@@ -4750,6 +4750,43 @@ TEST(SendAPI, SimpleInterface_Online) {
EXPECT_EQ(301, res->status);
}
TEST(ClientImplMethods, GetSocketTest) {
httplib::Server svr;
svr.Get( "/", [&](const httplib::Request& /*req*/, httplib::Response& res) {
res.status = 200;
});
auto thread = std::thread([&]() { svr.listen("127.0.0.1", 3333); });
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
{
httplib::Client cli("http://127.0.0.1:3333");
cli.set_keep_alive(true);
// Use the behavior of cpp-httplib of opening the connection
// only when the first request happens. If that changes,
// this test would be obsolete.
EXPECT_EQ(cli.socket(), INVALID_SOCKET);
// This also implicitly tests the server. But other tests would fail much
// earlier than this one to be considered.
auto res = cli.Get("/");
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
ASSERT_TRUE(cli.socket() != INVALID_SOCKET);
}
svr.stop();
thread.join();
ASSERT_FALSE(svr.is_running());
}
// Disabled due to out-of-memory problem on GitHub Actions
#ifdef _WIN64
TEST(ServerLargeContentTest, DISABLED_SendLargeContent) {
@@ -5027,3 +5064,71 @@ TEST(MultipartFormDataTest, WithPreamble) {
#endif
#ifndef _WIN32
class UnixSocketTest : public ::testing::Test {
protected:
void TearDown() override {
std::remove(pathname_.c_str());
}
void client_GET(const std::string &addr) {
httplib::Client cli{addr};
cli.set_address_family(AF_UNIX);
ASSERT_TRUE(cli.is_valid());
const auto &result = cli.Get(pattern_);
ASSERT_TRUE(result) << "error: " << result.error();
const auto &resp = result.value();
EXPECT_EQ(resp.status, 200);
EXPECT_EQ(resp.body, content_);
}
const std::string pathname_ {"./httplib-server.sock"};
const std::string pattern_ {"/hi"};
const std::string content_ {"Hello World!"};
};
TEST_F(UnixSocketTest, pathname) {
httplib::Server svr;
svr.Get(pattern_, [&](const httplib::Request &, httplib::Response &res) {
res.set_content(content_, "text/plain");
});
std::thread t {[&] {
ASSERT_TRUE(svr.set_address_family(AF_UNIX).listen(pathname_, 80)); }};
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_TRUE(svr.is_running());
client_GET(pathname_);
svr.stop();
t.join();
}
#ifdef __linux__
TEST_F(UnixSocketTest, abstract) {
constexpr char svr_path[] {"\x00httplib-server.sock"};
const std::string abstract_addr {svr_path, sizeof(svr_path) - 1};
httplib::Server svr;
svr.Get(pattern_, [&](const httplib::Request &, httplib::Response &res) {
res.set_content(content_, "text/plain");
});
std::thread t {[&] {
ASSERT_TRUE(svr.set_address_family(AF_UNIX).listen(abstract_addr, 80)); }};
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_TRUE(svr.is_running());
client_GET(abstract_addr);
svr.stop();
t.join();
}
#endif
#endif // #ifndef _WIN32