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