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