| // |
| // server.cpp |
| // ~~~~~~~~~~ |
| // |
| // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
| // |
| // Distributed under the Boost Software License, Version 1.0. (See accompanying |
| // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| // |
| |
| #include "server.hpp" |
| #include "request.hpp" |
| #include "reply.hpp" |
| |
| namespace http { |
| namespace server4 { |
| |
| server::server(boost::asio::io_service& io_service, |
| const std::string& address, const std::string& port, |
| boost::function<void(const request&, reply&)> request_handler) |
| : request_handler_(request_handler) |
| { |
| tcp::resolver resolver(io_service); |
| tcp::resolver::query query(address, port); |
| acceptor_.reset(new tcp::acceptor(io_service, *resolver.resolve(query))); |
| } |
| |
| #include "yield.hpp" // Enable the pseudo-keywords reenter, yield and fork. |
| |
| void server::operator()(boost::system::error_code ec, std::size_t length) |
| { |
| // In this example we keep the error handling code in one place by |
| // hoisting it outside the coroutine. An alternative approach would be to |
| // check the value of ec after each yield for an asynchronous operation. |
| if (!ec) |
| { |
| // On reentering a coroutine, control jumps to the location of the last |
| // yield or fork. The argument to the "reenter" pseudo-keyword can be a |
| // pointer or reference to an object of type coroutine. |
| reenter (this) |
| { |
| // Loop to accept incoming connections. |
| do |
| { |
| // Create a new socket for the next incoming connection. |
| socket_.reset(new tcp::socket(acceptor_->get_io_service())); |
| |
| // Accept a new connection. The "yield" pseudo-keyword saves the current |
| // line number and exits the coroutine's "reenter" block. We use the |
| // server coroutine as the completion handler for the async_accept |
| // operation. When the asynchronous operation completes, the io_service |
| // invokes the function call operator, we "reenter" the coroutine, and |
| // then control resumes at the following line. |
| yield acceptor_->async_accept(*socket_, *this); |
| |
| // We "fork" by cloning a new server coroutine to handle the connection. |
| // After forking we have a parent coroutine and a child coroutine. Both |
| // parent and child continue execution at the following line. They can |
| // be distinguished using the functions coroutine::is_parent() and |
| // coroutine::is_child(). |
| fork server(*this)(); |
| |
| // The parent continues looping to accept the next incoming connection. |
| // The child exits the loop and processes the connection. |
| } while (is_parent()); |
| |
| // Create the objects needed to receive a request on the connection. |
| buffer_.reset(new boost::array<char, 8192>); |
| request_.reset(new request); |
| |
| // Loop until a complete request (or an invalid one) has been received. |
| do |
| { |
| // Receive some more data. When control resumes at the following line, |
| // the ec and length parameters reflect the result of the asynchronous |
| // operation. |
| yield socket_->async_read_some(boost::asio::buffer(*buffer_), *this); |
| |
| // Parse the data we just received. |
| boost::tie(valid_request_, boost::tuples::ignore) |
| = request_parser_.parse(*request_, |
| buffer_->data(), buffer_->data() + length); |
| |
| // An indeterminate result means we need more data, so keep looping. |
| } while (boost::indeterminate(valid_request_)); |
| |
| // Create the reply object that will be sent back to the client. |
| reply_.reset(new reply); |
| |
| if (valid_request_) |
| { |
| // A valid request was received. Call the user-supplied function object |
| // to process the request and compose a reply. |
| request_handler_(*request_, *reply_); |
| } |
| else |
| { |
| // The request was invalid. |
| *reply_ = reply::stock_reply(reply::bad_request); |
| } |
| |
| // Send the reply back to the client. |
| yield boost::asio::async_write(*socket_, reply_->to_buffers(), *this); |
| |
| // Initiate graceful connection closure. |
| socket_->shutdown(tcp::socket::shutdown_both, ec); |
| } |
| } |
| |
| // If an error occurs then the coroutine is not reentered. Consequently, no |
| // new asynchronous operations are started. This means that all shared_ptr |
| // references will disappear and the resources associated with the coroutine |
| // will be destroyed automatically after this function call returns. |
| } |
| |
| #include "unyield.hpp" // Disable the pseudo-keywords reenter, yield and fork. |
| |
| } // namespace server4 |
| } // namespace http |