From: Marcin Siodelski Date: Fri, 15 Dec 2017 15:52:03 +0000 (+0100) Subject: [5451] HttpRequest can now be used for outbound messages. X-Git-Tag: trac5457_base~4^2~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=166ef01e159758ef2b7a67de81eacae8429f7b97;p=thirdparty%2Fkea.git [5451] HttpRequest can now be used for outbound messages. --- diff --git a/src/lib/http/header_context.h b/src/lib/http/header_context.h index 125591186e..9280d20b66 100644 --- a/src/lib/http/header_context.h +++ b/src/lib/http/header_context.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -12,9 +12,25 @@ namespace isc { namespace http { +/// @brief HTTP header context. struct HttpHeaderContext { std::string name_; std::string value_; + + /// @brief Constructor. + /// + /// Sets header name and value to empty strings. + HttpHeaderContext() + : name_(), value_() { + } + + /// @brief Constructor. + /// + /// @param name Header name. + /// @param value Header value. + HttpHeaderContext(const std::string& name, const std::string& value) + : name_(name), value_(value) { + } }; } // namespace http diff --git a/src/lib/http/request.cc b/src/lib/http/request.cc index 74b5d18759..1cc7a2db06 100644 --- a/src/lib/http/request.cc +++ b/src/lib/http/request.cc @@ -7,6 +7,7 @@ #include #include #include +#include namespace isc { namespace http { @@ -87,6 +88,12 @@ HttpRequest::create() { headers_[hdr->getLowerCaseName()] = hdr; } + if (!context_->body_.empty() && (headers_.count("content-length") == 0)) { + HttpHeaderPtr hdr(new HttpHeader("Content-Length", + boost::lexical_cast(context_->body_.length()))); + headers_["content-length"] = hdr; + } + // Iterate over required headers and check that they exist // in the HTTP request. for (auto req_header = required_headers_.begin(); @@ -206,6 +213,27 @@ HttpRequest::getBody() const { return (context_->body_); } +std::string +HttpRequest::toText() const { + checkFinalized(); + + std::ostringstream s; + s << methodToString(getMethod()) << " " << getUri() << " HTTP/" << + getHttpVersion().major_ << "." << getHttpVersion().minor_ << "\r\n"; + + for (auto header_it = headers_.cbegin(); header_it != headers_.cend(); + ++header_it) { + s << header_it->second->getName() << ": " << header_it->second->getValue() + << "\r\n"; + } + + s << "\r\n"; + + s << getBody(); + + return (s.str()); +} + bool HttpRequest::isPersistent() const { HttpHeaderPtr conn = getHeaderSafe("connection"); diff --git a/src/lib/http/request.h b/src/lib/http/request.h index afd13c7514..5bbdcdbcbd 100644 --- a/src/lib/http/request.h +++ b/src/lib/http/request.h @@ -148,7 +148,7 @@ public: /// requirements for it. virtual void create(); - /// @brief Complete parsing of the HTTP request. + /// @brief Complete parsing of the HTTP request or create outbound HTTP request. /// /// HTTP request parsing is performed in two stages: HTTP headers, then /// request body. The @ref create method parses HTTP headers. Once this is @@ -160,6 +160,12 @@ public: /// that the @ref create method hasn't been called, it calls @ref create /// before parsing the body. /// + /// For the outbound (client) request, this method must be called after + /// setting all required values in the request context. The Content-Length + /// is generally not explicitly set by the caller in this case. This method + /// computes the value of the Content-Length and inserts the suitable header + /// when it finds non-empty body. + /// /// The derivations must call @ref create if it hasn't been called prior to /// calling this method. It must set @ref finalized_ to true if the call /// to @ref finalize was successful. @@ -221,6 +227,13 @@ public: /// @brief Returns HTTP message body as string. std::string getBody() const; + /// @brief Returns HTTP message as text. + /// + /// This method is called to generate the outbound HTTP message to be sent + /// to a server. Make sure to call @c HttpRequest::finalize prior to + /// calling this method. + virtual std::string toText() const; + /// @brief Checks if the request has been successfully finalized. /// /// The request is gets finalized on successful call to diff --git a/src/lib/http/tests/request_unittests.cc b/src/lib/http/tests/request_unittests.cc index fc65b44372..9d1e5411df 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 @@ -19,6 +20,7 @@ using namespace isc::http::test; namespace { +/// @brief Test fixture class for @c HttpRequest class. class HttpRequestTest : public HttpRequestTestBase { public: @@ -226,4 +228,47 @@ TEST_F(HttpRequestTest, isPersistentHttp11Close) { ); } +TEST_F(HttpRequestTest, clientRequest) { + setContextBasics("POST", "/isc/org", HttpVersion(1, 0)); + + // Capture current date and time. + HttpDateTime date_time; + + // Add headers. + request_.context()->headers_.push_back(HttpHeaderContext("Date", date_time.rfc1123Format())); + request_.context()->headers_.push_back(HttpHeaderContext("Content-Type", "text/html")); + request_.context()->headers_.push_back(HttpHeaderContext("Accept", "text/html")); + // Add a body. + request_.context()->body_ = ""; + // Commit and validate the data. + ASSERT_NO_THROW(request_.finalize()); + + // Check that the HTTP request in the textual format is correct. Note that + // it should include "Content-Length", even though we haven't explicitly set + // this header. It is dynamically computed from the body size. + EXPECT_EQ("POST /isc/org HTTP/1.0\r\n" + "Accept: text/html\r\n" + "Content-Length: 13\r\n" + "Content-Type: text/html\r\n" + "Date: " + date_time.rfc1123Format() + "\r\n" + "\r\n" + "", + request_.toText()); +} + +TEST_F(HttpRequestTest, clientRequestNoBody) { + setContextBasics("GET", "/isc/org", HttpVersion(1, 1)); + // Add headers. + request_.context()->headers_.push_back(HttpHeaderContext("Content-Type", "text/html")); + // Commit and validate the data. + ASSERT_NO_THROW(request_.finalize()); + + // Check that the HTTP request in the textual format is correct. Note that + // there should be no Content-Length included, because the body is empty. + EXPECT_EQ("GET /isc/org HTTP/1.1\r\n" + "Content-Type: text/html\r\n" + "\r\n", + request_.toText()); +} + }