Commit 909e20b9 authored by Marcin Siodelski's avatar Marcin Siodelski

[5448] Implemented method indicating whether HTTP connection is persistent.

parent 528ab521
......@@ -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
......
......@@ -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
......
......@@ -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.
......
......@@ -7,6 +7,7 @@
#include <config.h>
#include <http/request.h>
#include <http/http_header.h>
#include <http/http_types.h>
#include <http/tests/request_test.h>
#include <boost/lexical_cast.hpp>
......@@ -18,7 +19,51 @@ using namespace isc::http::test;
namespace {
typedef HttpRequestTestBase<HttpRequest> HttpRequestTest;
class HttpRequestTest : public HttpRequestTestBase<HttpRequest> {
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"))
);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment