1 /* 2 * nghttp2 - HTTP/2 C Library 3 * 4 * Copyright (c) 2014 Tatsuhiro Tsujikawa 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining 7 * a copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sublicense, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be 15 * included in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 */ 25 // We wrote this code based on the original code which has the 26 // following license: 27 // 28 // connection.hpp 29 // ~~~~~~~~~~~~~~ 30 // 31 // Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 32 // 33 // Distributed under the Boost Software License, Version 1.0. (See accompanying 34 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 35 // 36 37 #ifndef ASIO_SERVER_CONNECTION_H 38 #define ASIO_SERVER_CONNECTION_H 39 40 #include "nghttp2_config.h" 41 42 #include <memory> 43 44 #include <boost/noncopyable.hpp> 45 #include <boost/array.hpp> 46 47 #include <nghttp2/asio_http2_server.h> 48 49 #include "asio_server_http2_handler.h" 50 #include "asio_server_serve_mux.h" 51 #include "util.h" 52 #include "template.h" 53 54 #if BOOST_VERSION >= 107000 55 # define GET_IO_SERVICE(s) \ 56 ((boost::asio::io_context &)(s).get_executor().context()) 57 #else 58 # define GET_IO_SERVICE(s) ((s).get_io_service()) 59 #endif 60 61 namespace nghttp2 { 62 63 namespace asio_http2 { 64 65 namespace server { 66 67 /// Represents a single connection from a client. 68 template <typename socket_type> 69 class connection : public std::enable_shared_from_this<connection<socket_type>>, 70 private boost::noncopyable { 71 public: 72 /// Construct a connection with the given io_service. 73 template <typename... SocketArgs> connection(serve_mux & mux,const boost::posix_time::time_duration & tls_handshake_timeout,const boost::posix_time::time_duration & read_timeout,SocketArgs &&...args)74 explicit connection( 75 serve_mux &mux, 76 const boost::posix_time::time_duration &tls_handshake_timeout, 77 const boost::posix_time::time_duration &read_timeout, 78 SocketArgs &&...args) 79 : socket_(std::forward<SocketArgs>(args)...), 80 mux_(mux), 81 deadline_(GET_IO_SERVICE(socket_)), 82 tls_handshake_timeout_(tls_handshake_timeout), 83 read_timeout_(read_timeout), 84 writing_(false), 85 stopped_(false) {} 86 87 /// Start the first asynchronous operation for the connection. start()88 void start() { 89 boost::system::error_code ec; 90 91 handler_ = std::make_shared<http2_handler>( 92 GET_IO_SERVICE(socket_), socket_.lowest_layer().remote_endpoint(ec), 93 [this]() { do_write(); }, mux_); 94 if (handler_->start() != 0) { 95 stop(); 96 return; 97 } 98 do_read(); 99 } 100 socket()101 socket_type &socket() { return socket_; } 102 start_tls_handshake_deadline()103 void start_tls_handshake_deadline() { 104 deadline_.expires_from_now(tls_handshake_timeout_); 105 deadline_.async_wait( 106 std::bind(&connection::handle_deadline, this->shared_from_this())); 107 } 108 start_read_deadline()109 void start_read_deadline() { 110 deadline_.expires_from_now(read_timeout_); 111 deadline_.async_wait( 112 std::bind(&connection::handle_deadline, this->shared_from_this())); 113 } 114 handle_deadline()115 void handle_deadline() { 116 if (stopped_) { 117 return; 118 } 119 120 if (deadline_.expires_at() <= 121 boost::asio::deadline_timer::traits_type::now()) { 122 stop(); 123 deadline_.expires_at(boost::posix_time::pos_infin); 124 return; 125 } 126 127 deadline_.async_wait( 128 std::bind(&connection::handle_deadline, this->shared_from_this())); 129 } 130 do_read()131 void do_read() { 132 auto self = this->shared_from_this(); 133 134 deadline_.expires_from_now(read_timeout_); 135 136 socket_.async_read_some( 137 boost::asio::buffer(buffer_), 138 [this, self](const boost::system::error_code &e, 139 std::size_t bytes_transferred) { 140 if (e) { 141 stop(); 142 return; 143 } 144 145 if (handler_->on_read(buffer_, bytes_transferred) != 0) { 146 stop(); 147 return; 148 } 149 150 do_write(); 151 152 if (!writing_ && handler_->should_stop()) { 153 stop(); 154 return; 155 } 156 157 do_read(); 158 159 // If an error occurs then no new asynchronous operations are 160 // started. This means that all shared_ptr references to the 161 // connection object will disappear and the object will be 162 // destroyed automatically after this handler returns. The 163 // connection class's destructor closes the socket. 164 }); 165 } 166 do_write()167 void do_write() { 168 auto self = this->shared_from_this(); 169 170 if (writing_) { 171 return; 172 } 173 174 int rv; 175 std::size_t nwrite; 176 177 rv = handler_->on_write(outbuf_, nwrite); 178 179 if (rv != 0) { 180 stop(); 181 return; 182 } 183 184 if (nwrite == 0) { 185 if (handler_->should_stop()) { 186 stop(); 187 } 188 return; 189 } 190 191 writing_ = true; 192 193 // Reset read deadline here, because normally client is sending 194 // something, it does not expect timeout while doing it. 195 deadline_.expires_from_now(read_timeout_); 196 197 boost::asio::async_write( 198 socket_, boost::asio::buffer(outbuf_, nwrite), 199 [this, self](const boost::system::error_code &e, std::size_t) { 200 if (e) { 201 stop(); 202 return; 203 } 204 205 writing_ = false; 206 207 do_write(); 208 }); 209 210 // No new asynchronous operations are started. This means that all 211 // shared_ptr references to the connection object will disappear and 212 // the object will be destroyed automatically after this handler 213 // returns. The connection class's destructor closes the socket. 214 } 215 stop()216 void stop() { 217 if (stopped_) { 218 return; 219 } 220 221 stopped_ = true; 222 boost::system::error_code ignored_ec; 223 socket_.lowest_layer().close(ignored_ec); 224 deadline_.cancel(); 225 } 226 227 private: 228 socket_type socket_; 229 230 serve_mux &mux_; 231 232 std::shared_ptr<http2_handler> handler_; 233 234 /// Buffer for incoming data. 235 boost::array<uint8_t, 8_k> buffer_; 236 237 boost::array<uint8_t, 64_k> outbuf_; 238 239 boost::asio::deadline_timer deadline_; 240 boost::posix_time::time_duration tls_handshake_timeout_; 241 boost::posix_time::time_duration read_timeout_; 242 243 bool writing_; 244 bool stopped_; 245 }; 246 247 } // namespace server 248 249 } // namespace asio_http2 250 251 } // namespace nghttp2 252 253 #endif // ASIO_SERVER_CONNECTION_H 254