]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5451] Extended HttpResponse for inbound messages.
authorMarcin Siodelski <marcin@isc.org>
Mon, 18 Dec 2017 14:01:39 +0000 (15:01 +0100)
committerMarcin Siodelski <marcin@isc.org>
Mon, 18 Dec 2017 14:01:39 +0000 (15:01 +0100)
src/lib/http/Makefile.am
src/lib/http/http_message.cc [new file with mode: 0644]
src/lib/http/http_message.h [new file with mode: 0644]
src/lib/http/post_request_json.cc
src/lib/http/request.cc
src/lib/http/request.h
src/lib/http/response.cc
src/lib/http/response.h
src/lib/http/response_context.h [new file with mode: 0644]
src/lib/http/tests/post_request_json_unittests.cc
src/lib/http/tests/request_unittests.cc

index 599711500c6ef1307a542193eb4304388c520f93..13cca2f3152ce6677be312b1c080f0745ab041ba 100644 (file)
@@ -29,6 +29,7 @@ libkea_http_la_SOURCES += http_log.cc http_log.h
 libkea_http_la_SOURCES += header_context.h
 libkea_http_la_SOURCES += http_acceptor.h
 libkea_http_la_SOURCES += http_header.cc http_header.h
+libkea_http_la_SOURCES += http_message.cc http_message.h
 libkea_http_la_SOURCES += http_types.h
 libkea_http_la_SOURCES += listener.cc listener.h
 libkea_http_la_SOURCES += post_request.cc post_request.h
@@ -37,6 +38,7 @@ libkea_http_la_SOURCES += request.cc request.h
 libkea_http_la_SOURCES += request_context.h
 libkea_http_la_SOURCES += request_parser.cc request_parser.h
 libkea_http_la_SOURCES += response.cc response.h
+libkea_http_la_SOURCES += response_context.h
 libkea_http_la_SOURCES += response_creator.cc response_creator.h
 libkea_http_la_SOURCES += response_creator_factory.h
 libkea_http_la_SOURCES += response_json.cc response_json.h
