// // 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 // //------------------------------------------------------------------------------ // // Example: HTTP server, small // //------------------------------------------------------------------------------ #include #include #include #include #include #include #include #include #include #include namespace beast = boost::beast; // from namespace http = beast::http; // from namespace net = boost::asio; // from using tcp = boost::asio::ip::tcp; // 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. 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. net::steady_timer deadline_{ socket_.get_executor(), 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](beast::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); if(!ec) self->process_request(); }); } // Determine what needs to be done with the request message. void process_request() { response_.version(request_.version()); response_.keep_alive(false); 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"); beast::ostream(response_.body()) << "Invalid request-method '" << std::string(request_.method_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"); 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"); 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"); 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](beast::error_code ec, std::size_t) { 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](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, [&](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 const address = net::ip::make_address(argv[1]); unsigned short port = static_cast(std::atoi(argv[2])); net::io_context ioc{1}; tcp::acceptor acceptor{ioc, {address, port}}; tcp::socket socket{ioc}; http_server(acceptor, socket); ioc.run(); } catch(std::exception const& e) { std::cerr << "Error: " << e.what() << std::endl; return EXIT_FAILURE; } }