310e672c2a8524605b0b11a360de5fb1bccc7286
[platform/upstream/nghttp2.git] / src / asio_server.cc
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 "util.h"
41
42 namespace nghttp2 {
43 namespace asio_http2 {
44 namespace server {
45
46 server::server(std::size_t io_service_pool_size)
47     : io_service_pool_(io_service_pool_size) {}
48
49 boost::system::error_code
50 server::listen_and_serve(boost::system::error_code &ec,
51                          boost::asio::ssl::context *tls_context,
52                          const std::string &address, const std::string &port,
53                          int backlog, serve_mux &mux, bool asynchronous) {
54   ec.clear();
55
56   if (bind_and_listen(ec, address, port, backlog)) {
57     return ec;
58   }
59
60   for (auto &acceptor : acceptors_) {
61     if (tls_context) {
62       start_accept(*tls_context, acceptor, mux);
63     } else {
64       start_accept(acceptor, mux);
65     }
66   }
67
68   io_service_pool_.run(asynchronous);
69
70   return ec;
71 }
72
73 boost::system::error_code server::bind_and_listen(boost::system::error_code &ec,
74                                                   const std::string &address,
75                                                   const std::string &port,
76                                                   int backlog) {
77   // Open the acceptor with the option to reuse the address (i.e.
78   // SO_REUSEADDR).
79   tcp::resolver resolver(io_service_pool_.get_io_service());
80   tcp::resolver::query query(address, port);
81   auto it = resolver.resolve(query, ec);
82   if (ec) {
83     return ec;
84   }
85
86   for (; it != tcp::resolver::iterator(); ++it) {
87     tcp::endpoint endpoint = *it;
88     auto acceptor = tcp::acceptor(io_service_pool_.get_io_service());
89
90     if (acceptor.open(endpoint.protocol(), ec)) {
91       continue;
92     }
93
94     acceptor.set_option(tcp::acceptor::reuse_address(true));
95
96     if (acceptor.bind(endpoint, ec)) {
97       continue;
98     }
99
100     if (acceptor.listen(
101             backlog == -1 ? boost::asio::socket_base::max_connections : backlog,
102             ec)) {
103       continue;
104     }
105
106     acceptors_.push_back(std::move(acceptor));
107   }
108
109   if (acceptors_.empty()) {
110     return ec;
111   }
112
113   // ec could have some errors since we may have failed to bind some
114   // interfaces.
115   ec.clear();
116
117   return ec;
118 }
119
120 void server::start_accept(boost::asio::ssl::context &tls_context,
121                           tcp::acceptor &acceptor, serve_mux &mux) {
122   auto new_connection = std::make_shared<connection<ssl_socket>>(
123       mux, io_service_pool_.get_io_service(), tls_context);
124
125   acceptor.async_accept(new_connection->socket().lowest_layer(),
126                         [this, &tls_context, &acceptor, &mux, new_connection](
127                             const boost::system::error_code &e) {
128     if (!e) {
129       new_connection->socket().lowest_layer().set_option(tcp::no_delay(true));
130       new_connection->socket().async_handshake(
131           boost::asio::ssl::stream_base::server,
132           [new_connection](const boost::system::error_code &e) {
133             if (!e) {
134               new_connection->start();
135             }
136           });
137     }
138
139     start_accept(tls_context, acceptor, mux);
140   });
141 }
142
143 void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) {
144   auto new_connection = std::make_shared<connection<tcp::socket>>(
145       mux, io_service_pool_.get_io_service());
146
147   acceptor.async_accept(new_connection->socket(),
148                         [this, &acceptor, &mux, new_connection](
149                             const boost::system::error_code &e) {
150     if (!e) {
151       new_connection->socket().set_option(tcp::no_delay(true));
152       new_connection->start();
153     }
154
155     start_accept(acceptor, mux);
156   });
157 }
158
159 void server::stop() { io_service_pool_.stop(); }
160
161 void server::join() { io_service_pool_.join(); }
162
163 } // namespace server
164 } // namespace asio_http2
165 } // namespace nghttp2