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 // server.cpp
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 #include "asio_server.h"
38 
39 #include "asio_server_connection.h"
40 #include "asio_common.h"
41 #include "util.h"
42 
43 namespace nghttp2 {
44 namespace asio_http2 {
45 namespace server {
46 
server(std::size_t io_service_pool_size,const boost::posix_time::time_duration & tls_handshake_timeout,const boost::posix_time::time_duration & read_timeout)47 server::server(std::size_t io_service_pool_size,
48                const boost::posix_time::time_duration &tls_handshake_timeout,
49                const boost::posix_time::time_duration &read_timeout)
50     : io_service_pool_(io_service_pool_size),
51       tls_handshake_timeout_(tls_handshake_timeout),
52       read_timeout_(read_timeout) {}
53 
54 boost::system::error_code
listen_and_serve(boost::system::error_code & ec,boost::asio::ssl::context * tls_context,const std::string & address,const std::string & port,int backlog,serve_mux & mux,bool asynchronous)55 server::listen_and_serve(boost::system::error_code &ec,
56                          boost::asio::ssl::context *tls_context,
57                          const std::string &address, const std::string &port,
58                          int backlog, serve_mux &mux, bool asynchronous) {
59   ec.clear();
60 
61   if (bind_and_listen(ec, address, port, backlog)) {
62     return ec;
63   }
64 
65   for (auto &acceptor : acceptors_) {
66     if (tls_context) {
67       start_accept(*tls_context, acceptor, mux);
68     } else {
69       start_accept(acceptor, mux);
70     }
71   }
72 
73   io_service_pool_.run(asynchronous);
74 
75   return ec;
76 }
77 
bind_and_listen(boost::system::error_code & ec,const std::string & address,const std::string & port,int backlog)78 boost::system::error_code server::bind_and_listen(boost::system::error_code &ec,
79                                                   const std::string &address,
80                                                   const std::string &port,
81                                                   int backlog) {
82   // Open the acceptor with the option to reuse the address (i.e.
83   // SO_REUSEADDR).
84   tcp::resolver resolver(io_service_pool_.get_io_service());
85   tcp::resolver::query query(address, port);
86   auto it = resolver.resolve(query, ec);
87   if (ec) {
88     return ec;
89   }
90 
91   for (; it != tcp::resolver::iterator(); ++it) {
92     tcp::endpoint endpoint = *it;
93     auto acceptor = tcp::acceptor(io_service_pool_.get_io_service());
94 
95     if (acceptor.open(endpoint.protocol(), ec)) {
96       continue;
97     }
98 
99     acceptor.set_option(tcp::acceptor::reuse_address(true));
100 
101     if (acceptor.bind(endpoint, ec)) {
102       continue;
103     }
104 
105     if (acceptor.listen(
106             backlog == -1 ? boost::asio::socket_base::max_connections : backlog,
107             ec)) {
108       continue;
109     }
110 
111     acceptors_.push_back(std::move(acceptor));
112   }
113 
114   if (acceptors_.empty()) {
115     return ec;
116   }
117 
118   // ec could have some errors since we may have failed to bind some
119   // interfaces.
120   ec.clear();
121 
122   return ec;
123 }
124 
start_accept(boost::asio::ssl::context & tls_context,tcp::acceptor & acceptor,serve_mux & mux)125 void server::start_accept(boost::asio::ssl::context &tls_context,
126                           tcp::acceptor &acceptor, serve_mux &mux) {
127 
128   if (!acceptor.is_open()) {
129     return;
130   }
131 
132   auto new_connection = std::make_shared<connection<ssl_socket>>(
133       mux, tls_handshake_timeout_, read_timeout_,
134       io_service_pool_.get_io_service(), tls_context);
135 
136   acceptor.async_accept(
137       new_connection->socket().lowest_layer(),
138       [this, &tls_context, &acceptor, &mux,
139        new_connection](const boost::system::error_code &e) {
140         if (!e) {
141           new_connection->socket().lowest_layer().set_option(
142               tcp::no_delay(true));
143           new_connection->start_tls_handshake_deadline();
144           new_connection->socket().async_handshake(
145               boost::asio::ssl::stream_base::server,
146               [new_connection](const boost::system::error_code &e) {
147                 if (e) {
148                   new_connection->stop();
149                   return;
150                 }
151 
152                 if (!tls_h2_negotiated(new_connection->socket())) {
153                   new_connection->stop();
154                   return;
155                 }
156 
157                 new_connection->start();
158               });
159         }
160 
161         start_accept(tls_context, acceptor, mux);
162       });
163 }
164 
start_accept(tcp::acceptor & acceptor,serve_mux & mux)165 void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) {
166 
167   if (!acceptor.is_open()) {
168     return;
169   }
170 
171   auto new_connection = std::make_shared<connection<tcp::socket>>(
172       mux, tls_handshake_timeout_, read_timeout_,
173       io_service_pool_.get_io_service());
174 
175   acceptor.async_accept(
176       new_connection->socket(), [this, &acceptor, &mux, new_connection](
177                                     const boost::system::error_code &e) {
178         if (!e) {
179           new_connection->socket().set_option(tcp::no_delay(true));
180           new_connection->start_read_deadline();
181           new_connection->start();
182         }
183         if (acceptor.is_open()) {
184           start_accept(acceptor, mux);
185         }
186       });
187 }
188 
stop()189 void server::stop() {
190   for (auto &acceptor : acceptors_) {
191     acceptor.close();
192   }
193   io_service_pool_.stop();
194 }
195 
join()196 void server::join() { io_service_pool_.join(); }
197 
198 const std::vector<std::shared_ptr<boost::asio::io_service>> &
io_services() const199 server::io_services() const {
200   return io_service_pool_.io_services();
201 }
202 
ports() const203 const std::vector<int> server::ports() const {
204   auto ports = std::vector<int>(acceptors_.size());
205   auto index = 0;
206   for (const auto &acceptor : acceptors_) {
207     ports[index++] = acceptor.local_endpoint().port();
208   }
209   return ports;
210 }
211 
212 } // namespace server
213 } // namespace asio_http2
214 } // namespace nghttp2
215