From: Marcin Siodelski Date: Tue, 12 Dec 2017 09:29:09 +0000 (+0100) Subject: [5448] Implemented method indicating whether HTTP connection is persistent. X-Git-Tag: trac5452_base~5^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=909e20b909bf44d932326f7d67bd5c215e146ae8;p=thirdparty%2Fkea.git [5448] Implemented method indicating whether HTTP connection is persistent. --- diff --git a/src/lib/http/http_types.h b/src/lib/http/http_types.h index 285096643c..68d1c0952b 100644 --- a/src/lib/http/http_types.h +++ b/src/lib/http/http_types.h @@ -44,6 +44,30 @@ struct HttpVersion { bool operator!=(const HttpVersion& rhs) const { return (!operator==(rhs)); } + + /// @name Methods returning @c HttpVersion object encapsulating typical + /// HTTP version numbers. + //@{ + + /// @brief HTTP version 1.0. + static const HttpVersion& HTTP_10() { + static HttpVersion ver(1, 0); + return (ver); + }; + + /// @brief HTTP version 1.1. + static const HttpVersion& HTTP_11() { + static HttpVersion ver(1, 1); + return (ver); + } + + /// @brief HTTP version 2.0. + static const HttpVersion& HTTP_20() { + static HttpVersion ver(2, 0); + return (ver); + } + + //@} }; } // end of namespace isc::http diff --git a/src/lib/http/request.cc b/src/lib/http/request.cc index b54a35ad4d..74b5d18759 100644 --- a/src/lib/http/request.cc +++ b/src/lib/http/request.cc @@ -158,6 +158,20 @@ HttpRequest::getHttpVersion() const { HttpHeaderPtr HttpRequest::getHeader(const std::string& header_name) const { + HttpHeaderPtr http_header = getHeaderSafe(header_name); + + // No such header. + if (!http_header) { + isc_throw(HttpRequestNonExistingHeader, header_name << " HTTP header" + " not found in the request"); + } + + // Header found. + return (http_header); +} + +HttpHeaderPtr +HttpRequest::getHeaderSafe(const std::string& header_name) const { checkCreated(); HttpHeader hdr(header_name); @@ -166,9 +180,8 @@ HttpRequest::getHeader(const std::string& header_name) const { return (header_it->second); } - // No such header. - isc_throw(HttpRequestNonExistingHeader, header_name << " HTTP header" - " not found in the request"); + // Header not found. Return null pointer. + return (HttpHeaderPtr()); } std::string @@ -195,7 +208,16 @@ HttpRequest::getBody() const { bool HttpRequest::isPersistent() const { - return (false); + HttpHeaderPtr conn = getHeaderSafe("connection"); + std::string conn_value; + if (conn) { + conn_value = conn->getLowerCaseValue(); + } + + HttpVersion ver = getHttpVersion(); + + return (((ver == HttpVersion::HTTP_10()) && (conn_value == "keep-alive")) || + ((HttpVersion::HTTP_10() < ver) && (conn_value.empty() || (conn_value != "close")))); } void diff --git a/src/lib/http/request.h b/src/lib/http/request.h index cb7178efdc..afd13c7514 100644 --- a/src/lib/http/request.h +++ b/src/lib/http/request.h @@ -183,8 +183,26 @@ public: /// @brief Returns object encapsulating HTTP header. /// /// @param header_name HTTP header name. + /// + /// @return Non-null pointer to the header. + /// @throw HttpRequestNonExistingHeader if header with the specified name + /// doesn't exist. + /// @throw HttpRequestError if the request hasn't been created. HttpHeaderPtr getHeader(const std::string& header_name) const; + /// @brief Returns object encapsulating HTTP header. + /// + /// This variant doesn't throw an exception if the header doesn't exist. + /// It will throw if the request hasn't been created using @c create() + /// method. + /// + /// @param header_name HTTP header name. + /// + /// @return Pointer to the specified header, or null if such header doesn't + /// exist. + /// @throw HttpRequestError if the request hasn't been created. + HttpHeaderPtr getHeaderSafe(const std::string& header_name) const; + /// @brief Returns a value of the specified HTTP header. /// /// @param header_name Name of the HTTP header. diff --git a/src/lib/http/tests/request_unittests.cc b/src/lib/http/tests/request_unittests.cc index ae6bbf20f9..fc65b44372 100644 --- a/src/lib/http/tests/request_unittests.cc +++ b/src/lib/http/tests/request_unittests.cc @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -18,7 +19,51 @@ using namespace isc::http::test; namespace { -typedef HttpRequestTestBase HttpRequestTest; +class HttpRequestTest : public HttpRequestTestBase { +public: + + /// @brief Tests connection persistence for the given HTTP version + /// and header value. + /// + /// This method creates a dummy HTTP request and sets the specified + /// version and header. Next, it returns the value if @c isPersistent + /// method for this request. The unit test verifies this value for + /// correctness. + /// + /// @param http_version HTTP version. + /// @param http_header HTTP header to be included in the request. If + /// the header has an empty value, it is not included. + /// + /// @return true if request indicates that connection is to be + /// persistent. + bool isPersistent(const HttpVersion& http_version, + const HttpHeader& http_header = HttpHeader("Connection")) { + try { + // We need to add some JSON body. + std::string json_body = "{ \"param1\": \"foo\" }"; + + // Set method, path, version and content length. + setContextBasics("POST", "/isc/org", http_version); + addHeaderToContext("Content-Length", json_body.length()); + + // If additional header has been specified (typically "Connection"), + // include it. + if (!http_header.getValue().empty()) { + addHeaderToContext(http_header.getName(), http_header.getValue()); + } + // Attach JSON body. + request_.context()->body_ = json_body; + request_.create(); + + } catch (...) { + ADD_FAILURE() << "failed to create HTTP request while testing" + " connection persistence"; + } + + return (request_.isPersistent()); + } + +}; TEST_F(HttpRequestTest, minimal) { setContextBasics("GET", "/isc/org", HttpVersion(1, 1)); @@ -155,4 +200,30 @@ TEST_F(HttpRequestTest, requiresBody) { EXPECT_TRUE(request_.requiresBody()); } +TEST_F(HttpRequestTest, isPersistentHttp10) { + // In HTTP 1.0 the connection is by default non-persistent. + EXPECT_FALSE(isPersistent(HttpVersion(1, 0))); +} + +TEST_F(HttpRequestTest, isPersistentHttp11) { + // In HTTP 1.1 the connection is by default persistent. + EXPECT_TRUE(isPersistent(HttpVersion(1, 1))); +} + +TEST_F(HttpRequestTest, isPersistentHttp10KeepAlive) { + // In HTTP 1.0 the client indicates that the connection is desired to be + // persistent by including "Connection: keep-alive" header. + EXPECT_TRUE( + isPersistent(HttpVersion(1, 0), HttpHeader("Connection", "Keep-alive")) + ); +} + +TEST_F(HttpRequestTest, isPersistentHttp11Close) { + // In HTTP 1.1 the client would include "Connection: close" header if it + // desires to close the connection. + EXPECT_FALSE( + isPersistent(HttpVersion(1, 1), HttpHeader("Connection", "close")) + ); +} + }