}
// This will generate the response holding JSON content.
HttpResponsePtr response(new HttpResponseJson(http_version, status_code));
+ response->finalize();
return (response);
}
HttpResponseJsonPtr http_response = boost::dynamic_pointer_cast<
HttpResponseJson>(createStockHttpResponse(request, HttpStatusCode::OK));
http_response->setBodyAsJson(response);
+ http_response->finalize();
return (http_response);
}
#ifndef HTTP_HEADER_CONTEXT_H
#define HTTP_HEADER_CONTEXT_H
+#include <boost/lexical_cast.hpp>
+#include <cstdint>
#include <string>
namespace isc {
HttpHeaderContext(const std::string& name, const std::string& value)
: name_(name), value_(value) {
}
+
+ /// @brief Constructor.
+ ///
+ /// @param name Header name.
+ /// @param value Numeric value for the header.
+ HttpHeaderContext(const std::string& name, const int64_t value)
+ : name_(name), value_(boost::lexical_cast<std::string>(value)) {
+ }
};
} // namespace http
namespace isc {
namespace http {
-HttpMessage::HttpMessage()
- : required_versions_(), http_version_(HttpVersion::HTTP_10()),
- required_headers_(), created_(false), finalized_(false), headers_(),
- body_() {
+HttpMessage::HttpMessage(const HttpMessage::Direction& direction)
+ : direction_(direction), required_versions_(),
+ http_version_(HttpVersion::HTTP_10()), required_headers_(),
+ created_(false), finalized_(false), headers_() {
}
HttpMessage::~HttpMessage() {
#include <http/http_types.h>
#include <map>
#include <set>
-#include <stdint.h>
+#include <cstdint>
#include <string>
namespace isc {
/// @brief Base class for @ref HttpRequest and @ref HttpResponse.
+///
+/// This abstract class provides a common functionality for the HTTP
+/// requests and responses. Each such message can be marked as outbound
+/// or inbound. An HTTP inbound request is the one received by the server
+/// and HTTP inbound response is the response received by the client.
+/// Conversely, an HTTP outbound request is the request created by the
+/// client and HTTP outbound response is the response created by the
+/// server. There are differences in how the inbound and outbound
+/// messages are created. The inbound messages are received over the
+/// TCP sockets and parsed by the parsers. The parsed information is
+/// stored in a context, i.e. structure holding raw information and
+/// associated with the given @c HttpMessage instance. Once the message
+/// is parsed and all required information is stored in the context,
+/// the @c create method is called to validate and fetch information
+/// from the context into the message. The @c finalize method is called
+/// to commit the HTTP message body into the message.
+///
+/// The outbound message is created locally from the known data, e.g.
+/// HTTP version number, URI, method etc. The headers can be then
+/// appended to the message via the context. In order to use this message
+/// the @c finalize method must be called to commit this information.
+/// Them, @c toString method can be called to generate the message in
+/// the textual form, which can be transferred via TCP socket.
class HttpMessage {
public:
+ /// @brief Specifies the direction of the HTTP message.
+ enum Direction {
+ INBOUND,
+ OUTBOUND
+ };
+
/// @brief Constructor.
- HttpMessage();
+ ///
+ /// @param direction Direction of the message (inbound or outbound).
+ explicit HttpMessage(const Direction& direction);
/// @brief Destructor.
virtual ~HttpMessage();
+ /// @brief Returns HTTP message direction.
+ Direction getDirection() const {
+ return (direction_);
+ }
+
+ /// @brief Sets direction for the HTTP message.
+ ///
+ /// This is mostly useful in unit testing.
+ ///
+ /// @param direction New direction of the HTTP message.
+ void setDirection(const Direction& direction) {
+ direction_ = direction;
+ }
+
/// @brief Specifies HTTP version allowed.
///
/// Allowed HTTP versions must be specified prior to calling @ref create
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.
///
/// @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.
+ /// @tparam T Element type, @ref HttpVersion etc.
///
/// @return true if the element set is empty or if the element belongs
/// to the set.
return (element_set.empty() || element_set.count(element) > 0);
}
+ /// @brief Message direction (inbound or outbound).
+ Direction direction_;
+
/// @brief Set of required HTTP versions.
///
/// If the set is empty, all versions are allowed.
/// @brief Parsed HTTP headers.
HttpHeaderMap headers_;
-
- /// @brief HTTP body as string.
- std::string body_;
-
};
} // end of namespace isc::http
-// 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
class PostHttpRequest : public HttpRequest {
public:
- /// @brief Constructor.
+ /// @brief Constructor for inbound HTTP request.
PostHttpRequest();
};
class PostHttpRequestJson : public PostHttpRequest {
public:
- /// @brief Constructor.
- PostHttpRequestJson();
+ /// @brief Constructor for inbound HTTP request.
+ explicit PostHttpRequestJson();
/// @brief Complete parsing of the HTTP request.
///
#include <boost/lexical_cast.hpp>
#include <sstream>
+namespace {
+
+/// @brief New line (CRLF).
+const std::string crlf = "\r\n";
+
+}
+
namespace isc {
namespace http {
HttpRequest::HttpRequest()
- : HttpMessage(), required_methods_(),
+ : HttpMessage(INBOUND), required_methods_(),
+ method_(Method::HTTP_METHOD_UNKNOWN),
+ context_(new HttpRequestContext()) {
+}
+
+HttpRequest::HttpRequest(const Method& method, const std::string& uri,
+ const HttpVersion& version)
+ : HttpMessage(OUTBOUND), required_methods_(),
method_(Method::HTTP_METHOD_UNKNOWN),
context_(new HttpRequestContext()) {
+ context()->method_ = methodToString(method);
+ context()->uri_ = uri;
+ context()->http_version_major_ = version.major_;
+ context()->http_version_minor_ = version.minor_;
}
void
headers_[hdr->getLowerCaseName()] = hdr;
}
- if (!context_->body_.empty() && (headers_.count("content-length") == 0)) {
+ if (getDirection() == HttpMessage::OUTBOUND) {
HttpHeaderPtr hdr(new HttpHeader("Content-Length",
boost::lexical_cast<std::string>(context_->body_.length())));
headers_["content-length"] = hdr;
// 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;
}
finalized_ = false;
method_ = HttpRequest::Method::HTTP_METHOD_UNKNOWN;
headers_.clear();
- body_.clear();
}
HttpRequest::Method
std::ostringstream s;
s << methodToString(getMethod()) << " " << getUri() << " HTTP/" <<
- getHttpVersion().major_ << "." << getHttpVersion().minor_ << "\r\n";
+ getHttpVersion().major_ << "." << getHttpVersion().minor_ << crlf;
for (auto header_it = headers_.cbegin(); header_it != headers_.cend();
++header_it) {
s << header_it->second->getName() << ": " << header_it->second->getValue()
- << "\r\n";
+ << crlf;
}
- s << "\r\n";
+ s << crlf;
s << getBody();
bool
HttpRequest::isPersistent() const {
+ if (getDirection() == OUTBOUND) {
+ isc_throw(InvalidOperation, "can't call isPersistent for the outbound request");
+ }
+
HttpHeaderPtr conn = getHeaderSafe("connection");
std::string conn_value;
if (conn) {
#include <http/http_message.h>
#include <http/request_context.h>
#include <boost/shared_ptr.hpp>
-#include <stdint.h>
+#include <cstdint>
namespace isc {
namespace http {
HttpMessageError(file, line, what) { };
};
-/// @brief Exception thrown when attempt is made to retrieve a
-/// non-existing header.
-class HttpRequestNonExistingHeader : public HttpRequestError {
-public:
- HttpRequestNonExistingHeader(const char* file, size_t line,
- const char* what) :
- HttpRequestError(file, line, what) { };
-};
-
class HttpRequest;
/// @brief Pointer to the @ref HttpRequest object.
/// @brief Represents HTTP request message.
///
-/// This object represents parsed HTTP message. The @ref HttpRequestContext
-/// contains raw data used as input for this object. This class interprets the
-/// data. In particular, it verifies that the appropriate method, HTTP version,
-/// and headers were used. The derivations of this class provide specializations
-/// and specify the HTTP methods, versions and headers supported/required in
-/// the specific use cases.
+/// This derivation of the @c HttpMessage class is specialized to represent
+/// HTTP requests. This class provides two constructors for creating an inbound
+/// and outbound request instance respectively. This class is associated with
+/// an instance of the @c HttpRequestContext, which is used to provide request
+/// specific values, such as: HTTP method, version, URI and headers.
///
-/// For example, the @ref PostHttpRequest class derives from @ref HttpRequest
-/// and it requires that parsed messages use POST method. The
-/// @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.
+/// The derivations of this class provide specializations and specify the
+/// HTTP methods, versions and headers supported/required in the specific use
+/// cases. For example, the @c PostHttpRequest class derives from @c HttpRequest
+/// and it requires that request uses POST method. The @c PostHttpRequestJson,
+/// which derives from @c PostHttpRequest requires that the POST message
+/// includes body holding a JSON structure and provides methods to parse the
+/// JSON body.
class HttpRequest : public HttpMessage {
public:
HTTP_METHOD_UNKNOWN
};
- /// @brief Constructor.
- ///
- /// Creates new context (@ref HttpRequestContext).
+ /// @brief Constructor for inbound HTTP request.
HttpRequest();
- /// @brief Returns reference to the @ref HttpRequestContext.
+ /// @brief Constructor for oubtound HTTP request.
+ ///
+ /// @param method HTTP method, e.g. POST.
+ /// @param uri URI.
+ /// @param version HTTP version.
+ HttpRequest(const Method& method, const std::string& uri, const HttpVersion& version);
+
+ /// @brief Returns pointer to the @ref HttpRequestContext.
///
/// 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.
+ /// the data are validated and committed into the @c HttpRequest.
///
/// @return Pointer to the underlying @ref HttpRequestContext.
const HttpRequestContextPtr& context() const {
/// @param method HTTP method allowed for the request.
void requireHttpMethod(const HttpRequest::Method& method);
- /// @brief Reads parsed request from the @ref HttpRequestContext, validates
- /// the request and stores parsed information.
+ /// @brief Commits information held in the context into the request.
///
- /// This method must be called before retrieving parsed data using accessors
- /// such as @ref getMethod, @ref getUri etc.
- ///
- /// This method doesn't parse the HTTP request body.
+ /// This function reads HTTP method, version and headers from the context
+ /// and validates their values. For the outbound messages, it automatically
+ /// appends Content-Length header to the request, based on the length of the
+ /// request body.
///
/// @throw HttpRequestError if the parsed request doesn't meet the specified
/// requirements for it.
virtual void create();
- /// @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
- /// done, the caller can check if the "Content-Length" was specified and use
- /// it's value to determine the size of the body which is parsed in the
- /// second stage.
- ///
- /// This method generally performs the body parsing, but if it determines
- /// that the @ref create method hasn't been called, it calls @ref create
- /// before parsing the body.
+ /// @brief Completes creation of the HTTP request.
///
- /// 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.
+ /// This method marks the message as finalized. The outbound request may now be
+ /// sent over the TCP socket. The information from the inbound message may be
+ /// read, including the request body.
virtual void finalize();
/// @brief Reset the state of the object.
/// @brief Returns HTTP message body as string.
std::string getBody() const;
- /// @brief Returns HTTP message as text.
+ /// @brief Returns HTTP message as string.
///
/// 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 client has requested persistent connection.
///
/// For the HTTP/1.0 case, the connection is persistent if the client has
///
/// @return true if the client has requested persistent connection, false
/// otherwise.
+ /// @throw InvalidOperation if the method is called for the outbound message.
bool isPersistent() const;
protected:
namespace isc {
namespace http {
+HttpResponse::HttpResponse()
+ : HttpMessage(INBOUND), context_(new HttpResponseContext()) {
+}
+
HttpResponse::HttpResponse(const HttpVersion& version,
const HttpStatusCode& status_code,
const CallSetGenericBody& generic_body)
- : http_version_(version), status_code_(status_code), headers_(),
- body_(), context_(new HttpResponseContext()) {
+ : HttpMessage(OUTBOUND), context_(new HttpResponseContext()) {
+ context_->http_version_major_ = version.major_;
+ context_->http_version_minor_ = version.minor_;
+ context_->status_code_ = static_cast<uint16_t>(status_code);
+
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.
void
HttpResponse::create() {
+ try {
+ 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(http_version_, required_versions_)) {
+ isc_throw(BadValue, "use of HTTP version "
+ << http_version_.major_ << "."
+ << http_version_.minor_
+ << " not allowed");
+ }
+
+ // Copy headers from the context.
+ for (auto header = context_->headers_.begin();
+ header != context_->headers_.end();
+ ++header) {
+ HttpHeaderPtr hdr(new HttpHeader(header->name_, header->value_));
+ headers_[hdr->getLowerCaseName()] = hdr;
+ }
+
+ if (getDirection() == HttpMessage::OUTBOUND) {
+ HttpHeaderPtr length_header(new HttpHeader("Content-Length", boost::lexical_cast<std::string>
+ (context_->body_.length())));
+ headers_["content-length"] = length_header;
+
+ HttpHeaderPtr date_header(new HttpHeader("Date", getDateHeaderValue()));;
+ headers_["date"] = date_header;
+ }
+
+ // Iterate over required headers and check that they exist
+ // in the HTTP response.
+ for (auto req_header = required_headers_.begin();
+ req_header != required_headers_.end();
+ ++req_header) {
+ auto header = headers_.find(req_header->first);
+ if (header == headers_.end()) {
+ isc_throw(BadValue, "required header " << req_header->first
+ << " not found in the HTTP response");
+ } else if (!req_header->second->getValue().empty() &&
+ !header->second->isValueEqual(req_header->second->getValue())) {
+ // If specific value is required for the header, check
+ // that the value in the HTTP response matches it.
+ isc_throw(BadValue, "required header's " << header->first
+ << " value is " << req_header->second->getValue()
+ << ", but " << header->second->getValue() << " was found");
+ }
+ }
+
+ } catch (const std::exception& ex) {
+ // Reset the state of the object if we failed at any point.
+ reset();
+ isc_throw(HttpResponseError, ex.what());
+ }
+
+ // All ok.
+ created_ = true;
+}
+
+void
+HttpResponse::finalize() {
+ if (!created_) {
+ create();
+ }
+
+ finalized_ = true;
}
void
-HttpResponse::setBody(const std::string& body) {
- body_ = body;
+HttpResponse::reset() {
+ created_ = false;
+ finalized_ = false;
+ headers_.clear();
+}
+
+HttpStatusCode
+HttpResponse::getStatusCode() const {
+ checkCreated();
+ return (static_cast<HttpStatusCode>(context_->status_code_));
+}
+
+std::string
+HttpResponse::getBody() const {
+ checkFinalized();
+ return (context_->body_);
}
bool
std::string
HttpResponse::toBriefString() const {
+ checkFinalized();
+
std::ostringstream s;
// HTTP version number and status code.
s << "HTTP/" << http_version_.major_ << "." << http_version_.minor_;
- s << " " << static_cast<uint16_t>(status_code_);
- s << " " << statusCodeToString(status_code_) << crlf;
+ s << " " << context_->status_code_;
+ s << " " << statusCodeToString(static_cast<HttpStatusCode>(context_->status_code_)) << crlf;
return (s.str());
}
std::string
HttpResponse::toString() const {
+
std::ostringstream s;
// HTTP version number and status code.
s << toBriefString();
- // We need to at least insert "Date" header into the HTTP headers. This
- // method is const thus we can't insert it into the headers_ map. We'll
- // work on the copy of the map. Admittedly, we could just append "Date"
- // into the generated string but we prefer that headers are ordered
- // alphabetically.
- std::map<std::string, std::string> headers(headers_);
-
- // Update or add "Date" header.
- addHeaderInternal("Date", getDateHeaderValue(), headers);
-
- // Always add "Content-Length", perhaps equal to 0.
- addHeaderInternal("Content-Length", body_.length(), headers);
-
- // Include all headers.
- for (auto header = headers.cbegin(); header != headers.cend();
- ++header) {
- s << header->first << ": " << header->second << crlf;
+ for (auto header_it = headers_.cbegin(); header_it != headers_.cend();
+ ++header_it) {
+ s << header_it->second->getName() << ": " << header_it->second->getValue()
+ << crlf;
}
s << crlf;
// Include message body.
- s << body_;
+ s << getBody();
return (s.str());
}
#ifndef HTTP_RESPONSE_H
#define HTTP_RESPONSE_H
-#include <exceptions/exceptions.h>
-#include <http/http_types.h>
+#include <http/header_context.h>
+#include <http/http_message.h>
#include <http/response_context.h>
#include <boost/lexical_cast.hpp>
#include <boost/shared_ptr.hpp>
-#include <cstdint>
-#include <map>
-#include <string>
+#include <vector>
namespace isc {
namespace http {
/// @brief Generic exception thrown by @ref HttpResponse class.
-class HttpResponseError : public Exception {
+class HttpResponseError : public HttpMessageError {
public:
HttpResponseError(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) { };
+ HttpMessageError(file, line, what) { };
};
/// @brief HTTP status codes (cf RFC 2068)
/// @brief Represents HTTP response message.
///
-/// This class represents HTTP response message. An instance of this object
-/// or its derivation is typically created by the implementation of the
-/// @ref HttpResponseCreator interface.
+/// This derivation of the @c HttpMessage class is specialized to represent
+/// HTTP responses. This class provides two constructors for creating an inbound
+/// and outbound response instance respectively. This class is associated with
+/// an instance of the @c HttpResponseContext, which is used to provide response
+/// specific values, such as HTTP status and headers.
///
-/// It contains @c toString method generating a textual representation of
-/// the HTTP response, which is send to the client over TCP socket.
-class HttpResponse {
+/// The derivations of this class provide specializations and specify the HTTP
+/// versions and headers supported/required in the specific use cases. For example,
+/// the @c HttpResponseJson class derives from the @c HttpResponse and it requires
+/// that response includes a body in the JSON format.
+class HttpResponse : public HttpMessage {
public:
- /// @brief Constructor.
+ /// @brief Constructor for the inbound HTTP response.
+ explicit HttpResponse();
+
+
+ /// @brief Constructor for outbound HTTP response.
///
/// Creates basic instance of the object. It sets the HTTP version and the
/// status code to be included in the response.
const CallSetGenericBody& generic_body =
CallSetGenericBody::yes());
- /// @brief Destructor.
- ///
- /// 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.
+ /// the data are validated and committed into the @c HttpResponse.
///
/// @return Pointer to the underlying @ref HttpResponseContext.
const HttpResponseContextPtr& context() const {
return (context_);
}
- /// @brief Adds HTTP header to the response.
+ /// @brief Commits information held in the context into the response.
///
- /// The "Content-Length" and "Date" headers should not be added using this
- /// method because they are generated and added automatically when the
- /// @c toString is called.
- ///
- /// @param name Header name.
- /// @param value Header value.
- /// @tparam ValueType Type of the header value.
- template<typename ValueType>
- void addHeader(const std::string& name, const ValueType& value) {
- 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.
+ /// This function reads HTTP version, status code and headers from the
+ /// context and validates their values. For the outbound messages, it
+ /// automatically appends Content-Length and Date headers to the response.
+ /// The Content-Length is set to the body size. The Date is set to the
+ /// current date and time.
virtual void create();
- /// @brief Assigns body/content to the message.
+ /// @brief Completes creation of the HTTP response.
///
- /// @param body Body to be assigned.
- void setBody(const std::string& body);
+ /// This method marks the response as finalized. The outbound response may now
+ /// be sent over the TCP socket. The information from the inbound message may
+ /// be read, including the response body.
+ virtual void finalize();
+
+ /// @brief Reset the state of the object.
+ virtual void reset();
+
+ /// @brief Returns HTTP status code.
+ HttpStatusCode getStatusCode() const;
+
+ /// @brief Returns HTTP response body as string.
+ virtual std::string getBody() const;
/// @brief Checks if the status code indicates client error.
///
/// @brief Returns HTTP version and HTTP status as a string.
std::string toBriefString() const;
- /// @brief Returns textual representation of the HTTP response.
- ///
- /// It includes the "Date" header with the current time in RFC 1123 format.
- /// It also includes "Content-Length" when the response has a non-empty
- /// body.
+ /// @brief Returns HTTP response as string.
///
- /// @return Textual representation of the HTTP response.
- std::string toString() const ;
+ /// This method is called to generate the outbound HTTP response. Make
+ /// sure to call @c finalize prior to calling this method.
+ virtual std::string toString() const;
protected:
- /// @brief Adds HTTP header to the map.
- ///
- /// @param name Header name.
- /// @param value Header value.
- /// @param [out] headers A map to which header value should be inserted.
- /// @tparam ValueType Type of the header value.
- template<typename ValueType>
- void addHeaderInternal(const std::string& name, const ValueType& value,
- std::map<std::string, std::string>& headers) const {
- try {
- headers[name] = boost::lexical_cast<std::string>(value);
-
- } catch (const boost::bad_lexical_cast& ex) {
- isc_throw(HttpResponseError, "unable to convert the "
- << name << " header value to a string");
- }
- }
-
/// @brief Returns current time formatted as required by RFC 1123.
///
/// This method is virtual so as it can be overridden in unit tests
/// generated.
void setGenericBody(const HttpStatusCode& /*status_code*/) { };
- /// @brief Holds HTTP version for the response.
- HttpVersion http_version_;
-
- /// @brief Holds status code for the response.
- HttpStatusCode status_code_;
-
- /// @brief Holds HTTP headers for the response.
- std::map<std::string, std::string> headers_;
-
- /// @brief Holds the body/content.
- std::string body_;
-
/// @brief Pointer to the @ref HttpResponseContext holding parsed
/// data.
HttpResponseContextPtr context_;
/// @brief HTTP response context.
///
-/// This context is used by the @ref HttpResponseParser to store parsed
+/// This context is used by the @c HttpResponseParser to store parsed
/// data. This data is later used to create an instance of the
-/// @ref HttpResponse or its derivation.
+/// @c HttpResponse or its derivation.
struct HttpResponseContext {
/// @brief HTTP major version number.
unsigned int http_version_major_;
namespace isc {
namespace http {
+HttpResponseJson::HttpResponseJson()
+ : HttpResponse() {
+ context()->headers_.push_back(HttpHeaderContext("Content-Type", "application/json"));
+}
+
+
HttpResponseJson::HttpResponseJson(const HttpVersion& version,
const HttpStatusCode& status_code,
const CallSetGenericBody& generic_body)
: HttpResponse(version, status_code, CallSetGenericBody::no()) {
- addHeader("Content-Type", "application/json");
+ context()->headers_.push_back(HttpHeaderContext("Content-Type", "application/json"));
// This class provides its own implementation of the setGenericBody.
// We call it here unless the derived class calls this constructor
// from its own constructor and indicates that we shouldn't set the
void
HttpResponseJson::setBodyAsJson(const ConstElementPtr& json_body) {
- setBody(json_body->str());
+ if (json_body) {
+ context()->body_ = json_body->str();
+
+ } else {
+ context()->body_.clear();
+ }
+
+ json_ = json_body;
}
class HttpResponseJson : public HttpResponse {
public:
- /// @brief Constructor.
+ /// @brief Constructor for the inbound HTTP response.
+ explicit HttpResponseJson();
+
+ /// @brief Constructor for the outbound HTTP response.
///
/// @param version HTTP version.
/// @param status_code HTTP status code.
/// @param status_code Status code for which the body should be
/// generated.
void setGenericBody(const HttpStatusCode& status_code);
+
+protected:
+
+ /// @brief Pointer to the parsed JSON body.
+ data::ConstElementPtr json_;
};
-}
-}
+} // end of namespace isc::http
+} // end of namespace isc
#endif
request->context()->http_version_minor_);
// This will generate the response holding JSON content.
ResponsePtr response(new Response(http_version, status_code));
+ response->finalize();
return (response);
}
// We don't need content to test our class.
ResponsePtr response(new Response(request->getHttpVersion(),
HttpStatusCode::OK));
+ response->finalize();
return (response);
}
};
// This test verifies that it is possible to create client side request
// containing JSON body.
TEST_F(PostHttpRequestJsonTest, clientRequest) {
+ request_.setDirection(HttpMessage::OUTBOUND);
+
setContextBasics("POST", "/isc/org", HttpVersion(1, 0));
addHeaderToContext("Content-Type", "application/json");
template<typename ValueType>
void addHeaderToContext(const std::string& header_name,
const ValueType& header_value) {
- request_.context()->headers_.push_back(HttpHeaderContext());
- request_.context()->headers_.back().name_ = header_name;
- request_.context()->headers_.back().value_ =
- boost::lexical_cast<std::string>(header_value);
+ request_.context()->headers_.push_back(HttpHeaderContext(header_name, header_value));
}
/// @brief Instance of the @ref HttpRequest or its derivation.
}
TEST_F(HttpRequestTest, clientRequest) {
+ request_.setDirection(HttpMessage::OUTBOUND);
setContextBasics("POST", "/isc/org", HttpVersion(1, 0));
// Capture current date and time.
request->context()->http_version_minor_);
// This will generate the response holding JSON content.
ResponsePtr response(new Response(http_version, status_code));
+ response->finalize();
return (response);
}
// We don't need content to test our class.
ResponsePtr response(new Response(request->getHttpVersion(),
HttpStatusCode::OK));
+ response->finalize();
return (response);
}
};
void testGenericResponse(const HttpStatusCode& status_code,
const std::string& status_message) {
TestHttpResponseJson response(HttpVersion(1, 0), status_code);
+ ASSERT_NO_THROW(response.finalize());
std::ostringstream status_message_json;
// Build the expected content.
status_message_json << "{ \"result\": "
TEST_F(HttpResponseJsonTest, responseWithContent) {
TestHttpResponseJson response(HttpVersion(1, 1), HttpStatusCode::OK);
ASSERT_NO_THROW(response.setBodyAsJson(json_));
+ ASSERT_NO_THROW(response.finalize());
std::ostringstream response_string;
response_string <<
// it returns the fixed value of the Date header, which is
// very useful in unit tests.
TestHttpResponse response(HttpVersion(1, 0), status_code);
- response.addHeader("Content-Type", "text/html");
+ response.context()->headers_.push_back(HttpHeaderContext("Content-Type", "text/html"));
+ ASSERT_NO_THROW(response.finalize());
std::ostringstream response_string;
response_string << "HTTP/1.0 " << static_cast<uint16_t>(status_code)
<< " " << status_message << "\r\n";
// Create the message and add some headers.
TestHttpResponse response(HttpVersion(1, 0), HttpStatusCode::OK);
- response.addHeader("Content-Type", "text/html");
- response.addHeader("Host", "kea.example.org");
- response.setBody(sample_body);
+ response.context()->headers_.push_back(HttpHeaderContext("Content-Type", "text/html"));
+ response.context()->headers_.push_back(HttpHeaderContext("Host", "kea.example.org"));
+ response.context()->body_ = sample_body;
+ ASSERT_NO_THROW(response.finalize());
// Create a string holding expected response. Note that the Date
// is a fixed value returned by the customized TestHttpResponse