// // Copyright (c) 2017 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) // // Official repository: https://github.com/boostorg/beast // #include #include #include #include #include #include #include #include #include #include namespace ip = boost::asio::ip; // from using tcp = boost::asio::ip::tcp; // from namespace http = boost::beast::http; // from namespace my_program_state { std::size_t request_count() { static std::size_t count = 0; return ++count; } std::time_t now() { return std::time(0); } } class http_connection : public std::enable_shared_from_this { public: http_connection(tcp::socket socket) : socket_(std::move(socket)) { } // Initiate the asynchronous operations associated with the connection. void start() { read_request(); check_deadline(); } private: // The socket for the currently connected client. tcp::socket socket_; // The buffer for performing reads. boost::beast::flat_buffer buffer_{8192}; // The request message. http::request request_; // The response message. http::response response_; // The timer for putting a deadline on connection processing. boost::asio::basic_waitable_timer deadline_{ socket_.get_io_service(), std::chrono::seconds(60)}; // Asynchronously receive a complete request message. void read_request() { auto self = shared_from_this(); http::async_read( socket_, buffer_, request_, [self](boost::beast::error_code ec) { if(!ec) self->process_request(); }); } // Determine what needs to be done with the request message. void process_request() { response_.version = 11; response_.set(http::field::connection, "close"); switch(request_.method()) { case http::verb::get: response_.result(http::status::ok); response_.set(http::field::server, "Beast"); create_response(); break; default: // We return responses indicating an error if // we do not recognize the request method. response_.result(http::status::bad_request); response_.set(http::field::content_type, "text/plain"); boost::beast::ostream(response_.body) << "Invalid request-method '" << request_.method_string().to_string() << "'"; break; } write_response(); } // Construct a response message based on the program state. void create_response() { if(request_.target() == "/count") { response_.set(http::field::content_type, "text/html"); boost::beast::ostream(response_.body) << "\n" << "Request count\n" << "\n" << "

Request count

\n" << "

There have been " << my_program_state::request_count() << " requests so far.

\n" << "\n" << "\n"; } else if(request_.target() == "/time") { response_.set(http::field::content_type, "text/html"); boost::beast::ostream(response_.body) << "\n" << "Current time\n" << "\n" << "

Current time

\n" << "

The current time is " << my_program_state::now() << " seconds since the epoch.

\n" << "\n" << "\n"; } else { response_.result(http::status::not_found); response_.set(http::field::content_type, "text/plain"); boost::beast::ostream(response_.body) << "File not found\r\n"; } } // Asynchronously transmit the response message. void write_response() { auto self = shared_from_this(); response_.set(http::field::content_length, response_.body.size()); http::async_write( socket_, response_, [self](boost::beast::error_code ec) { self->socket_.shutdown(tcp::socket::shutdown_send, ec); self->deadline_.cancel(); }); } // Check whether we have spent enough time on this connection. void check_deadline() { auto self = shared_from_this(); deadline_.async_wait( [self](boost::beast::error_code ec) { if(!ec) { // Close socket to cancel any outstanding operation. self->socket_.close(ec); } }); } }; // "Loop" forever accepting new connections. void http_server(tcp::acceptor& acceptor, tcp::socket& socket) { acceptor.async_accept(socket, [&](boost::beast::error_code ec) { if(!ec) std::make_shared(std::move(socket))->start(); http_server(acceptor, socket); }); } int main(int argc, char* argv[]) { try { // Check command line arguments. if(argc != 3) { std::cerr << "Usage: " << argv[0] << "
\n"; std::cerr << " For IPv4, try:\n"; std::cerr << " receiver 0.0.0.0 80\n"; std::cerr << " For IPv6, try:\n"; std::cerr << " receiver 0::0 80\n"; return EXIT_FAILURE; } auto address = ip::address::from_string(argv[1]); unsigned short port = static_cast(std::atoi(argv[2])); boost::asio::io_service ios{1}; tcp::acceptor acceptor{ios, {address, port}}; tcp::socket socket{ios}; http_server(acceptor, socket); ios.run(); } catch(std::exception const& e) { std::cerr << "Exception: " << e.what() << std::endl; return EXIT_FAILURE; } }