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