mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f086bf5310 | |||
| 6613d7b7ad | |||
| 6adf130bf3 | |||
| b6b2eaf5bc | |||
| eb4b7c70a9 | |||
| 84661ea6ed |
@@ -87,6 +87,14 @@
|
||||
: 0))
|
||||
#endif
|
||||
|
||||
#ifndef CPPHTTPLIB_RECV_FLAGS
|
||||
#define CPPHTTPLIB_RECV_FLAGS 0
|
||||
#endif
|
||||
|
||||
#ifndef CPPHTTPLIB_SEND_FLAGS
|
||||
#define CPPHTTPLIB_SEND_FLAGS 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Headers
|
||||
*/
|
||||
@@ -430,7 +438,7 @@ struct Response {
|
||||
void set_redirect(const char *url, int status = 302);
|
||||
void set_redirect(const std::string &url, int status = 302);
|
||||
void set_content(const char *s, size_t n, const char *content_type);
|
||||
void set_content(std::string s, const char *content_type);
|
||||
void set_content(const std::string &s, const char *content_type);
|
||||
|
||||
void set_content_provider(
|
||||
size_t length, const char *content_type, ContentProvider provider,
|
||||
@@ -681,9 +689,10 @@ private:
|
||||
bool write_response(Stream &strm, bool close_connection, const Request &req,
|
||||
Response &res);
|
||||
bool write_response_with_content(Stream &strm, bool close_connection,
|
||||
const Request &req, Response &res,
|
||||
std::string &content_type,
|
||||
std::string &boundary);
|
||||
const Request &req, Response &res);
|
||||
bool write_response_core(Stream &strm, bool close_connection,
|
||||
const Request &req, Response &res,
|
||||
bool need_apply_ranges);
|
||||
bool write_content_with_provider(Stream &strm, const Request &req,
|
||||
Response &res, const std::string &boundary,
|
||||
const std::string &content_type);
|
||||
@@ -3642,12 +3651,15 @@ inline void Response::set_redirect(const std::string &url, int stat) {
|
||||
inline void Response::set_content(const char *s, size_t n,
|
||||
const char *content_type) {
|
||||
body.assign(s, n);
|
||||
|
||||
auto rng = headers.equal_range("Content-Type");
|
||||
headers.erase(rng.first, rng.second);
|
||||
set_header("Content-Type", content_type);
|
||||
}
|
||||
|
||||
inline void Response::set_content(std::string s, const char *content_type) {
|
||||
body = std::move(s);
|
||||
set_header("Content-Type", content_type);
|
||||
inline void Response::set_content(const std::string &s,
|
||||
const char *content_type) {
|
||||
set_content(s.data(), s.size(), content_type);
|
||||
}
|
||||
|
||||
inline void
|
||||
@@ -3755,9 +3767,10 @@ inline ssize_t SocketStream::read(char *ptr, size_t size) {
|
||||
if (size > static_cast<size_t>((std::numeric_limits<int>::max)())) {
|
||||
return -1;
|
||||
}
|
||||
return recv(sock_, ptr, static_cast<int>(size), 0);
|
||||
return recv(sock_, ptr, static_cast<int>(size), CPPHTTPLIB_RECV_FLAGS);
|
||||
#else
|
||||
return handle_EINTR([&]() { return recv(sock_, ptr, size, 0); });
|
||||
return handle_EINTR(
|
||||
[&]() { return recv(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS); });
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -3768,9 +3781,10 @@ inline ssize_t SocketStream::write(const char *ptr, size_t size) {
|
||||
if (size > static_cast<size_t>((std::numeric_limits<int>::max)())) {
|
||||
return -1;
|
||||
}
|
||||
return send(sock_, ptr, static_cast<int>(size), 0);
|
||||
return send(sock_, ptr, static_cast<int>(size), CPPHTTPLIB_SEND_FLAGS);
|
||||
#else
|
||||
return handle_EINTR([&]() { return send(sock_, ptr, size, 0); });
|
||||
return handle_EINTR(
|
||||
[&]() { return send(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS); });
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -4010,19 +4024,27 @@ inline bool Server::parse_request_line(const char *s, Request &req) {
|
||||
|
||||
inline bool Server::write_response(Stream &strm, bool close_connection,
|
||||
const Request &req, Response &res) {
|
||||
std::string content_type;
|
||||
std::string boundary;
|
||||
return write_response_with_content(strm, close_connection, req, res,
|
||||
content_type, boundary);
|
||||
return write_response_core(strm, close_connection, req, res, false);
|
||||
}
|
||||
|
||||
inline bool Server::write_response_with_content(
|
||||
Stream &strm, bool close_connection, const Request &req, Response &res,
|
||||
std::string &content_type, std::string &boundary) {
|
||||
inline bool Server::write_response_with_content(Stream &strm,
|
||||
bool close_connection,
|
||||
const Request &req,
|
||||
Response &res) {
|
||||
return write_response_core(strm, close_connection, req, res, true);
|
||||
}
|
||||
|
||||
inline bool Server::write_response_core(Stream &strm, bool close_connection,
|
||||
const Request &req, Response &res,
|
||||
bool need_apply_ranges) {
|
||||
assert(res.status != -1);
|
||||
|
||||
if (400 <= res.status && error_handler_) { error_handler_(req, res); }
|
||||
|
||||
std::string content_type;
|
||||
std::string boundary;
|
||||
if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }
|
||||
|
||||
// Headers
|
||||
if (close_connection || req.get_header_value("Connection") == "close") {
|
||||
res.set_header("Connection", "close");
|
||||
@@ -4649,16 +4671,11 @@ Server::process_request(Stream &strm, bool close_connection,
|
||||
// Rounting
|
||||
if (routing(req, res, strm)) {
|
||||
if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; }
|
||||
return write_response_with_content(strm, close_connection, req, res);
|
||||
} else {
|
||||
if (res.status == -1) { res.status = 404; }
|
||||
return write_response(strm, close_connection, req, res);
|
||||
}
|
||||
|
||||
std::string content_type;
|
||||
std::string boundary;
|
||||
apply_ranges(req, res, content_type, boundary);
|
||||
|
||||
return write_response_with_content(strm, close_connection, req, res,
|
||||
content_type, boundary);
|
||||
}
|
||||
|
||||
inline bool Server::is_valid() const { return true; }
|
||||
|
||||
+94
-3
@@ -135,6 +135,17 @@ TEST(GetHeaderValueTest, RegularValue) {
|
||||
EXPECT_STREQ("text/html", val);
|
||||
}
|
||||
|
||||
TEST(GetHeaderValueTest, SetContent) {
|
||||
Response res;
|
||||
|
||||
res.set_content("html", "text/html");
|
||||
EXPECT_EQ("text/html", res.get_header_value("Content-Type"));
|
||||
|
||||
res.set_content("text", "text/plain");
|
||||
EXPECT_EQ(1, res.get_header_value_count("Content-Type"));
|
||||
EXPECT_EQ("text/plain", res.get_header_value("Content-Type"));
|
||||
}
|
||||
|
||||
TEST(GetHeaderValueTest, RegularValueInt) {
|
||||
Headers headers = {{"Content-Length", "100"}, {"Dummy", "Dummy"}};
|
||||
auto val =
|
||||
@@ -830,7 +841,7 @@ TEST(UrlWithSpace, Redirect) {
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(Server, BindDualStack) {
|
||||
TEST(BindServerTest, BindDualStack) {
|
||||
Server svr;
|
||||
|
||||
svr.Get("/1", [&](const Request & /*req*/, Response &res) {
|
||||
@@ -863,7 +874,7 @@ TEST(Server, BindDualStack) {
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
TEST(Server, BindAndListenSeparately) {
|
||||
TEST(BindServerTest, BindAndListenSeparately) {
|
||||
Server svr;
|
||||
int port = svr.bind_to_any_port("0.0.0.0");
|
||||
ASSERT_TRUE(svr.is_valid());
|
||||
@@ -872,7 +883,7 @@ TEST(Server, BindAndListenSeparately) {
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(SSLServer, BindAndListenSeparately) {
|
||||
TEST(BindServerTest, BindAndListenSeparatelySSL) {
|
||||
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE,
|
||||
CLIENT_CA_CERT_DIR);
|
||||
int port = svr.bind_to_any_port("0.0.0.0");
|
||||
@@ -882,6 +893,41 @@ TEST(SSLServer, BindAndListenSeparately) {
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(ErrorHandlerTest, ContentLength) {
|
||||
Server svr;
|
||||
|
||||
svr.set_error_handler([](const Request & /*req*/, Response &res) {
|
||||
res.status = 200;
|
||||
res.set_content("abcdefghijklmnopqrstuvwxyz",
|
||||
"text/html"); // <= Content-Length still 13
|
||||
});
|
||||
|
||||
svr.Get("/hi", [](const Request & /*req*/, Response &res) {
|
||||
res.set_content("Hello World!\n", "text/plain");
|
||||
res.status = 524;
|
||||
});
|
||||
|
||||
auto thread = std::thread([&]() { svr.listen(HOST, PORT); });
|
||||
|
||||
// Give GET time to get a few messages.
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
{
|
||||
Client cli(HOST, PORT);
|
||||
|
||||
auto res = cli.Get("/hi");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ("26", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ("abcdefghijklmnopqrstuvwxyz", res->body);
|
||||
}
|
||||
|
||||
svr.stop();
|
||||
thread.join();
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
class ServerTest : public ::testing::Test {
|
||||
protected:
|
||||
ServerTest()
|
||||
@@ -3459,6 +3505,51 @@ TEST(SSLClientServerTest, TrustDirOptional) {
|
||||
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST(SSLClientServerTest, SSLConnectTimeout) {
|
||||
class NoListenSSLServer : public SSLServer {
|
||||
public:
|
||||
NoListenSSLServer(const char *cert_path, const char *private_key_path,
|
||||
const char *client_ca_cert_file_path,
|
||||
const char *client_ca_cert_dir_path = nullptr)
|
||||
: SSLServer(cert_path, private_key_path, client_ca_cert_file_path,
|
||||
client_ca_cert_dir_path),
|
||||
stop_(false) {}
|
||||
|
||||
bool stop_;
|
||||
|
||||
private:
|
||||
bool process_and_close_socket(socket_t /*sock*/) override {
|
||||
// Don't create SSL context
|
||||
while (!stop_) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
NoListenSSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE,
|
||||
CLIENT_CA_CERT_FILE);
|
||||
ASSERT_TRUE(svr.is_valid());
|
||||
|
||||
svr.Get("/test", [&](const Request &, Response &res) {
|
||||
res.set_content("test", "text/plain");
|
||||
});
|
||||
|
||||
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
|
||||
SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
|
||||
cli.enable_server_certificate_verification(false);
|
||||
cli.set_connection_timeout(1);
|
||||
|
||||
auto res = cli.Get("/test");
|
||||
ASSERT_TRUE(!res);
|
||||
EXPECT_EQ(Error::SSLConnection, res.error());
|
||||
|
||||
svr.stop_ = true;
|
||||
svr.stop();
|
||||
t.join();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
Reference in New Issue
Block a user