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;
 | |
|     }
 | |
| }
 |