summaryrefslogtreecommitdiffstats
path: root/src/lib/http/connection.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/http/connection.h')
-rw-r--r--src/lib/http/connection.h432
1 files changed, 432 insertions, 0 deletions
diff --git a/src/lib/http/connection.h b/src/lib/http/connection.h
new file mode 100644
index 0000000..70109de
--- /dev/null
+++ b/src/lib/http/connection.h
@@ -0,0 +1,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(asiolink::IOService& 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