• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // server.cpp
3 // ~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
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 
server(boost::asio::io_context & io_context,const std::string & address,const std::string & port,boost::function<void (const request &,reply &)> request_handler)18 server::server(boost::asio::io_context& io_context,
19     const std::string& address, const std::string& port,
20     boost::function<void(const request&, reply&)> request_handler)
21   : request_handler_(request_handler)
22 {
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));
27 }
28 
29 // Enable the pseudo-keywords reenter, yield and fork.
30 #include <boost/asio/yield.hpp>
31 
operator ()(boost::system::error_code ec,std::size_t length)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.
48         socket_.reset(new tcp::socket(acceptor_->get_executor()));
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
53         // operation. When the asynchronous operation completes, the io_context
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
123