summaryrefslogtreecommitdiffstats
path: root/src/lib/http/connection.h
blob: 4597ca4b4829f5878cc17767e3e44249f19b4b3c (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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
// Copyright (C) 2017-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_CONNECTION_H
#define HTTP_CONNECTION_H

#include <asiolink/interval_timer.h>
#include <asiolink/io_service.h>
#include <http/http_acceptor.h>
#include <http/request_parser.h>
#include <http/response_creator_factory.h>
#include <boost/enable_shared_from_this.hpp>
#include <boost/system/error_code.hpp>
#include <boost/shared_ptr.hpp>
#include <array>
#include <functional>
#include <string>

namespace isc {
namespace http {

/// @brief Generic error reported within @ref HttpConnection class.
class HttpConnectionError : public Exception {
public:
    HttpConnectionError(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) { };
};

/// @brief Forward declaration to the @ref HttpConnectionPool.
///
/// This declaration is needed because we don't include the header file
/// declaring @ref HttpConnectionPool to avoid circular inclusion.
class HttpConnectionPool;

class HttpConnection;
/// @brief Pointer to the @ref HttpConnection.
typedef boost::shared_ptr<HttpConnection> HttpConnectionPtr;

/// @brief Accepts and handles a single HTTP connection.
class HttpConnection : public boost::enable_shared_from_this<HttpConnection> {
private:

    /// @brief Type of the function implementing a callback invoked by the
    /// @c SocketCallback functor.
    typedef std::function<void(boost::system::error_code ec, size_t length)>
    SocketCallbackFunction;

    /// @brief Functor associated with the socket object.
    ///
    /// This functor calls a callback function specified in the constructor.
    class SocketCallback {
    public:

        /// @brief Constructor.
        ///
        /// @param socket_callback Callback to be invoked by the functor upon
        /// an event associated with the socket.
        SocketCallback(SocketCallbackFunction socket_callback)
            : callback_(socket_callback) {
        }

        /// @brief Operator called when event associated with a socket occurs.
        ///
        /// This operator returns immediately when received error code is
        /// @c boost::system::error_code is equal to
        /// @c boost::asio::error::operation_aborted, i.e. the callback is not
        /// invoked.
        ///
        /// @param ec Error code.
        /// @param length Data length.
        void operator()(boost::system::error_code ec, size_t length = 0);

    private:
        /// @brief Supplied callback.
        SocketCallbackFunction callback_;
    };

protected:

    class Transaction;

    /// @brief Shared pointer to the @c Transaction.
    typedef boost::shared_ptr<Transaction> TransactionPtr;

    /// @brief Represents a single exchange of the HTTP messages.
    ///
    /// In HTTP/1.1 multiple HTTP message exchanges may be conducted
    /// over the same persistent connection before the connection is
    /// closed. Since ASIO handlers for these exchanges may be sometimes
    /// executed out of order, there is a need to associate the states of
    /// the exchanges with the appropriate ASIO handlers. This object
    /// represents such state and includes: received request, request
    /// parser (being in the particular state of parsing), input buffer
    /// and the output buffer.
    ///
    /// The new @c Transaction instance is created when the connection
    /// is established and the server starts receiving the HTTP request.
    /// The shared pointer to the created transaction is passed between
    /// the asynchronous handlers. Therefore, as long as the asynchronous
    /// communication is conducted the instance of the transaction is
    /// held by the IO service which runs the handlers. The transaction
    /// instance exists as long as the asynchronous handlers for the
    /// given request/response exchange are executed. When the server
    /// responds to the client and all corresponding IO handlers are
    /// invoked the transaction is automatically destroyed.
    ///
    /// The timeout may occur anytime during the transaction. In such
    /// cases, a new transaction instance is created to send the
    /// HTTP 408 (timeout) response to the client. Creation of the
    /// new transaction for the timeout response is necessary because
    /// there may be some asynchronous handlers scheduled by the
    /// original transaction which rely on the original transaction's
    /// state. The timeout response's state is held within the new
    /// transaction spawned from the original transaction.
    class Transaction {
    public:

        /// @brief Constructor.
        ///
        /// @param response_creator Pointer to the response creator being
        /// used for generating a response from the request.
        /// @param request Pointer to the HTTP request. If the request is
        /// null, the constructor creates new request instance using the
        /// provided response creator.
        Transaction(const HttpResponseCreatorPtr& response_creator,
                    const HttpRequestPtr& request = HttpRequestPtr());

        /// @brief Creates new transaction instance.
        ///
        /// It is called when the HTTP server has just scheduled asynchronous
        /// reading of the HTTP message.
        ///
        /// @param response_creator Pointer to the response creator to be passed
        /// to the transaction's constructor.
        ///
        /// @return Pointer to the created transaction instance.
        static TransactionPtr create(const HttpResponseCreatorPtr& response_creator);

        /// @brief Creates new transaction from the current transaction.
        ///
        /// This method creates new transaction and inherits the request
        /// from the existing transaction. This is used when the timeout
        /// occurs during the messages exchange. The server creates the new
        /// transaction to handle the timeout but this new transaction must
        /// include the request instance so as HTTP version information can
        /// be inferred from it while sending the timeout response. The
        /// HTTP version information should match between the request and
        /// the response.
        ///
        /// @param response_creator Pointer to the response creator.
        /// @param transaction Existing transaction from which the request
        /// should be inherited. If the transaction is null, the new (dummy)
        /// request is created for the new transaction.
        static TransactionPtr spawn(const HttpResponseCreatorPtr& response_creator,
                                    const TransactionPtr& transaction);

        /// @brief Returns request instance associated with the transaction.
        HttpRequestPtr getRequest() const {
            return (request_);
        }

        /// @brief Returns parser instance associated with the transaction.
        HttpRequestParserPtr getParser() const {
            return (parser_);
        }

        /// @brief Returns pointer to the first byte of the input buffer.
        char* getInputBufData() {
            return (input_buf_.data());
        }

        /// @brief Returns input buffer size.
        size_t getInputBufSize() const {
            return (input_buf_.size());
        }

        /// @brief Checks if the output buffer contains some data to be
        /// sent.
        ///
        /// @return true if the output buffer contains data to be sent,
        /// false otherwise.
        bool outputDataAvail() const {
            return (!output_buf_.empty());
        }

        /// @brief Returns pointer to the first byte of the output buffer.
        const char* getOutputBufData() const {
            return (output_buf_.data());
        }

        /// @brief Returns size of the output buffer.
        size_t getOutputBufSize() const {
            return (output_buf_.size());
        }

        /// @brief Replaces output buffer contents with new contents.
        ///
        /// @param response New contents for the output buffer.
        void setOutputBuf(const std::string& response) {
            output_buf_ = response;
        }

        /// @brief Erases n bytes from the beginning of the output buffer.
        ///
        /// @param length Number of bytes to be erased.
        void consumeOutputBuf(const size_t length) {
            output_buf_.erase(0, length);
        }

    private:

        /// @brief Pointer to the request received over this connection.
        HttpRequestPtr request_;

        /// @brief Pointer to the HTTP request parser.
        HttpRequestParserPtr parser_;

        /// @brief Buffer for received data.
        std::array<char, 32768> input_buf_;

        /// @brief Buffer used for outbound data.
        std::string output_buf_;
    };

public:

    /// @brief Constructor.
    ///
    /// @param io_service IO service to be used by the connection.
    /// @param acceptor Pointer to the TCP acceptor object used to listen for
    /// new HTTP connections.
    /// @param tls_context TLS context.
    /// @param connection_pool Connection pool in which this connection is
    /// stored.
    /// @param response_creator Pointer to the response creator object used to
    /// create HTTP response from the HTTP request received.
    /// @param callback Callback invoked when new connection is accepted.
    /// @param request_timeout Configured timeout for a HTTP request.
    /// @param idle_timeout Timeout after which persistent HTTP connection is
    /// closed by the server.
    HttpConnection(const asiolink::IOServicePtr& io_service,
                   const HttpAcceptorPtr& acceptor,
                   const asiolink::TlsContextPtr& tls_context,
                   HttpConnectionPool& connection_pool,
                   const HttpResponseCreatorPtr& response_creator,
                   const HttpAcceptorCallback& callback,
                   const long request_timeout,
                   const long idle_timeout);

    /// @brief Destructor.
    ///
    /// Closes current connection.
    virtual ~HttpConnection();

    /// @brief Asynchronously accepts new connection.
    ///
    /// When the connection is established successfully, the timeout timer is
    /// setup and the asynchronous handshake with client is performed.
    void asyncAccept();

    /// @brief Shutdown the socket.
    void shutdown();

    /// @brief Closes the socket.
    void close();

    /// @brief Records connection parameters into the HTTP request.
    ///
    /// @param request Pointer to the HTTP request.
    void recordParameters(const HttpRequestPtr& request) const;

    /// @brief Asynchronously performs TLS handshake.
    ///
    /// When the handshake is performed successfully or skipped because TLS
    /// was not enabled, the asynchronous read from the socket is started.
    void doHandshake();

    /// @brief Starts asynchronous read from the socket.
    ///
    /// The data received over the socket are supplied to the HTTP parser until
    /// the parser signals that the entire request has been received or until
    /// the parser signals an error. In the former case the server creates an
    /// HTTP response using supplied response creator object.
    ///
    /// In case of error the connection is stopped.
    ///
    /// @param transaction Pointer to the transaction for which the read
    /// operation should be performed. It defaults to null pointer which
    /// indicates that this function should create new transaction.
    void doRead(TransactionPtr transaction = TransactionPtr());

protected:

    /// @brief Starts asynchronous write to the socket.
    ///
    /// The @c output_buf_ must contain the data to be sent.
    ///
    /// In case of error the connection is stopped.
    ///
    /// @param transaction Pointer to the transaction for which the write
    /// operation should be performed.
    void doWrite(TransactionPtr transaction);

    /// @brief Sends HTTP response asynchronously.
    ///
    /// Internally it calls @ref HttpConnection::doWrite to send the data.
    ///
    /// @param response Pointer to the HTTP response to be sent.
    /// @param transaction Pointer to the transaction.
    void asyncSendResponse(const ConstHttpResponsePtr& response,
                           TransactionPtr transaction);

    /// @brief Local callback invoked when new connection is accepted.
    ///
    /// It invokes external (supplied via constructor) acceptor callback. If
    /// the acceptor is not opened it returns immediately. If the connection
    /// is accepted successfully the @ref HttpConnection::doRead or
    /// @ref HttpConnection::doHandshake is called.
    ///
    /// @param ec Error code.
    void acceptorCallback(const boost::system::error_code& ec);

    /// @brief Local callback invoked when TLS handshake is performed.
    ///
    /// If the handshake is performed successfully the @ref
    /// HttpConnection::doRead is called.
    ///
    /// @param ec Error code.
    void handshakeCallback(const boost::system::error_code& ec);

    /// @brief Callback invoked when new data is received over the socket.
    ///
    /// This callback supplies the data to the HTTP parser and continues
    /// parsing. When the parser signals end of the HTTP request the callback
    /// prepares a response and starts asynchronous send over the socket.
    ///
    /// @param transaction Pointer to the transaction for which the callback
    /// is invoked.
    /// @param ec Error code.
    /// @param length Length of the received data.
    void socketReadCallback(TransactionPtr transaction,
                            boost::system::error_code ec,
                            size_t length);

    /// @brief Callback invoked when data is sent over the socket.
    ///
    /// @param transaction Pointer to the transaction for which the callback
    /// is invoked.
    /// @param ec Error code.
    /// @param length Length of the data sent.
    virtual void socketWriteCallback(TransactionPtr transaction,
                                     boost::system::error_code ec,
                                     size_t length);

    /// @brief Callback invoked when TLS shutdown is performed.
    ///
    /// The TLS socket is unconditionally closed but the callback is called
    /// only when the peer has answered so the connection should be
    /// explicitly closed in all cases, i.e. do not rely on this handler.
    ///
    /// @param ec Error code (ignored).
    void shutdownCallback(const boost::system::error_code& ec);

    /// @brief Reset timer for detecting request timeouts.
    ///
    /// @param transaction Pointer to the transaction to be guarded by the timeout.
    void setupRequestTimer(TransactionPtr transaction = TransactionPtr());

    /// @brief Reset timer for detecting idle timeout in persistent connections.
    void setupIdleTimer();

    /// @brief Callback invoked when the HTTP Request Timeout occurs.
    ///
    /// This callback creates HTTP response with Request Timeout error code
    /// and sends it to the client.
    ///
    /// @param transaction Pointer to the transaction for which timeout occurs.
    void requestTimeoutCallback(TransactionPtr transaction);

    void idleTimeoutCallback();

    /// @brief Shuts down current connection.
    ///
    /// Copied from the next method @ref stopThisConnection
    void shutdownConnection();

    /// @brief Stops current connection.
    void stopThisConnection();

    /// @brief returns remote address in textual form
    std::string getRemoteEndpointAddressAsText() const;

    /// @brief Timer used to detect Request Timeout.
    asiolink::IntervalTimer request_timer_;

    /// @brief Configured Request Timeout in milliseconds.
    long request_timeout_;

    /// @brief TLS context.
    asiolink::TlsContextPtr tls_context_;

    /// @brief Timeout after which the persistent HTTP connection is shut
    /// down by the server.
    long idle_timeout_;

    /// @brief TCP socket used by this connection.
    std::unique_ptr<asiolink::TCPSocket<SocketCallback> > tcp_socket_;

    /// @brief TLS socket used by this connection.
    std::unique_ptr<asiolink::TLSSocket<SocketCallback> > tls_socket_;

    /// @brief Pointer to the TCP acceptor used to accept new connections.
    HttpAcceptorPtr acceptor_;

    /// @brief Connection pool holding this connection.
    HttpConnectionPool& connection_pool_;

    /// @brief Pointer to the @ref HttpResponseCreator object used to create
    /// HTTP responses.
    HttpResponseCreatorPtr response_creator_;

    /// @brief External TCP acceptor callback.
    HttpAcceptorCallback acceptor_callback_;
};

} // end of namespace isc::http
} // end of namespace isc

#endif