diff --git a/src/lib/http/http_message.cc b/src/lib/http/http_message.cc
new file mode 100644 (file)
index 0000000..0eb9770
--- /dev/null
@@ -0,0 +1,120 @@
+// Copyright (C) 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <http/http_message.h>
+
+namespace isc {
+namespace http {
+
+HttpMessage::HttpMessage()
+    : required_versions_(),  http_version_(HttpVersion::HTTP_10()),
+      required_headers_(), created_(false), finalized_(false), headers_(),
+      body_() {
+}
+
+HttpMessage::~HttpMessage() {
+}
+
+void
+HttpMessage::requireHttpVersion(const HttpVersion& version) {
+    required_versions_.insert(version);
+}
+
+void
+HttpMessage::requireHeader(const std::string& header_name) {
+    // Empty value denotes that the header is required but no specific
+    // value is expected.
+    HttpHeaderPtr hdr(new HttpHeader(header_name));
+    required_headers_[hdr->getLowerCaseName()] = hdr;
+}
+
+void
+HttpMessage::requireHeaderValue(const std::string& header_name,
+                                const std::string& header_value) {
+    HttpHeaderPtr hdr(new HttpHeader(header_name, header_value));
+    required_headers_[hdr->getLowerCaseName()] = hdr;
+}
+
+bool
+HttpMessage::requiresBody() const {
+    // If Content-Length is required the body must exist too. There may
+    // be probably some cases when Content-Length is not provided but
+    // the body is provided. But, probably not in our use cases.
+    // Use lower case header name because this is how it is indexed in
+    // the storage.
+    return (required_headers_.find("content-length") != required_headers_.end());
+}
+
+HttpVersion
+HttpMessage::getHttpVersion() const {
+    checkCreated();
+    return (http_version_);
+}
+
+HttpHeaderPtr
+HttpMessage::getHeader(const std::string& header_name) const {
+    HttpHeaderPtr http_header = getHeaderSafe(header_name);
+
+    // No such header.
+    if (!http_header) {
+        isc_throw(HttpMessageNonExistingHeader, header_name << " HTTP header"
+                  " not found in the request");
+    }
+
+    // Header found.
+    return (http_header);
+}
+
+HttpHeaderPtr
+HttpMessage::getHeaderSafe(const std::string& header_name) const {
+    checkCreated();
+
+    HttpHeader hdr(header_name);
+    auto header_it = headers_.find(hdr.getLowerCaseName());
+    if (header_it != headers_.end()) {
+        return (header_it->second);
+    }
+
+    // Header not found. Return null pointer.
+    return (HttpHeaderPtr());
+}
+
+std::string
+HttpMessage::getHeaderValue(const std::string& header_name) const {
+    return (getHeader(header_name)->getValue());
+}
+
+uint64_t
+HttpMessage::getHeaderValueAsUint64(const std::string& header_name) const {
+    try {
+        return (getHeader(header_name)->getUint64Value());
+
+    } catch (const std::exception& ex) {
+        // The specified header does exist, but the value is not a number.
+        isc_throw(HttpMessageError, ex.what());
+    }
+}
+
+void
+HttpMessage::checkCreated() const {
+    if (!created_) {
+        isc_throw(HttpMessageError, "unable to retrieve values of HTTP"
+                  " message because the HttpMessage::create() must be"
+                  " called first. This is a programmatic error");
+    }
+}
+
+void
+HttpMessage::checkFinalized() const {
+    if (!finalized_) {
+        isc_throw(HttpMessageError, "unable to retrieve body of HTTP"
+                  " message because the HttpMessage::finalize() must be"
+                  " called first. This is a programmatic error");
+    }
+}
+
+} // end of namespace isc::http
+} // end of namespace isc
diff --git a/src/lib/http/http_message.h b/src/lib/http/http_message.h
new file mode 100644 (file)
index 0000000..c635b6f
--- /dev/null
@@ -0,0 +1,240 @@
+// Copyright (C) 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef HTTP_MESSAGE_H
+#define HTTP_MESSAGE_H
+
+#include <exceptions/exceptions.h>
+#include <http/http_header.h>
+#include <http/http_types.h>
+#include <map>
+#include <set>
+#include <stdint.h>
+#include <string>
+
+namespace isc {
+namespace http {
+
+/// @brief Generic exception thrown by @ref HttpMessage class.
+class HttpMessageError : public Exception {
+public:
+    HttpMessageError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief Exception thrown when attempt is made to retrieve a
+/// non-existing header.
+class HttpMessageNonExistingHeader : public HttpMessageError {
+public:
+    HttpMessageNonExistingHeader(const char* file, size_t line,
+                                 const char* what) :
+        HttpMessageError(file, line, what) { };
+};
+
+
+/// @brief Base class for @ref HttpRequest and @ref HttpResponse.
+class HttpMessage {
+public:
+
+    /// @brief Constructor.
+    HttpMessage();
+
+    /// @brief Destructor.
+    virtual ~HttpMessage();
+
+    /// @brief Specifies HTTP version allowed.
+    ///
+    /// Allowed HTTP versions must be specified prior to calling @ref create
+    /// method. If no version is specified, all versions are allowed.
+    ///
+    /// @param version Version number allowed for the request.
+    void requireHttpVersion(const HttpVersion& version);
+
+    /// @brief Specifies a required HTTP header for the HTTP message.
+    ///
+    /// Required headers must be specified prior to calling @ref create method.
+    /// The specified header must exist in the received HTTP request. This puts
+    /// no requirement on the header value.
+    ///
+    /// @param header_name Required header name.
+    void requireHeader(const std::string& header_name);
+
+    /// @brief Specifies a required value of a header in the message.
+    ///
+    /// Required header values must be specified prior to calling @ref create
+    /// method. The specified header must exist and its value must be equal to
+    /// the value specified as second parameter.
+    ///
+    /// @param header_name HTTP header name.
+    /// @param header_value HTTP header value.
+    void requireHeaderValue(const std::string& header_name,
+                            const std::string& header_value);
+
+    /// @brief Checks if the body is required for the HTTP message.
+    ///
+    /// Current implementation simply checks if the "Content-Length" header
+    /// is required.
+    ///
+    /// @return true if the body is required, false otherwise.
+    bool requiresBody() const;
+
+    /// @brief Reads parsed message from the context, validates the message and
+    /// stores parsed information.
+    ///
+    /// This method must be called before retrieving parsed data using accessors.
+    /// This method doesn't parse the HTTP request body.
+    virtual void create() = 0;
+
+    /// @brief Complete parsing HTTP message or creating an HTTP outbound message.
+    ///
+    /// This method is used in two situations: when a message has been received
+    /// into a context and may be fully parsed (including the body) or when the
+    /// data for the creation of the outbound message have been stored in a context
+    /// and the message can be now created from the context.
+    ///
+    /// This method should call @c create method if it hasn't been called yet and
+    /// then read the message body from the context and interpret it. If the body
+    /// doesn't adhere to the requirements for the message (in particular, when the
+    /// content type of the body is invalid) an exception should be thrown.
+    virtual void finalize() = 0;
+
+    /// @brief Reset the state of the object.
+    virtual void reset() = 0;
+
+    /// @brief Returns HTTP version number (major and minor).
+    HttpVersion getHttpVersion() const;
+
+    /// @brief Returns object encapsulating HTTP header.
+    ///
+    /// @param header_name HTTP header name.
+    ///
+    /// @return Non-null pointer to the header.
+    /// @throw HttpMessageNonExistingHeader if header with the specified name
+    /// doesn't exist.
+    /// @throw HttpMessageError 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 HttpMessageError 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.
+    ///
+    /// @throw HttpMessageError if the header doesn't exist.
+    std::string getHeaderValue(const std::string& header_name) const;
+
+    /// @brief Returns a value of the specified HTTP header as number.
+    ///
+    /// @param header_name Name of the HTTP header.
+    ///
+    /// @throw HttpMessageError if the header doesn't exist or if the
+    /// header value is not number.
+    uint64_t getHeaderValueAsUint64(const std::string& header_name) const;
+
+    /// @brief Returns HTTP message body as string.
+    virtual std::string getBody() const = 0;
+
+    /// @brief Returns HTTP message as text.
+    ///
+    /// This method is called to generate the outbound HTTP message. Make
+    /// sure to call @c finalize prior to calling this method.
+    virtual std::string toString() const = 0;
+
+    /// @brief Checks if the message has been successfully finalized.
+    ///
+    /// The message gets finalized on successful call to @c finalize.
+    ///
+    /// @return true if the message has been finalized, false otherwise.
+    bool isFinalized() const {
+        return (finalized_);
+    }
+
+    /// @brief Checks if the message indicates persistent connection.
+    ///
+    /// @return true if the message indicates persistent connection, false
+    /// otherwise.
+    virtual bool isPersistent() const = 0;
+
+protected:
+
+    /// @brief Checks if the @ref create was called.
+    ///
+    /// @throw HttpMessageError if @ref create wasn't called.
+    void checkCreated() const;
+
+    /// @brief Checks if the @ref finalize was called.
+    ///
+    /// @throw HttpMessageError if @ref finalize wasn't called.
+    void checkFinalized() const;
+
+    /// @brief Checks if the set is empty or the specified element belongs
+    /// to this set.
+    ///
+    /// This is a convenience method used by the class to verify that the
+    /// given HTTP method belongs to "required methods", HTTP version belongs
+    /// to "required versions" etc.
+    ///
+    /// @param element Reference to the element.
+    /// @param element_set Reference to the set of elements.
+    /// @tparam Element type, e.g. @ref Method, @ref HttpVersion etc.
+    ///
+    /// @return true if the element set is empty or if the element belongs
+    /// to the set.
+    template<typename T>
+    bool inRequiredSet(const T& element,
+                       const std::set<T>& element_set) const {
+        return (element_set.empty() || element_set.count(element) > 0);
+    }
+
+    /// @brief Set of required HTTP versions.
+    ///
+    /// If the set is empty, all versions are allowed.
+    std::set<HttpVersion> required_versions_;
+
+    /// @brief HTTP version numbers.
+    HttpVersion http_version_;
+
+    /// @brief Map of HTTP headers indexed by lower case header names.
+    typedef std::map<std::string, HttpHeaderPtr> HttpHeaderMap;
+
+    /// @brief Map holding required HTTP headers.
+    ///
+    /// The key of this map specifies the lower case HTTP header name.
+    /// If the value of the HTTP header is empty, the header is required
+    /// but the value of the header is not checked. If the value is
+    /// non-empty, the value in the HTTP request must be equal (case
+    /// insensitive) to the value in the map.
+    HttpHeaderMap required_headers_;
+
+    /// @brief Flag indicating whether @ref create was called.
+    bool created_;
+
+    /// @brief Flag indicating whether @ref finalize was called.
+    bool finalized_;
+
+    /// @brief Parsed HTTP headers.
+    HttpHeaderMap headers_;
+
+    /// @brief HTTP body as string.
+    std::string body_;
+
+};
+
+} // end of namespace isc::http
+} // end of namespace isc
+
+#endif // HTTP_MESSAGE_H
index d7c401663f81250e0f2389cd73b02c2bf69a045e..0a93312720c58bd58b6268bb1358dd1452c22a91 100644 (file)
@@ -42,11 +42,11 @@ PostHttpRequestJson::getBodyAsJson() const {
 void
 PostHttpRequestJson::setBodyAsJson(const data::ConstElementPtr& body) {
     if (body) {
-        context()->body_ = body->str();
+        context_->body_ = body->str();
         json_ = body;
 
     } else {
-        context()->body_.clear();
+        context_->body_.clear();
     }
 }
 
index 0352e2e8567ebed3202aa67474890ec649a2f74b..2d93c0c4cf74ab42bd7ded1a678bb9e1dcc8a653 100644 (file)
@@ -13,12 +13,9 @@ namespace isc {
 namespace http {
 
 HttpRequest::HttpRequest()
-    : required_methods_(),required_versions_(), required_headers_(),
-      created_(false), finalized_(false), method_(Method::HTTP_METHOD_UNKNOWN),
-      headers_(), context_(new HttpRequestContext()) {
-}
-
-HttpRequest::~HttpRequest() {
+    : HttpMessage(), required_methods_(),
+      method_(Method::HTTP_METHOD_UNKNOWN),
+      context_(new HttpRequestContext()) {
 }
 
 void
@@ -26,36 +23,6 @@ HttpRequest::requireHttpMethod(const HttpRequest::Method& method) {
     required_methods_.insert(method);
 }
 
-void
-HttpRequest::requireHttpVersion(const HttpVersion& version) {
-    required_versions_.insert(version);
-}
-
-void
-HttpRequest::requireHeader(const std::string& header_name) {
-    // Empty value denotes that the header is required but no specific
-    // value is expected.
-    HttpHeaderPtr hdr(new HttpHeader(header_name));
-    required_headers_[hdr->getLowerCaseName()] = hdr;
-}
-
-void
-HttpRequest::requireHeaderValue(const std::string& header_name,
-                                const std::string& header_value) {
-    HttpHeaderPtr hdr(new HttpHeader(header_name, header_value));
-    required_headers_[hdr->getLowerCaseName()] = hdr;
-}
-
-bool
-HttpRequest::requiresBody() const {
-    // If Content-Length is required the body must exist too. There may
-    // be probably some cases when Content-Length is not provided but
-    // the body is provided. But, probably not in our use cases.
-    // Use lower case header name because this is how it is indexed in
-    // the storage.
-    return (required_headers_.find("content-length") != required_headers_.end());
-}
-
 void
 HttpRequest::create() {
     try {
@@ -70,13 +37,14 @@ HttpRequest::create() {
                       << " not allowed");
         }
 
+        http_version_.major_ = context_->http_version_major_;
+        http_version_.minor_ = context_->http_version_minor_;
+
         // Check if the HTTP version is allowed for this request.
-        if (!inRequiredSet(HttpVersion(context_->http_version_major_,
-                                       context_->http_version_minor_),
-                           required_versions_)) {
+        if (!inRequiredSet(http_version_, required_versions_)) {
             isc_throw(BadValue, "use of HTTP version "
-                      << context_->http_version_major_ << "."
-                      << context_->http_version_minor_
+                      << http_version_.major_ << "."
+                      << http_version_.minor_
                       << " not allowed");
         }
 
@@ -129,10 +97,9 @@ HttpRequest::finalize() {
         create();
     }
 
-    // In this specific case, we don't need to do anything because the
-    // body is retrieved from the context object directly. We also don't
-    // know what type of body we have received. Derived classes should
-    // override this method and handle various types of bodies.
+    // Copy the body from the context. Derive classes may further
+    // interpret the body contents, e.g. against the Content-Type.
+    body_ = context_->body_;
     finalized_ = true;
 }
 
@@ -142,6 +109,7 @@ HttpRequest::reset() {
     finalized_ = false;
     method_ = HttpRequest::Method::HTTP_METHOD_UNKNOWN;
     headers_.clear();
+    body_.clear();
 }
 
 HttpRequest::Method
@@ -156,57 +124,6 @@ HttpRequest::getUri() const {
     return (context_->uri_);
 }
 
-HttpVersion
-HttpRequest::getHttpVersion() const {
-    checkCreated();
-    return (HttpVersion(context_->http_version_major_,
-                        context_->http_version_minor_));
-}
-
-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);
-    auto header_it = headers_.find(hdr.getLowerCaseName());
-    if (header_it != headers_.end()) {
-        return (header_it->second);
-    }
-
-    // Header not found. Return null pointer.
-    return (HttpHeaderPtr());
-}
-
-std::string
-HttpRequest::getHeaderValue(const std::string& header_name) const {
-    return (getHeader(header_name)->getValue());
-}
-
-uint64_t
-HttpRequest::getHeaderValueAsUint64(const std::string& header_name) const {
-    try {
-        return (getHeader(header_name)->getUint64Value());
-
-    } catch (const std::exception& ex) {
-        // The specified header does exist, but the value is not a number.
-        isc_throw(HttpRequestError, ex.what());
-    }
-}
-
 std::string
 HttpRequest::getBody() const {
     checkFinalized();
@@ -248,32 +165,6 @@ HttpRequest::isPersistent() const {
             ((HttpVersion::HTTP_10() < ver) && (conn_value.empty() || (conn_value != "close"))));
 }
 
-void
-HttpRequest::checkCreated() const {
-    if (!created_) {
-        isc_throw(HttpRequestError, "unable to retrieve values of HTTP"
-                  " request because the HttpRequest::create() must be"
-                  " called first. This is a programmatic error");
-    }
-}
-
-void
-HttpRequest::checkFinalized() const {
-    if (!finalized_) {
-        isc_throw(HttpRequestError, "unable to retrieve body of HTTP"
-                  " request because the HttpRequest::finalize() must be"
-                  " called first. This is a programmatic error");
-    }
-}
-
-template<typename T>
-bool
-HttpRequest::inRequiredSet(const T& element,
-                           const std::set<T>& element_set) const {
-    return (element_set.empty() || element_set.count(element) > 0);
-}
-
-
 HttpRequest::Method
 HttpRequest::methodFromString(std::string method) const {
     boost::to_upper(method);
index 3824877880cd6f4ef3c5f0cea946c37ddfe2cb2a..e41c601f7dbd932c3d6215a5074946e884a01dc9 100644 (file)
@@ -7,25 +7,19 @@
 #ifndef HTTP_REQUEST_H
 #define HTTP_REQUEST_H
 
-#include <exceptions/exceptions.h>
-#include <http/http_header.h>
-#include <http/http_types.h>
+#include <http/http_message.h>
 #include <http/request_context.h>
 #include <boost/shared_ptr.hpp>
-#include <map>
-#include <set>
 #include <stdint.h>
-#include <string>
-#include <utility>
 
 namespace isc {
 namespace http {
 
 /// @brief Generic exception thrown by @ref HttpRequest class.
-class HttpRequestError : public Exception {
+class HttpRequestError : public HttpMessageError {
 public:
     HttpRequestError(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) { };
+        HttpMessageError(file, line, what) { };
 };
 
 /// @brief Exception thrown when attempt is made to retrieve a
@@ -59,7 +53,7 @@ typedef boost::shared_ptr<const HttpRequest> ConstHttpRequestPtr;
 /// @ref PostHttpRequestJson, which derives from @ref PostHttpRequest requires
 /// that the POST message includes body holding a JSON structure and provides
 /// methods to parse the JSON body.
-class HttpRequest {
+class HttpRequest : public HttpMessage {
 public:
 
     /// @brief HTTP methods.
@@ -79,13 +73,11 @@ public:
     /// Creates new context (@ref HttpRequestContext).
     HttpRequest();
 
-    /// @brief Destructor.
-    virtual ~HttpRequest();
-
     /// @brief Returns reference to the @ref HttpRequestContext.
     ///
-    /// This method is called by the @ref HttpRequestParser to retrieve the
-    /// context in which parsed data is stored.
+    /// The context holds intermediate data for creating a request. The request
+    /// parser stores parsed raw data in the context. When parsing is finished,
+    /// the data are validated and committed into the @ref HttpRequest.
     ///
     /// @return Pointer to the underlying @ref HttpRequestContext.
     const HttpRequestContextPtr& context() const {
@@ -100,42 +92,6 @@ public:
     /// @param method HTTP method allowed for the request.
     void requireHttpMethod(const HttpRequest::Method& method);
 
-    /// @brief Specifies HTTP version allowed.
-    ///
-    /// Allowed HTTP versions must be specified prior to calling @ref create
-    /// method. If no version is specified, all versions are allowed.
-    ///
-    /// @param version Version number allowed for the request.
-    void requireHttpVersion(const HttpVersion& version);
-
-    /// @brief Specifies a required HTTP header for the request.
-    ///
-    /// Required headers must be specified prior to calling @ref create method.
-    /// The specified header must exist in the received HTTP request. This puts
-    /// no requirement on the header value.
-    ///
-    /// @param header_name Required header name.
-    void requireHeader(const std::string& header_name);
-
-    /// @brief Specifies a required value of a header in the request.
-    ///
-    /// Required header values must be specified prior to calling @ref create
-    /// method. The specified header must exist and its value must be equal to
-    /// the value specified as second parameter.
-    ///
-    /// @param header_name HTTP header name.
-    /// @param header_value HTTP header valuae.
-    void requireHeaderValue(const std::string& header_name,
-                            const std::string& header_value);
-
-    /// @brief Checks if the body is required for the HTTP request.
-    ///
-    /// Current implementation simply checks if the "Content-Length" header
-    /// is required for the request.
-    ///
-    /// @return true if the body is required for this request.
-    bool requiresBody() const;
-
     /// @brief Reads parsed request from the @ref HttpRequestContext, validates
     /// the request and stores parsed information.
     ///
@@ -174,75 +130,22 @@ public:
     /// @brief Reset the state of the object.
     virtual void reset();
 
-    /// @name HTTP data accessors.
-    ///
-    //@{
     /// @brief Returns HTTP method of the request.
     Method getMethod() const;
 
     /// @brief Returns HTTP request URI.
     std::string getUri() const;
 
-    /// @brief Returns HTTP version number (major and minor).
-    HttpVersion getHttpVersion() const;
-
-    /// @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.
-    ///
-    /// @throw HttpRequestError if the header doesn't exist.
-    std::string getHeaderValue(const std::string& header_name) const;
-
-    /// @brief Returns a value of the specified HTTP header as number.
-    ///
-    /// @param header_name Name of the HTTP header.
-    ///
-    /// @throw HttpRequestError if the header doesn't exist or if the
-    /// header value is not number.
-    uint64_t getHeaderValueAsUint64(const std::string& header_name) const;
 
     /// @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.
+    /// This method is called to generate the outbound HTTP message. Make
+    /// sure to call @c finalize prior to calling this method.
     virtual std::string toString() const;
 
-    /// @brief Checks if the request has been successfully finalized.
-    ///
-    /// The request is gets finalized on successful call to
-    /// @ref HttpRequest::finalize.
-    ///
-    /// @return true if the request has been finalized, false otherwise.
-    bool isFinalized() const {
-        return (finalized_);
-    }
 
     /// @brief Checks if the client has requested persistent connection.
     ///
@@ -255,37 +158,8 @@ public:
     /// otherwise.
     bool isPersistent() const;
 
-    //@}
-
 protected:
 
-    /// @brief Checks if the @ref create was called.
-    ///
-    /// @throw HttpRequestError if @ref create wasn't called.
-    void checkCreated() const;
-
-    /// @brief Checks if the @ref finalize was called.
-    ///
-    /// @throw HttpRequestError if @ref finalize wasn't called.
-    void checkFinalized() const;
-
-    /// @brief Checks if the set is empty or the specified element belongs
-    /// to this set.
-    ///
-    /// This is a convenience method used by the class to verify that the
-    /// given HTTP method belongs to "required methods", HTTP version belongs
-    /// to "required versions" etc.
-    ///
-    /// @param element Reference to the element.
-    /// @param element_set Reference to the set of elements.
-    /// @tparam Element type, e.g. @ref Method, @ref HttpVersion etc.
-    ///
-    /// @return true if the element set is empty or if the element belongs
-    /// to the set.
-    template<typename T>
-    bool inRequiredSet(const T& element,
-                       const std::set<T>& element_set) const;
-
     /// @brief Converts HTTP method specified in textual format to @ref Method.
     ///
     /// @param method HTTP method specified in the textual format. This value
@@ -307,35 +181,9 @@ protected:
     /// If the set is empty, all methods are allowed.
     std::set<Method> required_methods_;
 
-    /// @brief Set of required HTTP versions.
-    ///
-    /// If the set is empty, all versions are allowed.
-    std::set<HttpVersion> required_versions_;
-
-    /// @brief Map of HTTP headers indexed by lower case header names.
-    typedef std::map<std::string, HttpHeaderPtr> HttpHeaderMap;
-
-    /// @brief Map holding required HTTP headers.
-    ///
-    /// The key of this map specifies the lower case HTTP header name.
-    /// If the value of the HTTP header is empty, the header is required
-    /// but the value of the header is not checked. If the value is
-    /// non-empty, the value in the HTTP request must be equal (case
-    /// insensitive) to the value in the map.
-    HttpHeaderMap required_headers_;
-
-    /// @brief Flag indicating whether @ref create was called.
-    bool created_;
-
-    /// @brief Flag indicating whether @ref finalize was called.
-    bool finalized_;
-
     /// @brief HTTP method of the request.
     Method method_;
 
-    /// @brief Parsed HTTP headers.
-    HttpHeaderMap headers_;
-
     /// @brief Pointer to the @ref HttpRequestContext holding parsed
     /// data.
     HttpRequestContextPtr context_;
index 5764b3c30036433a39fb6ac38954c079a7263bf6..774d1f5d1be86f9dd5c56fbebba7de737295b300 100644 (file)
@@ -48,7 +48,7 @@ HttpResponse::HttpResponse(const HttpVersion& version,
                            const HttpStatusCode& status_code,
                            const CallSetGenericBody& generic_body)
     : http_version_(version), status_code_(status_code), headers_(),
-      body_() {
+      body_(), context_(new HttpResponseContext()) {
     if (generic_body.set_) {
         // This currently does nothing, but it is useful to have it here as
         // an example how to implement it in the derived classes.
@@ -56,6 +56,10 @@ HttpResponse::HttpResponse(const HttpVersion& version,
     }
 }
 
+void
+HttpResponse::create() {
+}
+
 void
 HttpResponse::setBody(const std::string& body) {
     body_ = body;
index a69ecd87aee954fbffdd235e90743bb83134ab25..7dc67d21880a71efa60997c3d5fcfecb9464d28f 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <exceptions/exceptions.h>
 #include <http/http_types.h>
+#include <http/response_context.h>
 #include <boost/lexical_cast.hpp>
 #include <boost/shared_ptr.hpp>
 #include <cstdint>
@@ -115,6 +116,17 @@ public:
     /// A class having virtual methods must have a virtual destructor.
     virtual ~HttpResponse() { }
 
+    /// @brief Returns pointer to the @ref HttpResponseContext.
+    ///
+    /// The context holds intermediate data for creating a response. The response
+    /// parser stores parsed raw data in the context. When parsing is finished,
+    /// the data are validated and committed into the @ref HttpResponse.
+    ///
+    /// @return Pointer to the underlying @ref HttpResponseContext.
+    const HttpResponseContextPtr& context() const {
+        return (context_);
+    }
+
     /// @brief Adds HTTP header to the response.
     ///
     /// The "Content-Length" and "Date" headers should not be added using this
@@ -129,6 +141,12 @@ public:
         addHeaderInternal(name, value, headers_);
     }
 
+    /// @brief Reads parsed response from the @ref HttpResponseContext, validates
+    /// the response and stores parsed information.
+    ///
+    /// This method doesn't prse the HTTP response body.
+    virtual void create();
+
     /// @brief Assigns body/content to the message.
     ///
     /// @param body Body to be assigned.
@@ -234,6 +252,9 @@ private:
     /// @brief Holds the body/content.
     std::string body_;
 
+    /// @brief Pointer to the @ref HttpResponseContext holding parsed
+    /// data.
+    HttpResponseContextPtr context_;
 };
 
 } // namespace http
diff --git a/src/lib/http/response_context.h b/src/lib/http/response_context.h
new file mode 100644 (file)
index 0000000..c1c04ce
--- /dev/null
@@ -0,0 +1,42 @@
+// Copyright (C) 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef HTTP_RESPONSE_CONTEXT_H
+#define HTTP_RESPONSE_CONTEXT_H
+
+#include <http/header_context.h>
+#include <boost/shared_ptr.hpp>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace http {
+
+/// @brief HTTP response context.
+///
+/// This context is used by the @ref HttpResponseParser to store parsed
+/// data. This data is later used to create an instance of the
+/// @ref HttpResponse or its derivation.
+struct HttpResponseContext {
+    /// @brief HTTP major version number.
+    unsigned int http_version_major_;
+    /// @brief HTTP minor version number.
+    unsigned int http_version_minor_;
+    /// @brief HTTP status code.
+    uint16_t status_code_;
+    /// @brief Collection of HTTP headers.
+    std::vector<HttpHeaderContext> headers_;
+    /// @brief HTTP request body.
+    std::string body_;
+};
+
+/// @brief Pointer to the @ref HttpResponseContext.
+typedef boost::shared_ptr<HttpResponseContext> HttpResponseContextPtr;
+
+} // end of namespace http
+} // end of namespace isc
+
+#endif // endif HTTP_RESPONSE_CONTEXT_H
index 0b6374b85c6823a9b783c4ac97242d291544330e..15ce685cb1fb0b8c0fce0edbcb23944fe1dde3c6 100644 (file)
@@ -178,6 +178,7 @@ TEST_F(PostHttpRequestJsonTest, clientRequest) {
 
     ElementPtr json = Element::fromJSON(json_body_);
     request_.setBodyAsJson(json);
+
     // Commit and validate the data.
     ASSERT_NO_THROW(request_.finalize());
 
index 22b69a189f1c77d11a04816fdb5330016920b835..4a85f623dad4b9be1e71d33298e1d911bde25062 100644 (file)
@@ -77,7 +77,7 @@ TEST_F(HttpRequestTest, minimal) {
     EXPECT_EQ(1, request_.getHttpVersion().minor_);
 
     EXPECT_THROW(request_.getHeaderValue("Content-Length"),
-                 HttpRequestNonExistingHeader);
+                 HttpMessageNonExistingHeader);
 }
 
 TEST_F(HttpRequestTest, includeHeaders) {
@@ -161,15 +161,15 @@ TEST_F(HttpRequestTest, notCreated) {
     addHeaderToContext("Content-Type", "text/html");
     addHeaderToContext("Content-Length", "1024");
 
-    EXPECT_THROW(static_cast<void>(request_.getMethod()), HttpRequestError);
+    EXPECT_THROW(static_cast<void>(request_.getMethod()), HttpMessageError);
     EXPECT_THROW(static_cast<void>(request_.getHttpVersion()),
-                 HttpRequestError);
-    EXPECT_THROW(static_cast<void>(request_.getUri()), HttpRequestError);
+                 HttpMessageError);
+    EXPECT_THROW(static_cast<void>(request_.getUri()), HttpMessageError);
     EXPECT_THROW(static_cast<void>(request_.getHeaderValue("Content-Type")),
-                 HttpRequestError);
+                 HttpMessageError);
     EXPECT_THROW(static_cast<void>(request_.getHeaderValueAsUint64("Content-Length")),
-                 HttpRequestError);
-    EXPECT_THROW(static_cast<void>(request_.getBody()), HttpRequestError);
+                 HttpMessageError);
+    EXPECT_THROW(static_cast<void>(request_.getBody()), HttpMessageError);
 
     ASSERT_NO_THROW(request_.finalize());