]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5451] HttpRequest can now be used for outbound messages.
authorMarcin Siodelski <marcin@isc.org>
Fri, 15 Dec 2017 15:52:03 +0000 (16:52 +0100)
committerMarcin Siodelski <marcin@isc.org>
Fri, 15 Dec 2017 15:52:03 +0000 (16:52 +0100)
src/lib/http/header_context.h
src/lib/http/request.cc
src/lib/http/request.h
src/lib/http/tests/request_unittests.cc

index 125591186ed99d1ffa67b73a054b2af2b69872a1..9280d20b6670cb573265b01035bad94ec4b4937b 100644 (file)
@@ -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
 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
index 74b5d18759b1bdc85a52d8e5a0f6b7c9be27111c..1cc7a2db06033087d42c0701dfcc299903b53c8f 100644 (file)
@@ -7,6 +7,7 @@
 #include <http/request.h>
 #include <boost/algorithm/string.hpp>
 #include <boost/lexical_cast.hpp>
+#include <sstream>
 
 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<std::string>(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");
index afd13c7514e028fc552f23b5903cccedc7cfc863..5bbdcdbcbd2d3d46190202670c6e34eaaf989501 100644 (file)
@@ -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
index fc65b443727305d4532ab9ad71af6cff9b6faccc..9d1e5411df89e4a31a99fe69073d406a7c8b5492 100644 (file)
@@ -7,6 +7,7 @@
 #include <config.h>
 
 #include <http/request.h>
+#include <http/date_time.h>
 #include <http/http_header.h>
 #include <http/http_types.h>
 #include <http/tests/request_test.h>
@@ -19,6 +20,7 @@ using namespace isc::http::test;
 
 namespace {
 
+/// @brief Test fixture class for @c HttpRequest class.
 class HttpRequestTest : public HttpRequestTestBase<HttpRequest> {
 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_ = "<html></html>";
+    // 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"
+              "<html></html>",
+              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());
+}
+
 }