1 // Copyright (C) 2017-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_CONNECTION_H
8 #define HTTP_CONNECTION_H
9 
10 #include <asiolink/interval_timer.h>
11 #include <asiolink/io_service.h>
12 #include <http/http_acceptor.h>
13 #include <http/request_parser.h>
14 #include <http/response_creator_factory.h>
15 #include <boost/enable_shared_from_this.hpp>
16 #include <boost/system/error_code.hpp>
17 #include <boost/shared_ptr.hpp>
18 #include <array>
19 #include <functional>
20 #include <string>
21 
22 namespace isc {
23 namespace http {
24 
25 /// @brief Generic error reported within @ref HttpConnection class.
26 class HttpConnectionError : public Exception {
27 public:
HttpConnectionError(const char * file,size_t line,const char * what)28     HttpConnectionError(const char* file, size_t line, const char* what) :
29         isc::Exception(file, line, what) { };
30 };
31 
32 /// @brief Forward declaration to the @ref HttpConnectionPool.
33 ///
34 /// This declaration is needed because we don't include the header file
35 /// declaring @ref HttpConnectionPool to avoid circular inclusion.
36 class HttpConnectionPool;
37 
38 class HttpConnection;
39 /// @brief Pointer to the @ref HttpConnection.
40 typedef boost::shared_ptr<HttpConnection> HttpConnectionPtr;
41 
42 /// @brief Accepts and handles a single HTTP connection.
43 class HttpConnection : public boost::enable_shared_from_this<HttpConnection> {
44 private:
45 
46     /// @brief Type of the function implementing a callback invoked by the
47     /// @c SocketCallback functor.
48     typedef std::function<void(boost::system::error_code ec, size_t length)>
49     SocketCallbackFunction;
50 
51     /// @brief Functor associated with the socket object.
52     ///
53     /// This functor calls a callback function specified in the constructor.
54     class SocketCallback {
55     public:
56 
57         /// @brief Constructor.
58         ///
59         /// @param socket_callback Callback to be invoked by the functor upon
60         /// an event associated with the socket.
SocketCallback(SocketCallbackFunction socket_callback)61         SocketCallback(SocketCallbackFunction socket_callback)
62             : callback_(socket_callback) {
63         }
64 
65         /// @brief Operator called when event associated with a socket occurs.
66         ///
67         /// This operator returns immediately when received error code is
68         /// @c boost::system::error_code is equal to
69         /// @c boost::asio::error::operation_aborted, i.e. the callback is not
70         /// invoked.
71         ///
72         /// @param ec Error code.
73         /// @param length Data length.
74         void operator()(boost::system::error_code ec, size_t length = 0);
75 
76     private:
77         /// @brief Supplied callback.
78         SocketCallbackFunction callback_;
79     };
80 
81 protected:
82 
83     class Transaction;
84 
85     /// @brief Shared pointer to the @c Transaction.
86     typedef boost::shared_ptr<Transaction> TransactionPtr;
87 
88     /// @brief Represents a single exchange of the HTTP messages.
89     ///
90     /// In HTTP/1.1 multiple HTTP message exchanges may be conducted
91     /// over the same persistent connection before the connection is
92     /// closed. Since ASIO handlers for these exchanges may be sometimes
93     /// executed out of order, there is a need to associate the states of
94     /// the exchanges with the appropriate ASIO handlers. This object
95     /// represents such state and includes: received request, request
96     /// parser (being in the particular state of parsing), input buffer
97     /// and the output buffer.
98     ///
99     /// The new @c Transaction instance is created when the connection
100     /// is established and the server starts receiving the HTTP request.
101     /// The shared pointer to the created transaction is passed between
102     /// the asynchronous handlers. Therefore, as long as the asynchronous
103     /// communication is conducted the instance of the transaction is
104     /// held by the IO service which runs the handlers. The transaction
105     /// instance exists as long as the asynchronous handlers for the
106     /// given request/response exchange are executed. When the server
107     /// responds to the client and all corresponding IO handlers are
108     /// invoked the transaction is automatically destroyed.
109     ///
110     /// The timeout may occur anytime during the transaction. In such
111     /// cases, a new transaction instance is created to send the
112     /// HTTP 408 (timeout) response to the client. Creation of the
113     /// new transaction for the timeout response is necessary because
114     /// there may be some asynchronous handlers scheduled by the
115     /// original transaction which rely on the original transaction's
116     /// state. The timeout response's state is held within the new
117     /// transaction spawned from the original transaction.
118     class Transaction {
119     public:
120 
121         /// @brief Constructor.
122         ///
123         /// @param response_creator Pointer to the response creator being
124         /// used for generating a response from the request.
125         /// @param request Pointer to the HTTP request. If the request is
126         /// null, the constructor creates new request instance using the
127         /// provided response creator.
128         Transaction(const HttpResponseCreatorPtr& response_creator,
129                     const HttpRequestPtr& request = HttpRequestPtr());
130 
131         /// @brief Creates new transaction instance.
132         ///
133         /// It is called when the HTTP server has just scheduled asynchronous
134         /// reading of the HTTP message.
135         ///
136         /// @param response_creator Pointer to the response creator to be passed
137         /// to the transaction's constructor.
138         ///
139         /// @return Pointer to the created transaction instance.
140         static TransactionPtr create(const HttpResponseCreatorPtr& response_creator);
141 
142         /// @brief Creates new transaction from the current transaction.
143         ///
144         /// This method creates new transaction and inherits the request
145         /// from the existing transaction. This is used when the timeout
146         /// occurs during the messages exchange. The server creates the new
147         /// transaction to handle the timeout but this new transaction must
148         /// include the request instance so as HTTP version information can
149         /// be inferred from it while sending the timeout response. The
150         /// HTTP version information should match between the request and
151         /// the response.
152         ///
153         /// @param response_creator Pointer to the response creator.
154         /// @param transaction Existing transaction from which the request
155         /// should be inherited. If the transaction is null, the new (dummy)
156         /// request is created for the new transaction.
157         static TransactionPtr spawn(const HttpResponseCreatorPtr& response_creator,
158                                     const TransactionPtr& transaction);
159 
160         /// @brief Returns request instance associated with the transaction.
getRequest()161         HttpRequestPtr getRequest() const {
162             return (request_);
163         }
164 
165         /// @brief Returns parser instance associated with the transaction.
getParser()166         HttpRequestParserPtr getParser() const {
167             return (parser_);
168         }
169 
170         /// @brief Returns pointer to the first byte of the input buffer.
getInputBufData()171         char* getInputBufData() {
172             return (input_buf_.data());
173         }
174 
175         /// @brief Returns input buffer size.
getInputBufSize()176         size_t getInputBufSize() const {
177             return (input_buf_.size());
178         }
179 
180         /// @brief Checks if the output buffer contains some data to be
181         /// sent.
182         ///
183         /// @return true if the output buffer contains data to be sent,
184         /// false otherwise.
outputDataAvail()185         bool outputDataAvail() const {
186             return (!output_buf_.empty());
187         }
188 
189         /// @brief Returns pointer to the first byte of the output buffer.
getOutputBufData()190         const char* getOutputBufData() const {
191             return (output_buf_.data());
192         }
193 
194         /// @brief Returns size of the output buffer.
getOutputBufSize()195         size_t getOutputBufSize() const {
196             return (output_buf_.size());
197         }
198 
199         /// @brief Replaces output buffer contents with new contents.
200         ///
201         /// @param response New contents for the output buffer.
setOutputBuf(const std::string & response)202         void setOutputBuf(const std::string& response) {
203             output_buf_ = response;
204         }
205 
206         /// @brief Erases n bytes from the beginning of the output buffer.
207         ///
208         /// @param length Number of bytes to be erased.
consumeOutputBuf(const size_t length)209         void consumeOutputBuf(const size_t length) {
210             output_buf_.erase(0, length);
211         }
212 
213     private:
214 
215         /// @brief Pointer to the request received over this connection.
216         HttpRequestPtr request_;
217 
218         /// @brief Pointer to the HTTP request parser.
219         HttpRequestParserPtr parser_;
220 
221         /// @brief Buffer for received data.
222         std::array<char, 32768> input_buf_;
223 
224         /// @brief Buffer used for outbound data.
225         std::string output_buf_;
226     };
227 
228 public:
229 
230     /// @brief Constructor.
231     ///
232     /// @param io_service IO service to be used by the connection.
233     /// @param acceptor Pointer to the TCP acceptor object used to listen for
234     /// new HTTP connections.
235     /// @param tls_context TLS context.
236     /// @param connection_pool Connection pool in which this connection is
237     /// stored.
238     /// @param response_creator Pointer to the response creator object used to
239     /// create HTTP response from the HTTP request received.
240     /// @param callback Callback invoked when new connection is accepted.
241     /// @param request_timeout Configured timeout for a HTTP request.
242     /// @param idle_timeout Timeout after which persistent HTTP connection is
243     /// closed by the server.
244     HttpConnection(asiolink::IOService& io_service,
245                    const HttpAcceptorPtr& acceptor,
246                    const asiolink::TlsContextPtr& tls_context,
247                    HttpConnectionPool& connection_pool,
248                    const HttpResponseCreatorPtr& response_creator,
249                    const HttpAcceptorCallback& callback,
250                    const long request_timeout,
251                    const long idle_timeout);
252 
253     /// @brief Destructor.
254     ///
255     /// Closes current connection.
256     virtual ~HttpConnection();
257 
258     /// @brief Asynchronously accepts new connection.
259     ///
260     /// When the connection is established successfully, the timeout timer is
261     /// setup and the asynchronous handshake with client is performed.
262     void asyncAccept();
263 
264     /// @brief Shutdown the socket.
265     void shutdown();
266 
267     /// @brief Closes the socket.
268     void close();
269 
270     /// @brief Asynchronously performs TLS handshake.
271     ///
272 
273     /// When the handshake is performed successfully or skipped because TLS
274     /// was not enabled, the asynchronous read from the socket is started.
275     void doHandshake();
276 
277     /// @brief Starts asynchronous read from the socket.
278     ///
279     /// The data received over the socket are supplied to the HTTP parser until
280     /// the parser signals that the entire request has been received or until
281     /// the parser signals an error. In the former case the server creates an
282     /// HTTP response using supplied response creator object.
283     ///
284     /// In case of error the connection is stopped.
285     ///
286     /// @param transaction Pointer to the transaction for which the read
287     /// operation should be performed. It defaults to null pointer which
288     /// indicates that this function should create new transaction.
289     void doRead(TransactionPtr transaction = TransactionPtr());
290 
291 protected:
292 
293     /// @brief Starts asynchronous write to the socket.
294     ///
295     /// The @c output_buf_ must contain the data to be sent.
296     ///
297     /// In case of error the connection is stopped.
298     ///
299     /// @param transaction Pointer to the transaction for which the write
300     /// operation should be performed.
301     void doWrite(TransactionPtr transaction);
302 
303     /// @brief Sends HTTP response asynchronously.
304     ///
305     /// Internally it calls @ref HttpConnection::doWrite to send the data.
306     ///
307     /// @param response Pointer to the HTTP response to be sent.
308     /// @param transaction Pointer to the transaction.
309     void asyncSendResponse(const ConstHttpResponsePtr& response,
310                            TransactionPtr transaction);
311 
312     /// @brief Local callback invoked when new connection is accepted.
313     ///
314     /// It invokes external (supplied via constructor) acceptor callback. If
315     /// the acceptor is not opened it returns immediately. If the connection
316     /// is accepted successfully the @ref HttpConnection::doRead or
317     /// @ref HttpConnection::doHandshake is called.
318     ///
319     /// @param ec Error code.
320     void acceptorCallback(const boost::system::error_code& ec);
321 
322     /// @brief Local callback invoked when TLS handshake is performed.
323     ///
324     /// If the handshake is performed successfully the @ref
325     /// HttpConnection::doRead is called.
326     ///
327     /// @param ec Error code.
328     void handshakeCallback(const boost::system::error_code& ec);
329 
330     /// @brief Callback invoked when new data is received over the socket.
331     ///
332     /// This callback supplies the data to the HTTP parser and continues
333     /// parsing. When the parser signals end of the HTTP request the callback
334     /// prepares a response and starts asynchronous send over the socket.
335     ///
336     /// @param transaction Pointer to the transaction for which the callback
337     /// is invoked.
338     /// @param ec Error code.
339     /// @param length Length of the received data.
340     void socketReadCallback(TransactionPtr transaction,
341                             boost::system::error_code ec,
342                             size_t length);
343 
344     /// @brief Callback invoked when data is sent over the socket.
345     ///
346     /// @param transaction Pointer to the transaction for which the callback
347     /// is invoked.
348     /// @param ec Error code.
349     /// @param length Length of the data sent.
350     virtual void socketWriteCallback(TransactionPtr transaction,
351                                      boost::system::error_code ec,
352                                      size_t length);
353 
354     /// @brief Callback invoked when TLS shutdown is performed.
355     ///
356     /// The TLS socket is unconditionally closed but the callback is called
357     /// only when the peer has answered so the connection should be
358     /// explicitly closed in all cases, i.e. do not rely on this handler.
359     ///
360     /// @param ec Error code (ignored).
361     void shutdownCallback(const boost::system::error_code& ec);
362 
363     /// @brief Reset timer for detecting request timeouts.
364     ///
365     /// @param transaction Pointer to the transaction to be guarded by the timeout.
366     void setupRequestTimer(TransactionPtr transaction = TransactionPtr());
367 
368     /// @brief Reset timer for detecting idle timeout in persistent connections.
369     void setupIdleTimer();
370 
371     /// @brief Callback invoked when the HTTP Request Timeout occurs.
372     ///
373     /// This callback creates HTTP response with Request Timeout error code
374     /// and sends it to the client.
375     ///
376     /// @param transaction Pointer to the transaction for which timeout occurs.
377     void requestTimeoutCallback(TransactionPtr transaction);
378 
379     void idleTimeoutCallback();
380 
381     /// @brief Shuts down current connection.
382     ///
383     /// Copied from the next method @ref stopThisConnection
384     void shutdownConnection();
385 
386     /// @brief Stops current connection.
387     void stopThisConnection();
388 
389     /// @brief returns remote address in textual form
390     std::string getRemoteEndpointAddressAsText() const;
391 
392     /// @brief Timer used to detect Request Timeout.
393     asiolink::IntervalTimer request_timer_;
394 
395     /// @brief Configured Request Timeout in milliseconds.
396     long request_timeout_;
397 
398     /// @brief TLS context.
399     asiolink::TlsContextPtr tls_context_;
400 
401     /// @brief Timeout after which the persistent HTTP connection is shut
402     /// down by the server.
403     long idle_timeout_;
404 
405     /// @brief TCP socket used by this connection.
406     std::unique_ptr<asiolink::TCPSocket<SocketCallback> > tcp_socket_;
407 
408     /// @brief TLS socket used by this connection.
409     std::unique_ptr<asiolink::TLSSocket<SocketCallback> > tls_socket_;
410 
411     /// @brief Pointer to the TCP acceptor used to accept new connections.
412     HttpAcceptorPtr acceptor_;
413 
414     /// @brief Connection pool holding this connection.
415     HttpConnectionPool& connection_pool_;
416 
417     /// @brief Pointer to the @ref HttpResponseCreator object used to create
418     /// HTTP responses.
419     HttpResponseCreatorPtr response_creator_;
420 
421     /// @brief External TCP acceptor callback.
422     HttpAcceptorCallback acceptor_callback_;
423 };
424 
425 } // end of namespace isc::http
426 } // end of namespace isc
427 
428 #endif
429