]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // server.cpp | |
3 | // ~~~~~~~~~~ | |
4 | // | |
b32b8144 | 5 | // Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
7c673cae FG |
6 | // |
7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
9 | // | |
10 | ||
11 | #include "server.hpp" | |
12 | #include "request.hpp" | |
13 | #include "reply.hpp" | |
14 | ||
15 | namespace http { | |
16 | namespace server4 { | |
17 | ||
b32b8144 | 18 | server::server(boost::asio::io_context& io_context, |
7c673cae FG |
19 | const std::string& address, const std::string& port, |
20 | boost::function<void(const request&, reply&)> request_handler) | |
21 | : request_handler_(request_handler) | |
22 | { | |
b32b8144 FG |
23 | tcp::resolver resolver(io_context); |
24 | boost::asio::ip::tcp::endpoint endpoint = | |
25 | *resolver.resolve(address, port).begin(); | |
26 | acceptor_.reset(new tcp::acceptor(io_context, endpoint)); | |
7c673cae FG |
27 | } |
28 | ||
29 | // Enable the pseudo-keywords reenter, yield and fork. | |
30 | #include <boost/asio/yield.hpp> | |
31 | ||
32 | void server::operator()(boost::system::error_code ec, std::size_t length) | |
33 | { | |
34 | // In this example we keep the error handling code in one place by | |
35 | // hoisting it outside the coroutine. An alternative approach would be to | |
36 | // check the value of ec after each yield for an asynchronous operation. | |
37 | if (!ec) | |
38 | { | |
39 | // On reentering a coroutine, control jumps to the location of the last | |
40 | // yield or fork. The argument to the "reenter" pseudo-keyword can be a | |
41 | // pointer or reference to an object of type coroutine. | |
42 | reenter (this) | |
43 | { | |
44 | // Loop to accept incoming connections. | |
45 | do | |
46 | { | |
47 | // Create a new socket for the next incoming connection. | |
b32b8144 | 48 | socket_.reset(new tcp::socket(acceptor_->get_executor().context())); |
7c673cae FG |
49 | |
50 | // Accept a new connection. The "yield" pseudo-keyword saves the current | |
51 | // line number and exits the coroutine's "reenter" block. We use the | |
52 | // server coroutine as the completion handler for the async_accept | |
b32b8144 | 53 | // operation. When the asynchronous operation completes, the io_context |
7c673cae FG |
54 | // invokes the function call operator, we "reenter" the coroutine, and |
55 | // then control resumes at the following line. | |
56 | yield acceptor_->async_accept(*socket_, *this); | |
57 | ||
58 | // We "fork" by cloning a new server coroutine to handle the connection. | |
59 | // After forking we have a parent coroutine and a child coroutine. Both | |
60 | // parent and child continue execution at the following line. They can | |
61 | // be distinguished using the functions coroutine::is_parent() and | |
62 | // coroutine::is_child(). | |
63 | fork server(*this)(); | |
64 | ||
65 | // The parent continues looping to accept the next incoming connection. | |
66 | // The child exits the loop and processes the connection. | |
67 | } while (is_parent()); | |
68 | ||
69 | // Create the objects needed to receive a request on the connection. | |
70 | buffer_.reset(new boost::array<char, 8192>); | |
71 | request_.reset(new request); | |
72 | ||
73 | // Loop until a complete request (or an invalid one) has been received. | |
74 | do | |
75 | { | |
76 | // Receive some more data. When control resumes at the following line, | |
77 | // the ec and length parameters reflect the result of the asynchronous | |
78 | // operation. | |
79 | yield socket_->async_read_some(boost::asio::buffer(*buffer_), *this); | |
80 | ||
81 | // Parse the data we just received. | |
82 | boost::tie(valid_request_, boost::tuples::ignore) | |
83 | = request_parser_.parse(*request_, | |
84 | buffer_->data(), buffer_->data() + length); | |
85 | ||
86 | // An indeterminate result means we need more data, so keep looping. | |
87 | } while (boost::indeterminate(valid_request_)); | |
88 | ||
89 | // Create the reply object that will be sent back to the client. | |
90 | reply_.reset(new reply); | |
91 | ||
92 | if (valid_request_) | |
93 | { | |
94 | // A valid request was received. Call the user-supplied function object | |
95 | // to process the request and compose a reply. | |
96 | request_handler_(*request_, *reply_); | |
97 | } | |
98 | else | |
99 | { | |
100 | // The request was invalid. | |
101 | *reply_ = reply::stock_reply(reply::bad_request); | |
102 | } | |
103 | ||
104 | // Send the reply back to the client. | |
105 | yield boost::asio::async_write(*socket_, reply_->to_buffers(), *this); | |
106 | ||
107 | // Initiate graceful connection closure. | |
108 | socket_->shutdown(tcp::socket::shutdown_both, ec); | |
109 | } | |
110 | } | |
111 | ||
112 | // If an error occurs then the coroutine is not reentered. Consequently, no | |
113 | // new asynchronous operations are started. This means that all shared_ptr | |
114 | // references will disappear and the resources associated with the coroutine | |
115 | // will be destroyed automatically after this function call returns. | |
116 | } | |
117 | ||
118 | // Disable the pseudo-keywords reenter, yield and fork. | |
119 | #include <boost/asio/unyield.hpp> | |
120 | ||
121 | } // namespace server4 | |
122 | } // namespace http |