123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- //
- // 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 <boost/beast/core.hpp>
- #include <boost/beast/http.hpp>
- #include <boost/beast/version.hpp>
- #include <boost/asio.hpp>
- #include <chrono>
- #include <cstdlib>
- #include <ctime>
- #include <iostream>
- #include <memory>
- #include <string>
- namespace beast = boost::beast; // from <boost/beast.hpp>
- namespace http = beast::http; // from <boost/beast/http.hpp>
- namespace net = boost::asio; // from <boost/asio.hpp>
- using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
- 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<http_connection>
- {
- 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<http::dynamic_body> request_;
- // The response message.
- http::response<http::dynamic_body> 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())
- << "<html>\n"
- << "<head><title>Request count</title></head>\n"
- << "<body>\n"
- << "<h1>Request count</h1>\n"
- << "<p>There have been "
- << my_program_state::request_count()
- << " requests so far.</p>\n"
- << "</body>\n"
- << "</html>\n";
- }
- else if(request_.target() == "/time")
- {
- response_.set(http::field::content_type, "text/html");
- beast::ostream(response_.body())
- << "<html>\n"
- << "<head><title>Current time</title></head>\n"
- << "<body>\n"
- << "<h1>Current time</h1>\n"
- << "<p>The current time is "
- << my_program_state::now()
- << " seconds since the epoch.</p>\n"
- << "</body>\n"
- << "</html>\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<http_connection>(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] << " <address> <port>\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<unsigned short>(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;
- }
- }
|