summaryrefslogtreecommitdiffstats
path: root/src/lib/http/response.h
blob: 734d92fd801249c30461cf7017483f77f03724f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
// Copyright (C) 2016-2022 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 <cc/data.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 <string>
#include <vector>

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<HttpResponse> HttpResponsePtr;

/// @brief Pointer to the const @ref HttpResponse object.
typedef boost::shared_ptr<const HttpResponse> 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 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 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);

    /// @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;

    /// @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;

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