forked from boostorg/beast
		
	
		
			
	
	
		
			241 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			241 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// 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)
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <beast/core.hpp>
							 | 
						||
| 
								 | 
							
								#include <beast/http.hpp>
							 | 
						||
| 
								 | 
							
								#include <beast/version.hpp>
							 | 
						||
| 
								 | 
							
								#include <boost/asio.hpp>
							 | 
						||
| 
								 | 
							
								#include <chrono>
							 | 
						||
| 
								 | 
							
								#include <cstdlib>
							 | 
						||
| 
								 | 
							
								#include <ctime>
							 | 
						||
| 
								 | 
							
								#include <iostream>
							 | 
						||
| 
								 | 
							
								#include <memory>
							 | 
						||
| 
								 | 
							
								#include <string>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace ip = boost::asio::ip; // from <boost/asio.hpp>
							 | 
						||
| 
								 | 
							
								using tcp = boost::asio::ip::tcp; // from <boost/asio.hpp>
							 | 
						||
| 
								 | 
							
								namespace http = beast::http; // from <beast/http.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.
							 | 
						||
| 
								 | 
							
								    boost::asio::basic_waitable_timer<std::chrono::steady_clock> 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](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");
							 | 
						||
| 
								 | 
							
								            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");
							 | 
						||
| 
								 | 
							
								            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)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                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 address = ip::address::from_string(argv[1]);
							 | 
						||
| 
								 | 
							
								        unsigned short port = static_cast<unsigned short>(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;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |