1 // Copyright (C) 2016-2021 Internet Systems Consortium, Inc. ("ISC") 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, v. 2.0. If a copy of the MPL was not distributed with this 5 // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 7 #ifndef HTTP_RESPONSE_H 8 #define HTTP_RESPONSE_H 9 10 #include <cc/data.h> 11 #include <http/header_context.h> 12 #include <http/http_message.h> 13 #include <http/response_context.h> 14 #include <boost/lexical_cast.hpp> 15 #include <boost/shared_ptr.hpp> 16 #include <string> 17 #include <vector> 18 19 namespace isc { 20 namespace http { 21 22 /// @brief Generic exception thrown by @ref HttpResponse class. 23 class HttpResponseError : public HttpMessageError { 24 public: HttpResponseError(const char * file,size_t line,const char * what)25 HttpResponseError(const char* file, size_t line, const char* what) : 26 HttpMessageError(file, line, what) { }; 27 }; 28 29 /// @brief HTTP status codes (cf RFC 2068) 30 enum class HttpStatusCode : std::uint16_t { 31 OK = 200, 32 CREATED = 201, 33 ACCEPTED = 202, 34 NO_CONTENT = 204, 35 MULTIPLE_CHOICES = 300, 36 MOVED_PERMANENTLY = 301, 37 MOVED_TEMPORARILY = 302, 38 NOT_MODIFIED = 304, 39 BAD_REQUEST = 400, 40 UNAUTHORIZED = 401, 41 FORBIDDEN = 403, 42 NOT_FOUND = 404, 43 REQUEST_TIMEOUT = 408, 44 INTERNAL_SERVER_ERROR = 500, 45 NOT_IMPLEMENTED = 501, 46 BAD_GATEWAY = 502, 47 SERVICE_UNAVAILABLE = 503 48 }; 49 50 /// @brief Encapsulates the boolean value indicating if the @ref HttpResponse 51 /// constructor should call its @c setGenericBody method during construction. 52 struct CallSetGenericBody { 53 54 /// @brief Constructor. 55 /// 56 /// @param set A boolean value indicating if the method should be called 57 /// or not. CallSetGenericBodyCallSetGenericBody58 explicit CallSetGenericBody(const bool set) 59 : set_(set) { 60 } 61 62 /// @brief Returns encapsulated true. yesCallSetGenericBody63 static const CallSetGenericBody& yes() { 64 static CallSetGenericBody yes(true); 65 return (yes); 66 } 67 68 /// @brief Returns encapsulated false. noCallSetGenericBody69 static const CallSetGenericBody& no() { 70 static CallSetGenericBody no(false); 71 return (no); 72 } 73 74 /// @brief A storage for the boolean flag. 75 bool set_; 76 }; 77 78 class HttpResponse; 79 80 /// @brief Pointer to the @ref HttpResponse object. 81 typedef boost::shared_ptr<HttpResponse> HttpResponsePtr; 82 83 /// @brief Pointer to the const @ref HttpResponse object. 84 typedef boost::shared_ptr<const HttpResponse> ConstHttpResponsePtr; 85 86 /// @brief Represents HTTP response message. 87 /// 88 /// This derivation of the @c HttpMessage class is specialized to represent 89 /// HTTP responses. This class provides two constructors for creating an inbound 90 /// and outbound response instance respectively. This class is associated with 91 /// an instance of the @c HttpResponseContext, which is used to provide response 92 /// specific values, such as HTTP status and headers. 93 /// 94 /// The derivations of this class provide specializations and specify the HTTP 95 /// versions and headers supported/required in the specific use cases. For example, 96 /// the @c HttpResponseJson class derives from the @c HttpResponse and it requires 97 /// that response includes a body in the JSON format. 98 class HttpResponse : public HttpMessage { 99 public: 100 101 /// @brief Constructor for the inbound HTTP response. 102 explicit HttpResponse(); 103 104 105 /// @brief Constructor for outbound HTTP response. 106 /// 107 /// Creates basic instance of the object. It sets the HTTP version and the 108 /// status code to be included in the response. 109 /// 110 /// @param version HTTP version. 111 /// @param status_code HTTP status code. 112 /// @param generic_body Indicates if the constructor should call 113 /// @c setGenericBody to create a generic content for the given 114 /// status code. This should be set to "no" when the constructor is 115 /// called by the derived class which provides its own implementation 116 /// of the @c setGenericBody method. 117 explicit HttpResponse(const HttpVersion& version, 118 const HttpStatusCode& status_code, 119 const CallSetGenericBody& generic_body = 120 CallSetGenericBody::yes()); 121 122 /// @brief Returns pointer to the @ref HttpResponseContext. 123 /// 124 /// The context holds intermediate data for creating a response. The response 125 /// parser stores parsed raw data in the context. When parsing is finished, 126 /// the data are validated and committed into the @c HttpResponse. 127 /// 128 /// @return Pointer to the underlying @ref HttpResponseContext. context()129 const HttpResponseContextPtr& context() const { 130 return (context_); 131 } 132 133 /// @brief Commits information held in the context into the response. 134 /// 135 /// This function reads HTTP version, status code and headers from the 136 /// context and validates their values. For the outbound messages, it 137 /// automatically appends Content-Length and Date headers to the response. 138 /// The Content-Length is set to the body size. The Date is set to the 139 /// current date and time. 140 virtual void create(); 141 142 /// @brief Completes creation of the HTTP response. 143 /// 144 /// This method marks the response as finalized. The outbound response may now 145 /// be sent over the TCP socket. The information from the inbound message may 146 /// be read, including the response body. 147 virtual void finalize(); 148 149 /// @brief Reset the state of the object. 150 virtual void reset(); 151 152 /// @brief Returns HTTP status code. 153 HttpStatusCode getStatusCode() const; 154 155 /// @brief Returns HTTP status phrase. 156 std::string getStatusPhrase() const; 157 158 /// @brief Returns HTTP response body as string. 159 virtual std::string getBody() const; 160 161 /// @brief Retrieves a single JSON element. 162 /// 163 /// The element must be at top level of the JSON structure. 164 /// 165 /// @param element_name Element name. 166 /// 167 /// @return Pointer to the specified element or NULL if such element 168 /// doesn't exist. 169 /// @throw HttpResponseJsonError if an error occurred. 170 data::ConstElementPtr getJsonElement(const std::string& element_name) const; 171 172 /// @brief Checks if the status code indicates client error. 173 /// 174 /// @param status_code HTTP status code. 175 /// @return true if the status code indicates client error. 176 static bool isClientError(const HttpStatusCode& status_code); 177 178 /// @brief Checks if the status code indicates server error. 179 /// 180 /// @param status_code HTTP status code. 181 /// @return true if the status code indicates server error. 182 static bool isServerError(const HttpStatusCode& status_code); 183 184 /// @brief Converts status code to string. 185 /// 186 /// @param status_code HTTP status code. 187 /// @return Textual representation of the status code. 188 static std::string statusCodeToString(const HttpStatusCode& status_code); 189 190 /// @brief Returns HTTP version and HTTP status as a string. 191 std::string toBriefString() const; 192 193 /// @brief Returns HTTP response as string. 194 /// 195 /// This method is called to generate the outbound HTTP response. Make 196 /// sure to call @c finalize prior to calling this method. 197 virtual std::string toString() const; 198 199 protected: 200 201 /// @brief Returns current time formatted as required by RFC 1123. 202 /// 203 /// This method is virtual so as it can be overridden in unit tests 204 /// to return a "predictable" value of time, e.g. constant value. 205 /// 206 /// @return Current time formatted as required by RFC 1123. 207 virtual std::string getDateHeaderValue() const; 208 209 /// @brief Convenience method converting status code to numeric value. 210 /// 211 /// @param status_code Status code represented as enum. 212 /// @return Numeric representation of the status code. 213 static uint16_t statusCodeToNumber(const HttpStatusCode& status_code); 214 215 private: 216 217 /// @brief Sets generic body for the given status code. 218 /// 219 /// Most of the classes derived from @ref HttpResponse will expect 220 /// a certain content type. Depending on the content type used they 221 /// will use different body formats for error messages. For example, 222 /// a response using text/html will use HTML within the response 223 /// body. The application/json will use JSON body etc. There is a 224 /// need to implement class specific way of generating the body 225 /// for error messages. Thus, each derivation of this class is 226 /// required to implement class specific @ref setGenericBody function 227 /// which should be called in the class constructor. 228 /// 229 /// This is also the case for this class, though the implementation 230 /// of @c setGenericBody is currently no-op. 231 /// 232 /// Note that this class can't be declared virtual because it is 233 /// meant to be called from the class constructor. 234 /// 235 /// @param status_code Status code for which the body should be 236 /// generated. setGenericBody(const HttpStatusCode &)237 void setGenericBody(const HttpStatusCode& /*status_code*/) { }; 238 239 protected: 240 241 /// @brief Pointer to the @ref HttpResponseContext holding parsed 242 /// data. 243 HttpResponseContextPtr context_; 244 }; 245 246 } // namespace http 247 } // namespace isc 248 249 #endif 250