| 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. |
| 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 std::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 | std::tie(parse_result_, std::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 (parse_result_ == request_parser::indeterminate); |
| 88 | |
| 89 | // Create the reply object that will be sent back to the client. |
nothing calls this directly
no test coverage detected