diff options
Diffstat (limited to 'src/lib/http/response.h')
-rw-r--r-- | src/lib/http/response.h | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/src/lib/http/response.h b/src/lib/http/response.h new file mode 100644 index 0000000000..a69ecd87ae --- /dev/null +++ b/src/lib/http/response.h @@ -0,0 +1,242 @@ +// 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef HTTP_RESPONSE_H +#define HTTP_RESPONSE_H + +#include <exceptions/exceptions.h> +#include <http/http_types.h> +#include <boost/lexical_cast.hpp> +#include <boost/shared_ptr.hpp> +#include <cstdint> +#include <map> +#include <string> + +namespace isc { +namespace http { + +/// @brief Generic exception thrown by @ref HttpResponse class. +class HttpResponseError : public Exception { +public: + HttpResponseError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief HTTP status codes (cf RFC 2068) +enum class HttpStatusCode : std::uint16_t { + OK = 200, + CREATED = 201, + ACCEPTED = 202, + NO_CONTENT = 204, + MULTIPLE_CHOICES = 300, + MOVED_PERMANENTLY = 301, + MOVED_TEMPORARILY = 302, + NOT_MODIFIED = 304, + BAD_REQUEST = 400, + UNAUTHORIZED = 401, + FORBIDDEN = 403, + NOT_FOUND = 404, + REQUEST_TIMEOUT = 408, + INTERNAL_SERVER_ERROR = 500, + NOT_IMPLEMENTED = 501, + BAD_GATEWAY = 502, + SERVICE_UNAVAILABLE = 503 +}; + +/// @brief Encapsulates the boolean value indicating if the @ref HttpResponse +/// constructor should call its @c setGenericBody method during construction. +struct CallSetGenericBody { + + /// @brief Constructor. + /// + /// @param set A boolean value indicating if the method should be called + /// or not. + explicit CallSetGenericBody(const bool set) + : set_(set) { + } + + /// @brief Returns encapsulated true. + static const CallSetGenericBody& yes() { + static CallSetGenericBody yes(true); + return (yes); + } + + /// @brief Returns encapsulated false. + static const CallSetGenericBody& no() { + static CallSetGenericBody no(false); + return (no); + } + + /// @brief A storage for the boolean flag. + bool set_; +}; + +class HttpResponse; + +/// @brief Pointer to the @ref HttpResponse object. +typedef boost::shared_ptr<HttpResponse> HttpResponsePtr; + +/// @brief Pointer to the const @ref HttpResponse object. +typedef boost::shared_ptr<const HttpResponse> ConstHttpResponsePtr; + +/// @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. +/// +/// It contains @c toString method generating a textual representation of +/// the HTTP response, which is send to the client over TCP socket. +class HttpResponse { +public: + + /// @brief Constructor. + /// + /// Creates basic instance of the object. It sets the HTTP version and the + /// status code to be included in the response. + /// + /// @param version HTTP version. + /// @param status_code HTTP status code. + /// @param generic_body Indicates if the constructor should call + /// @c setGenericBody to create a generic content for the given + /// status code. This should be set to "no" when the constructor is + /// called by the derived class which provides its own implementation + /// of the @c setGenericBody method. + explicit HttpResponse(const HttpVersion& version, + const HttpStatusCode& status_code, + const CallSetGenericBody& generic_body = + CallSetGenericBody::yes()); + + /// @brief Destructor. + /// + /// A class having virtual methods must have a virtual destructor. + virtual ~HttpResponse() { } + + /// @brief Adds HTTP header to 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 Assigns body/content to the message. + /// + /// @param body Body to be assigned. + void setBody(const std::string& body); + + /// @brief Checks if the status code indicates client error. + /// + /// @param status_code HTTP status code. + /// @return true if the status code indicates client error. + static bool isClientError(const HttpStatusCode& status_code); + + /// @brief Checks if the status code indicates server error. + /// + /// @param status_code HTTP status code. + /// @return true if the status code indicates server error. + static bool isServerError(const HttpStatusCode& status_code); + + /// @brief Converts status code to string. + /// + /// @param status_code HTTP status code. + /// @return Textual representation of the status code. + static std::string statusCodeToString(const HttpStatusCode& status_code); + + /// @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. + /// + /// @return Textual representation of the HTTP response. + 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 + /// to return a "predictable" value of time, e.g. constant value. + /// + /// @return Current time formatted as required by RFC 1123. + virtual std::string getDateHeaderValue() const; + + /// @brief Convenience method converting status code to numeric value. + /// + /// @param status_code Status code represented as enum. + /// @return Numeric representation of the status code. + static uint16_t statusCodeToNumber(const HttpStatusCode& status_code); + +private: + + /// @brief Sets generic body for the given status code. + /// + /// Most of the classes derived from @ref HttpResponse will expect + /// a certain content type. Depending on the content type used they + /// will use different body formats for error messages. For example, + /// a response using text/html will use HTML within the response + /// body. The application/json will use JSON body etc. There is a + /// need to implement class specific way of generating the body + /// for error messages. Thus, each derivation of this class is + /// required to implement class specific @ref setGenericBody function + /// which should be called in the class constructor. + /// + /// This is also the case for this class, though the implementation + /// of @c setGenericBody is currently no-op. + /// + /// Note that this class can't be declared virtual because it is + /// meant to be called from the class constructor. + /// + /// @param status_code Status code for which the body should be + /// 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_; + +}; + +} // namespace http +} // namespace isc + +#endif |