]> git.proxmox.com Git - ceph.git/blame - ceph/src/Beast/examples/http_async_server.hpp
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / Beast / examples / http_async_server.hpp
CommitLineData
7c673cae
FG
1//
2// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
3//
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6//
7
8#ifndef BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
9#define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
10
11#include "file_body.hpp"
12#include "mime_type.hpp"
13
14#include <beast/http.hpp>
15#include <beast/core/handler_helpers.hpp>
16#include <beast/core/handler_ptr.hpp>
17#include <beast/core/placeholders.hpp>
18#include <beast/core/streambuf.hpp>
19#include <boost/asio.hpp>
20#include <cstddef>
21#include <cstdio>
22#include <iostream>
23#include <memory>
24#include <mutex>
25#include <thread>
26#include <utility>
27
28namespace beast {
29namespace http {
30
31class http_async_server
32{
33 using endpoint_type = boost::asio::ip::tcp::endpoint;
34 using address_type = boost::asio::ip::address;
35 using socket_type = boost::asio::ip::tcp::socket;
36
37 using req_type = request<string_body>;
38 using resp_type = response<file_body>;
39
40 std::mutex m_;
41 bool log_ = true;
42 boost::asio::io_service ios_;
43 boost::asio::ip::tcp::acceptor acceptor_;
44 socket_type sock_;
45 std::string root_;
46 std::vector<std::thread> thread_;
47
48public:
49 http_async_server(endpoint_type const& ep,
50 std::size_t threads, std::string const& root)
51 : acceptor_(ios_)
52 , sock_(ios_)
53 , root_(root)
54 {
55 acceptor_.open(ep.protocol());
56 acceptor_.bind(ep);
57 acceptor_.listen(
58 boost::asio::socket_base::max_connections);
59 acceptor_.async_accept(sock_,
60 std::bind(&http_async_server::on_accept, this,
61 beast::asio::placeholders::error));
62 thread_.reserve(threads);
63 for(std::size_t i = 0; i < threads; ++i)
64 thread_.emplace_back(
65 [&] { ios_.run(); });
66 }
67
68 ~http_async_server()
69 {
70 error_code ec;
71 ios_.dispatch(
72 [&]{ acceptor_.close(ec); });
73 for(auto& t : thread_)
74 t.join();
75 }
76
77 template<class... Args>
78 void
79 log(Args const&... args)
80 {
81 if(log_)
82 {
83 std::lock_guard<std::mutex> lock(m_);
84 log_args(args...);
85 }
86 }
87
88private:
89 template<class Stream, class Handler,
90 bool isRequest, class Body, class Fields>
91 class write_op
92 {
93 struct data
94 {
95 bool cont;
96 Stream& s;
97 message<isRequest, Body, Fields> m;
98
99 data(Handler& handler, Stream& s_,
100 message<isRequest, Body, Fields>&& m_)
101 : cont(beast_asio_helpers::
102 is_continuation(handler))
103 , s(s_)
104 , m(std::move(m_))
105 {
106 }
107 };
108
109 handler_ptr<data, Handler> d_;
110
111 public:
112 write_op(write_op&&) = default;
113 write_op(write_op const&) = default;
114
115 template<class DeducedHandler, class... Args>
116 write_op(DeducedHandler&& h, Stream& s, Args&&... args)
117 : d_(std::forward<DeducedHandler>(h),
118 s, std::forward<Args>(args)...)
119 {
120 (*this)(error_code{}, false);
121 }
122
123 void
124 operator()(error_code ec, bool again = true)
125 {
126 auto& d = *d_;
127 d.cont = d.cont || again;
128 if(! again)
129 {
130 beast::http::async_write(d.s, d.m, std::move(*this));
131 return;
132 }
133 d_.invoke(ec);
134 }
135
136 friend
137 void* asio_handler_allocate(
138 std::size_t size, write_op* op)
139 {
140 return beast_asio_helpers::
141 allocate(size, op->d_.handler());
142 }
143
144 friend
145 void asio_handler_deallocate(
146 void* p, std::size_t size, write_op* op)
147 {
148 return beast_asio_helpers::
149 deallocate(p, size, op->d_.handler());
150 }
151
152 friend
153 bool asio_handler_is_continuation(write_op* op)
154 {
155 return op->d_->cont;
156 }
157
158 template<class Function>
159 friend
160 void asio_handler_invoke(Function&& f, write_op* op)
161 {
162 return beast_asio_helpers::
163 invoke(f, op->d_.handler());
164 }
165 };
166
167 template<class Stream,
168 bool isRequest, class Body, class Fields,
169 class DeducedHandler>
170 static
171 void
172 async_write(Stream& stream, message<
173 isRequest, Body, Fields>&& msg,
174 DeducedHandler&& handler)
175 {
176 write_op<Stream, typename std::decay<DeducedHandler>::type,
177 isRequest, Body, Fields>{std::forward<DeducedHandler>(
178 handler), stream, std::move(msg)};
179 }
180
181 class peer : public std::enable_shared_from_this<peer>
182 {
183 int id_;
184 streambuf sb_;
185 socket_type sock_;
186 http_async_server& server_;
187 boost::asio::io_service::strand strand_;
188 req_type req_;
189
190 public:
191 peer(peer&&) = default;
192 peer(peer const&) = default;
193 peer& operator=(peer&&) = delete;
194 peer& operator=(peer const&) = delete;
195
196 peer(socket_type&& sock, http_async_server& server)
197 : sock_(std::move(sock))
198 , server_(server)
199 , strand_(sock_.get_io_service())
200 {
201 static int n = 0;
202 id_ = ++n;
203 }
204
205 void
206 fail(error_code ec, std::string what)
207 {
208 if(ec != boost::asio::error::operation_aborted)
209 server_.log("#", id_, " ", what, ": ", ec.message(), "\n");
210 }
211
212 void run()
213 {
214 do_read();
215 }
216
217 void do_read()
218 {
219 async_read(sock_, sb_, req_, strand_.wrap(
220 std::bind(&peer::on_read, shared_from_this(),
221 asio::placeholders::error)));
222 }
223
224 void on_read(error_code const& ec)
225 {
226 if(ec)
227 return fail(ec, "read");
228 auto path = req_.url;
229 if(path == "/")
230 path = "/index.html";
231 path = server_.root_ + path;
232 if(! boost::filesystem::exists(path))
233 {
234 response<string_body> res;
235 res.status = 404;
236 res.reason = "Not Found";
237 res.version = req_.version;
238 res.fields.insert("Server", "http_async_server");
239 res.fields.insert("Content-Type", "text/html");
240 res.body = "The file '" + path + "' was not found";
241 prepare(res);
242 async_write(sock_, std::move(res),
243 std::bind(&peer::on_write, shared_from_this(),
244 asio::placeholders::error));
245 return;
246 }
247 try
248 {
249 resp_type res;
250 res.status = 200;
251 res.reason = "OK";
252 res.version = req_.version;
253 res.fields.insert("Server", "http_async_server");
254 res.fields.insert("Content-Type", mime_type(path));
255 res.body = path;
256 prepare(res);
257 async_write(sock_, std::move(res),
258 std::bind(&peer::on_write, shared_from_this(),
259 asio::placeholders::error));
260 }
261 catch(std::exception const& e)
262 {
263 response<string_body> res;
264 res.status = 500;
265 res.reason = "Internal Error";
266 res.version = req_.version;
267 res.fields.insert("Server", "http_async_server");
268 res.fields.insert("Content-Type", "text/html");
269 res.body =
270 std::string{"An internal error occurred"} + e.what();
271 prepare(res);
272 async_write(sock_, std::move(res),
273 std::bind(&peer::on_write, shared_from_this(),
274 asio::placeholders::error));
275 }
276 }
277
278 void on_write(error_code ec)
279 {
280 if(ec)
281 fail(ec, "write");
282 do_read();
283 }
284 };
285
286 void
287 log_args()
288 {
289 }
290
291 template<class Arg, class... Args>
292 void
293 log_args(Arg const& arg, Args const&... args)
294 {
295 std::cerr << arg;
296 log_args(args...);
297 }
298
299 void
300 fail(error_code ec, std::string what)
301 {
302 log(what, ": ", ec.message(), "\n");
303 }
304
305 void
306 on_accept(error_code ec)
307 {
308 if(! acceptor_.is_open())
309 return;
310 if(ec)
311 return fail(ec, "accept");
312 socket_type sock(std::move(sock_));
313 acceptor_.async_accept(sock_,
314 std::bind(&http_async_server::on_accept, this,
315 asio::placeholders::error));
316 std::make_shared<peer>(std::move(sock), *this)->run();
317 }
318};
319
320} // http
321} // beast
322
323#endif