Compare commits

..

44 Commits

Author SHA1 Message Date
yhirose e64379c3d7 Release v0.18.0 2024-09-12 12:28:35 -04:00
yhirose 5053912534 Updated actions/upload-artifact from v1 to v4 2024-09-12 12:04:25 -04:00
Jean-Francois Simoneau 932b1cbc32 Fix shadow parameter warning (#1936) 2024-09-12 12:02:25 -04:00
yhirose de36ea7755 Fix #1933 on Linux and macOS 2024-09-09 23:07:27 -04:00
yhirose 9f8db2c230 Fix #1933 2024-09-09 22:22:56 -04:00
yhirose 3f00e1b321 Revert "Changed set_file_content to accept only a regular file path."
This reverts commit 7ab9c119ef.
2024-09-09 20:03:47 -04:00
yhirose 7ab9c119ef Changed set_file_content to accept only a regular file path. 2024-09-09 19:59:18 -04:00
yhirose 3f2922b3fa Fix #1929 2024-09-09 19:25:52 -04:00
yhirose 509f583dca Fix problem caused by #1931. 2024-09-09 19:19:52 -04:00
Paul Harris 2d01e71286 Test reading empty zero-length file (#1931) 2024-09-09 17:54:11 -04:00
yhirose e612154694 Issue1431 (#1926)
* Renamed enable_server_host_verification to enable_server_hostname_verification and added Error::SSLServerHostnameVerification

* Add some Open SSL function calls

* Code cleanup

* Fix #1431
2024-09-08 22:45:47 -04:00
yhirose 82fcbe3901 Code cleanup 2024-09-08 20:10:35 -04:00
yhirose dbd2465b56 Add some Open SSL function calls 2024-09-08 20:10:17 -04:00
yhirose ea79494b29 Renamed enable_server_host_verification to enable_server_hostname_verification and added Error::SSLServerHostnameVerification 2024-09-08 20:08:52 -04:00
yhirose f35aff84c2 Fixed FuzzableServer build error 2024-09-08 19:00:23 -04:00
yhirose 7b18ae6f16 Update benchmark 2024-09-08 10:56:13 -04:00
yhirose a79c56d06b Fix #1796 2024-09-08 09:26:19 -04:00
yhirose 3d6e315a4c Fix #1923 2024-09-08 08:38:36 -04:00
yhirose 4c27f9c6ef Made default server and client read/write timeout settings separately 2024-09-07 21:06:23 -04:00
yhirose d173a37d17 Increased CPPHTTPLIB_READ_TIMEOUT_SECOND to 300 from 5 2024-09-07 16:10:54 -04:00
yhirose 7fd346a2ca Fix #1379 2024-09-07 16:07:45 -04:00
yhirose c673d502b9 Update server_and_client.cc 2024-09-07 11:11:57 -04:00
yhirose c43c51362a Add monitor tool 2024-09-07 10:16:15 -04:00
yhirose 3e86d93d13 clangformat 2024-09-07 10:16:03 -04:00
yhirose f6e4e2d0f3 Code cleanup 2024-09-07 10:15:22 -04:00
yhirose 01a52aa8bd Add example/server_and_client.cc 2024-09-07 10:05:53 -04:00
yhirose 8415bf0823 Resolve #1906 2024-09-07 00:05:41 -04:00
orbea 327ff263f5 httplib.h: support LibreSSL (#1922) 2024-09-06 22:19:53 -04:00
yhirose 61c418048d Release v0.17.3 2024-09-06 19:58:02 -04:00
yhirose 9720ef8c34 Code cleanup 2024-09-06 19:48:25 -04:00
yhirose 978a4f6345 Fix KeepAliveTest.SSLClientReconnectionPost problem (#1921) 2024-09-06 13:58:24 -04:00
bgs99 80fb03628b Only match path params that span full path segment (#1919)
* Only match path params that span full path segment

* Fix C++11 build
2024-09-06 08:48:51 -04:00
laowai9189 2480c0342c ‘constexpr’ error (#1918)
httplib.h: In member function ‘constexpr size_t httplib::detail::case_ignore_hash::operator()(const string&) const’:
httplib.h:359:30: error: call to non-‘constexpr’ function ‘const _CharT* std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::data() const [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’
  359 |     return hash_core(key.data(), key.size(), 0);
2024-09-06 07:23:29 -04:00
bgs99 eb6f610a45 Fix find_package for curl (#1920) 2024-09-06 07:22:03 -04:00
yhirose cb74e4191b Performance imporovement for Keep-Alive 2024-09-06 00:03:43 -04:00
yhirose dfa641ca41 Misc 2024-09-05 22:54:48 -04:00
yhirose 969a9f99d5 Adjust sleep 2024-09-05 22:54:28 -04:00
yhirose c099b42ba3 Removed write_format 2024-09-05 22:17:56 -04:00
yhirose b8315278cb Add a missing file 2024-09-05 19:35:43 -04:00
yhirose 485f8f2411 Added one more case to MountTest.Redicect unit test. 2024-09-05 17:49:12 -04:00
yhirose 953e4f3841 Adjust sleep duration 2024-09-05 17:45:09 -04:00
yhirose adf65cfe61 Target C++11 for benchmark 2024-09-05 17:44:51 -04:00
yhirose 12c829f6d3 Fix #1389 and #1907 2024-09-05 17:44:32 -04:00
yhirose 913314f1b1 Fix warning 2024-09-05 17:43:51 -04:00
16 changed files with 10580 additions and 246 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
dry-run: false
language: c++
- name: Upload Crash
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v4
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts
+4 -2
View File
@@ -9,6 +9,8 @@ example/benchmark
example/redirect
example/sse*
example/upload
example/one_time_request
example/server_and_client
example/*.pem
test/httplib.cc
test/httplib.h
@@ -21,8 +23,8 @@ test/test.xcodeproj/*/xcuser*
test/*.o
test/*.pem
test/*.srl
benchmark/server
benchmark/server-crow
work/
benchmark/server*
*.swp
+15
View File
@@ -77,6 +77,9 @@ cli.set_ca_cert_path("./ca-bundle.crt");
// Disable cert verification
cli.enable_server_certificate_verification(false);
// Disable host verification
cli.enable_server_host_verification(false);
```
> [!NOTE]
@@ -384,6 +387,18 @@ svr.Get("/chunked", [&](const Request& req, Response& res) {
});
```
### Send file content
```cpp
svr.Get("/content", [&](const Request &req, Response &res) {
res.set_file_content("./path/to/conent.html");
});
svr.Get("/content", [&](const Request &req, Response &res) {
res.set_file_content("./path/to/conent", "text/html");
});
```
### 'Expect: 100-continue' handler
By default, the server sends a `100 Continue` response for an `Expect: 100-continue` header.
+42 -13
View File
@@ -1,31 +1,60 @@
CXXFLAGS = -std=c++14 -O2 -I..
CXXFLAGS = -std=c++11 -O2 -I..
THEAD_POOL_COUNT = 16
BENCH_FLAGS = -c 8 -d 5s
BENCH = bombardier -c 10 -d 5s localhost:8080
MONITOR = ali http://localhost:8080
# cpp-httplib
bench: server
@./server & export PID=$$!; bombardier $(BENCH_FLAGS) localhost:8080; kill $${PID}
@echo "--------------------\n cpp-httplib latest\n--------------------\n"
@./server & export PID=$$!; $(BENCH); kill $${PID}
@echo ""
server : cpp-httplib/main.cpp ../httplib.h
g++ -o $@ $(CXXFLAGS) -DCPPHTTPLIB_THREAD_POOL_COUNT=$(THEAD_POOL_COUNT) cpp-httplib/main.cpp
monitor: server
@./server & export PID=$$!; $(MONITOR); kill $${PID}
run : server
@./server
server : cpp-httplib/main.cpp ../httplib.h
g++ -o $@ $(CXXFLAGS) -DCPPHTTPLIB_THREAD_POOL_COUNT=$(THEAD_POOL_COUNT) cpp-httplib/main.cpp
# cpp-httplib
bench-base: server-base
@echo "---------------------\n cpp-httplib v0.17.0\n---------------------\n"
@./server-base & export PID=$$!; $(BENCH); kill $${PID}
@echo ""
monitor-base: server-base
@./server-base & export PID=$$!; $(MONITOR); kill $${PID}
run-base : server-base
@./server-base
server-base : cpp-httplib-base/main.cpp cpp-httplib-base/httplib.h
g++ -o $@ $(CXXFLAGS) -DCPPHTTPLIB_THREAD_POOL_COUNT=$(THEAD_POOL_COUNT) cpp-httplib-base/main.cpp
# crow
bench-crow: server-crow
@echo "-------------\n Crow v1.2.0\n-------------\n"
@./server-crow & export PID=$$!; $(BENCH); kill $${PID}
@echo ""
monitor-crow: server-crow
@./server-crow & export PID=$$!; $(MONITOR); kill $${PID}
run-crow : server-crow
@./server-crow
server-crow : crow/main.cpp
g++ -o $@ $(CXXFLAGS) crow/main.cpp
bench-crow: server-crow
@./server-crow & export PID=$$!; bombardier $(BENCH_FLAGS) localhost:8080; kill $${PID}
# flask
bench-flask:
@FLASK_APP=flask/main.py flask run --port=8080 & export PID=$$!; bombardier $(BENCH_FLAGS) localhost:8080; kill $${PID}
# misc
bench-all: bench bench-crow bench-flask
bench-all: bench-crow bench bench-base
issue:
$(BENCH)
clean:
rm -rf server*
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
#include "./httplib.h"
using namespace httplib;
int main() {
Server svr;
svr.Get("/", [](const Request &, Response &res) {
res.set_content("Hello World!", "text/plain");
});
svr.listen("0.0.0.0", 8080);
}
+2
View File
@@ -0,0 +1,2 @@
rm -f httplib.h
wget https://raw.githubusercontent.com/yhirose/cpp-httplib/v$1/httplib.h
-9
View File
@@ -1,9 +0,0 @@
from flask import Flask
app = Flask(__name__)
import logging
logging.getLogger('werkzeug').disabled = True
@app.route('/')
def hello_world():
return 'Hello, World!'
+4 -1
View File
@@ -53,9 +53,12 @@ benchmark : benchmark.cc ../httplib.h Makefile
one_time_request : one_time_request.cc ../httplib.h Makefile
$(CXX) -o one_time_request $(CXXFLAGS) one_time_request.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
server_and_client : server_and_client.cc ../httplib.h Makefile
$(CXX) -o server_and_client $(CXXFLAGS) server_and_client.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
pem:
openssl genrsa 2048 > key.pem
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
clean:
rm server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark one_time_request *.pem
rm server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark one_time_request server_and_client *.pem
+90
View File
@@ -0,0 +1,90 @@
//
// server_and_client.cc
//
// Copyright (c) 2024 Yuji Hirose. All rights reserved.
// MIT License
//
#include <httplib.h>
#include <iostream>
#include <string>
using namespace httplib;
std::string dump_headers(const Headers &headers) {
std::string s;
char buf[BUFSIZ];
for (auto it = headers.begin(); it != headers.end(); ++it) {
const auto &x = *it;
snprintf(buf, sizeof(buf), "%s: %s\n", x.first.c_str(), x.second.c_str());
s += buf;
}
return s;
}
void logger(const Request &req, const Response &res) {
std::string s;
char buf[BUFSIZ];
s += "================================\n";
snprintf(buf, sizeof(buf), "%s %s %s", req.method.c_str(),
req.version.c_str(), req.path.c_str());
s += buf;
std::string query;
for (auto it = req.params.begin(); it != req.params.end(); ++it) {
const auto &x = *it;
snprintf(buf, sizeof(buf), "%c%s=%s",
(it == req.params.begin()) ? '?' : '&', x.first.c_str(),
x.second.c_str());
query += buf;
}
snprintf(buf, sizeof(buf), "%s\n", query.c_str());
s += buf;
s += dump_headers(req.headers);
s += "--------------------------------\n";
snprintf(buf, sizeof(buf), "%d %s\n", res.status, res.version.c_str());
s += buf;
s += dump_headers(res.headers);
s += "\n";
if (!res.body.empty()) { s += res.body; }
s += "\n";
std::cout << s;
}
int main(void) {
// Server
Server svr;
svr.set_logger(logger);
svr.Post("/post", [&](const Request & /*req*/, Response &res) {
res.set_content("POST", "text/plain");
});
auto th = std::thread([&]() { svr.listen("localhost", 8080); });
auto se = detail::scope_exit([&] {
svr.stop();
th.join();
});
svr.wait_until_ready();
// Client
Client cli{"localhost", 8080};
std::string body = R"({"hello": "world"})";
auto res = cli.Post("/post", body, "application/json");
std::cout << "--------------------------------" << std::endl;
std::cout << to_string(res.error()) << std::endl;
}
+333 -202
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -24,7 +24,7 @@ else()
FetchContent_MakeAvailable(gtest)
endif()
find_package(curl REQUIRED)
find_package(CURL REQUIRED)
add_executable(httplib-test test.cc)
target_compile_options(httplib-test PRIVATE "$<$<CXX_COMPILER_ID:MSVC>:/utf-8;/bigobj>")
+6 -2
View File
@@ -50,8 +50,12 @@ class FuzzableServer : public httplib::Server {
public:
void ProcessFuzzedRequest(FuzzedStream &stream) {
bool connection_close = false;
process_request(stream, /*last_connection=*/false, connection_close,
nullptr);
process_request(stream,
/*remote_addr=*/"",
/*remote_port =*/0,
/*local_addr=*/"",
/*local_port =*/0,
/*last_connection=*/false, connection_close, nullptr);
}
};
+114 -15
View File
@@ -2300,6 +2300,18 @@ protected:
[&](const Request & /*req*/, Response &res) {
res.set_content("Hello World!", "text/plain");
})
.Get("/file_content",
[&](const Request & /*req*/, Response &res) {
res.set_file_content("./www/dir/test.html");
})
.Get("/file_content_with_content_type",
[&](const Request & /*req*/, Response &res) {
res.set_file_content("./www/file", "text/plain");
})
.Get("/invalid_file_content",
[&](const Request & /*req*/, Response &res) {
res.set_file_content("./www/dir/invalid_file_path");
})
.Get("/http_response_splitting",
[&](const Request & /*req*/, Response &res) {
res.set_header("a", "1\r\nSet-Cookie: a=1");
@@ -2904,6 +2916,39 @@ TEST_F(ServerTest, GetMethod200) {
EXPECT_EQ("Hello World!", res->body);
}
TEST_F(ServerTest, GetEmptyFile) {
auto res = cli_.Get("/empty_file");
ASSERT_TRUE(res);
EXPECT_EQ(StatusCode::OK_200, res->status);
EXPECT_EQ("application/octet-stream", res->get_header_value("Content-Type"));
EXPECT_EQ(0, std::stoi(res->get_header_value("Content-Length")));
EXPECT_EQ("", res->body);
}
TEST_F(ServerTest, GetFileContent) {
auto res = cli_.Get("/file_content");
ASSERT_TRUE(res);
EXPECT_EQ(StatusCode::OK_200, res->status);
EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
EXPECT_EQ(9, std::stoi(res->get_header_value("Content-Length")));
EXPECT_EQ("test.html", res->body);
}
TEST_F(ServerTest, GetFileContentWithContentType) {
auto res = cli_.Get("/file_content_with_content_type");
ASSERT_TRUE(res);
EXPECT_EQ(StatusCode::OK_200, res->status);
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
EXPECT_EQ(5, std::stoi(res->get_header_value("Content-Length")));
EXPECT_EQ("file\n", res->body);
}
TEST_F(ServerTest, GetInvalidFileContent) {
auto res = cli_.Get("/invalid_file_content");
ASSERT_TRUE(res);
EXPECT_EQ(StatusCode::NotFound_404, res->status);
}
TEST_F(ServerTest, GetMethod200withPercentEncoding) {
auto res = cli_.Get("/%68%69"); // auto res = cli_.Get("/hi");
ASSERT_TRUE(res);
@@ -4722,9 +4767,10 @@ static void test_raw_request(const std::string &req,
svr.Put("/put_hi", [&](const Request & /*req*/, Response &res) {
res.set_content("ok", "text/plain");
});
svr.Get("/header_field_value_check", [&](const Request &/*req*/, Response &res) {
res.set_content("ok", "text/plain");
});
svr.Get("/header_field_value_check",
[&](const Request & /*req*/, Response &res) {
res.set_content("ok", "text/plain");
});
// Server read timeout must be longer than the client read timeout for the
// bug to reproduce, probably to force the server to process a request
@@ -5068,6 +5114,43 @@ TEST(MountTest, Unmount) {
EXPECT_EQ(StatusCode::NotFound_404, res->status);
}
TEST(MountTest, Redicect) {
Server svr;
auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
auto se = detail::scope_exit([&] {
svr.stop();
listen_thread.join();
ASSERT_FALSE(svr.is_running());
});
svr.set_mount_point("/", "./www");
svr.wait_until_ready();
Client cli("localhost", PORT);
auto res = cli.Get("/dir/");
ASSERT_TRUE(res);
EXPECT_EQ(StatusCode::OK_200, res->status);
res = cli.Get("/dir");
ASSERT_TRUE(res);
EXPECT_EQ(StatusCode::MovedPermanently_301, res->status);
res = cli.Get("/file");
ASSERT_TRUE(res);
EXPECT_EQ(StatusCode::OK_200, res->status);
res = cli.Get("/file/");
ASSERT_TRUE(res);
EXPECT_EQ(StatusCode::NotFound_404, res->status);
cli.set_follow_location(true);
res = cli.Get("/dir");
ASSERT_TRUE(res);
EXPECT_EQ(StatusCode::OK_200, res->status);
}
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
TEST(ExceptionTest, ThrowExceptionInHandler) {
Server svr;
@@ -7562,6 +7645,18 @@ TEST(PathParamsTest, SequenceOfParams) {
EXPECT_EQ(request.path_params, expected_params);
}
TEST(PathParamsTest, SemicolonInTheMiddleIsNotAParam) {
const auto pattern = "/prefix:suffix";
detail::PathParamsMatcher matcher(pattern);
Request request;
request.path = "/prefix:suffix";
ASSERT_TRUE(matcher.match(request));
const std::unordered_map<std::string, std::string> expected_params = {};
EXPECT_EQ(request.path_params, expected_params);
}
TEST(UniversalClientImplTest, Ipv6LiteralAddress) {
// If ipv6 regex working, regex match codepath is taken.
// else port will default to 80 in Client impl
@@ -7581,17 +7676,19 @@ TEST(FileSystemTest, FileAndDirExistenceCheck) {
auto file_path = "./www/dir/index.html";
auto dir_path = "./www/dir";
EXPECT_TRUE(detail::is_file(file_path));
EXPECT_FALSE(detail::is_dir(file_path));
detail::FileStat stat_file(file_path);
EXPECT_TRUE(stat_file.is_file());
EXPECT_FALSE(stat_file.is_dir());
EXPECT_FALSE(detail::is_file(dir_path));
EXPECT_TRUE(detail::is_dir(dir_path));
detail::FileStat stat_dir(dir_path);
EXPECT_FALSE(stat_dir.is_file());
EXPECT_TRUE(stat_dir.is_dir());
}
TEST(DirtyDataRequestTest, HeadFieldValueContains_CR_LF_NUL) {
Server svr;
svr.Get("/test", [&](const Request &/*req*/, Response &res) {
svr.Get("/test", [&](const Request & /*req*/, Response &res) {
EXPECT_EQ(res.status, 400);
});
@@ -7617,11 +7714,12 @@ TEST(Expect100ContinueTest, ServerClosesConnection) {
Server svr;
svr.set_expect_100_continue_handler([](const Request &/*req*/, Response &res) {
res.status = StatusCode::Unauthorized_401;
res.set_content(reject, "text/plain");
return res.status;
});
svr.set_expect_100_continue_handler(
[](const Request & /*req*/, Response &res) {
res.status = StatusCode::Unauthorized_401;
res.set_content(reject, "text/plain");
return res.status;
});
svr.Post("/", [&](const Request & /*req*/, Response &res) {
res.set_content(accept, "text/plain");
});
@@ -7696,9 +7794,10 @@ TEST(Expect100ContinueTest, ServerClosesConnection) {
{
auto dl = curl_off_t{};
const auto res = curl_easy_getinfo(curl.get(), CURLINFO_SIZE_DOWNLOAD_T, &dl);
const auto res =
curl_easy_getinfo(curl.get(), CURLINFO_SIZE_DOWNLOAD_T, &dl);
ASSERT_EQ(res, CURLE_OK);
ASSERT_EQ(dl, sizeof reject - 1);
ASSERT_EQ(dl, (curl_off_t)sizeof reject - 1);
}
{
View File
+1
View File
@@ -0,0 +1 @@
file