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
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);
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
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
/// @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.
#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>
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));
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"))
+ );
+}
+
}