Fixed problem with connection close

This commit is contained in:
yhirose 2018-05-14 00:05:14 -04:00
parent 312a8d7523
commit 7b9d752583
2 changed files with 46 additions and 30 deletions

View file

@ -216,7 +216,7 @@ public:
void stop(); void stop();
protected: protected:
bool process_request(Stream& strm, bool last_connection); bool process_request(Stream& strm, bool last_connection, bool& connection_close);
private: private:
typedef std::vector<std::pair<std::regex, Handler>> Handlers; typedef std::vector<std::pair<std::regex, Handler>> Handlers;
@ -285,7 +285,7 @@ public:
bool send(Request& req, Response& res); bool send(Request& req, Response& res);
protected: protected:
bool process_request(Stream& strm, Request& req, Response& res); bool process_request(Stream& strm, Request& req, Response& res, bool& connection_close);
const std::string host_; const std::string host_;
const int port_; const int port_;
@ -503,17 +503,21 @@ inline bool read_and_close_socket(socket_t sock, bool keep_alive, T callback)
detail::select_read(sock, detail::select_read(sock,
CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND,
CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) { CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) {
auto last_connection = count == 1;
SocketStream strm(sock); SocketStream strm(sock);
ret = callback(strm, last_connection); auto last_connection = count == 1;
if (!ret) { auto connection_close = false;
ret = callback(strm, last_connection, connection_close);
if (!ret || connection_close) {
break; break;
} }
count--; count--;
} }
} else { } else {
SocketStream strm(sock); SocketStream strm(sock);
ret = callback(strm, true); auto dummy_connection_close = false;
ret = callback(strm, true, dummy_connection_close);
} }
close_socket(sock); close_socket(sock);
@ -1535,7 +1539,9 @@ inline void Server::write_response(Stream& strm, bool last_connection, const Req
detail::status_message(res.status)); detail::status_message(res.status));
// Headers // Headers
if (!res.has_header("Connection") && (last_connection || req.version == "HTTP/1.0")) { if (last_connection ||
req.version == "HTTP/1.0" ||
req.get_header_value("Connection") == "close") {
res.set_header("Connection", "close"); res.set_header("Connection", "close");
} }
@ -1724,7 +1730,7 @@ inline bool Server::dispatch_request(Request& req, Response& res, Handlers& hand
return false; return false;
} }
inline bool Server::process_request(Stream& strm, bool last_connection) inline bool Server::process_request(Stream& strm, bool last_connection, bool& connection_close)
{ {
const auto bufsiz = 2048; const auto bufsiz = 2048;
char buf[bufsiz]; char buf[bufsiz];
@ -1750,7 +1756,8 @@ inline bool Server::process_request(Stream& strm, bool last_connection)
auto ret = true; auto ret = true;
if (req.get_header_value("Connection") == "close") { if (req.get_header_value("Connection") == "close") {
ret = false; // ret = false;
connection_close = true;
} }
req.set_header("REMOTE_ADDR", strm.get_remote_addr().c_str()); req.set_header("REMOTE_ADDR", strm.get_remote_addr().c_str());
@ -1810,8 +1817,8 @@ inline bool Server::read_and_close_socket(socket_t sock)
return detail::read_and_close_socket( return detail::read_and_close_socket(
sock, sock,
true, true,
[this](Stream& strm, bool last_connection) { [this](Stream& strm, bool last_connection, bool& connection_close) {
return process_request(strm, last_connection); return process_request(strm, last_connection, connection_close);
}); });
} }
@ -1910,11 +1917,10 @@ inline void Client::write_request(Stream& strm, Request& req)
req.set_header("User-Agent", "cpp-httplib/0.2"); req.set_header("User-Agent", "cpp-httplib/0.2");
} }
// TODO: if (!req.has_header("Connection") && // TODO: Support KeepAlive connection
// (last_connection || http_version_ == detail::HttpVersion::v1_0)) { // if (!req.has_header("Connection")) {
if (!req.has_header("Connection")) {
req.set_header("Connection", "close"); req.set_header("Connection", "close");
} // }
if (!req.body.empty()) { if (!req.body.empty()) {
if (!req.has_header("Content-Type")) { if (!req.has_header("Content-Type")) {
@ -1938,7 +1944,7 @@ inline void Client::write_request(Stream& strm, Request& req)
} }
} }
inline bool Client::process_request(Stream& strm, Request& req, Response& res) inline bool Client::process_request(Stream& strm, Request& req, Response& res, bool& connection_close)
{ {
// Send request // Send request
write_request(strm, req); write_request(strm, req);
@ -1948,8 +1954,9 @@ inline bool Client::process_request(Stream& strm, Request& req, Response& res)
return false; return false;
} }
// TODO: Check if 'Connection' header is 'close' or HTTP version is 1.0, if (res.get_header_value("Connection") == "close" || res.version == "HTTP/1.0") {
// then close socket... connection_close = true;
}
// Body // Body
if (req.method != "HEAD") { if (req.method != "HEAD") {
@ -1971,8 +1978,11 @@ inline bool Client::process_request(Stream& strm, Request& req, Response& res)
inline bool Client::read_and_close_socket(socket_t sock, Request& req, Response& res) inline bool Client::read_and_close_socket(socket_t sock, Request& req, Response& res)
{ {
return detail::read_and_close_socket(sock, false, [&](Stream& strm, bool /*last_connection*/) { return detail::read_and_close_socket(
return process_request(strm, req, res); sock,
false,
[&](Stream& strm, bool /*last_connection*/, bool& connection_close) {
return process_request(strm, req, res, connection_close);
}); });
} }
@ -2118,7 +2128,8 @@ namespace detail {
template <typename U, typename V, typename T> template <typename U, typename V, typename T>
inline bool read_and_close_socket_ssl( inline bool read_and_close_socket_ssl(
socket_t sock, bool keep_alive, socket_t sock, bool keep_alive,
// TODO: OpenSSL 1.0.2 occasionally crashes... The upcoming 1.1.0 is going to be thread safe. // TODO: OpenSSL 1.0.2 occasionally crashes...
// The upcoming 1.1.0 is going to be thread safe.
SSL_CTX* ctx, std::mutex& ctx_mutex, SSL_CTX* ctx, std::mutex& ctx_mutex,
U SSL_connect_or_accept, V setup, U SSL_connect_or_accept, V setup,
T callback) T callback)
@ -2148,17 +2159,21 @@ inline bool read_and_close_socket_ssl(
detail::select_read(sock, detail::select_read(sock,
CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND,
CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) { CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) {
auto last_connection = count == 1;
SSLSocketStream strm(sock, ssl); SSLSocketStream strm(sock, ssl);
ret = callback(strm, last_connection); auto last_connection = count == 1;
if (!ret) { auto connection_close = false;
ret = callback(strm, last_connection, connection_close);
if (!ret || connection_close) {
break; break;
} }
count--; count--;
} }
} else { } else {
SSLSocketStream strm(sock, ssl); SSLSocketStream strm(sock, ssl);
ret = callback(strm, true); auto dummy_connection_close = false;
ret = callback(strm, true, dummy_connection_close);
} }
SSL_shutdown(ssl); SSL_shutdown(ssl);
@ -2257,8 +2272,8 @@ inline bool SSLServer::read_and_close_socket(socket_t sock)
ctx_, ctx_mutex_, ctx_, ctx_mutex_,
SSL_accept, SSL_accept,
[](SSL* /*ssl*/) {}, [](SSL* /*ssl*/) {},
[this](Stream& strm, bool last_connection) { [this](Stream& strm, bool last_connection, bool& connection_close) {
return process_request(strm, last_connection); return process_request(strm, last_connection, connection_close);
}); });
} }
@ -2290,8 +2305,8 @@ inline bool SSLClient::read_and_close_socket(socket_t sock, Request& req, Respon
[&](SSL* ssl) { [&](SSL* ssl) {
SSL_set_tlsext_host_name(ssl, host_.c_str()); SSL_set_tlsext_host_name(ssl, host_.c_str());
}, },
[&](Stream& strm, bool /*last_connection*/) { [&](Stream& strm, bool /*last_connection*/, bool& connection_close) {
return process_request(strm, req, res); return process_request(strm, req, res, connection_close);
}); });
} }
#endif #endif

View file

@ -393,6 +393,7 @@ TEST_F(ServerTest, GetMethod200)
EXPECT_EQ("HTTP/1.1", res->version); EXPECT_EQ("HTTP/1.1", res->version);
EXPECT_EQ(200, res->status); EXPECT_EQ(200, res->status);
EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
EXPECT_EQ("close", res->get_header_value("Connection"));
EXPECT_EQ("Hello World!", res->body); EXPECT_EQ("Hello World!", res->body);
} }