// 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 #include #include #include #include #include #include #include namespace isc { namespace http { /// @brief Generic exception thrown by @ref HttpResponse class. class HttpResponseError : public HttpMessageError { public: HttpResponseError(const char* file, size_t line, const char* what) : HttpMessageError(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 HttpResponsePtr; /// @brief Pointer to the const @ref HttpResponse object. typedef boost::shared_ptr ConstHttpResponsePtr; /// @brief Represents HTTP response message. /// /// 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. /// /// 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 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. /// /// @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 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 @c HttpResponse. /// /// @return Pointer to the underlying @ref HttpResponseContext. const HttpResponseContextPtr& context() const { return (context_); } /// @brief Commits information held in the context into the response. /// /// 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 Completes creation of the HTTP response. /// /// 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 status phrase. std::string getStatusPhrase() const; /// @brief Returns HTTP response body as string. virtual std::string getBody() const; /// @brief Retrieves JSON body. /// /// @return Pointer to the root element of the JSON structure. /// @throw HttpResponseJsonError if an error occurred. data::ConstElementPtr getBodyAsJson() const; /// @brief Retrieves a single JSON element. /// /// The element must be at top level of the JSON structure. /// /// @param element_name Element name. /// /// @return Pointer to the specified element or NULL if such element /// doesn't exist. /// @throw HttpResponseJsonError if an error occurred. data::ConstElementPtr getJsonElement(const std::string& element_name) const; /// @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 HTTP response as string. /// /// 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 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*/) { }; protected: /// @brief Pointer to the @ref HttpResponseContext holding parsed /// data. HttpResponseContextPtr context_; }; } // namespace http } // namespace isc #endif