// // Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail 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) // #ifndef BEAST_EXAMPLE_SERVER_HTTP_SYNC_PORT_HPP #define BEAST_EXAMPLE_SERVER_HTTP_SYNC_PORT_HPP #include "server.hpp" #include "http_base.hpp" #include "rfc7231.hpp" #include "service_list.hpp" #include "write_msg.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace framework { /** A synchronous HTTP connection. This base class implements an HTTP connection object using synchronous calls. It uses the Curiously Recurring Template pattern (CRTP) where we refer to the derived class in order to access the stream object to use for reading and writing. This lets the same class be used for plain and SSL stream objects. @tparam Services The list of services this connection will support. */ template class sync_http_con_base : public http_base { // This function lets us access members of the derived class Derived& impl() { return static_cast(*this); } // The stream to use for logging std::ostream& log_; // The services configured for the port service_list const& services_; // A small unique integer for logging std::size_t id_; // The remote endpoint. We cache it here because // calls to remote_endpoint() can fail / throw. // endpoint_type ep_; // The buffer for performing reads beast::flat_buffer buffer_; public: /// Constructor sync_http_con_base( beast::string_view server_name, std::ostream& log, service_list const& services, std::size_t id, endpoint_type const& ep) : http_base(server_name) , log_(log) , services_(services) , id_(id) , ep_(ep) // The buffer has a limit of 8192, otherwise // the server is vulnerable to a buffer attack. // , buffer_(8192) { } // This is called to start the connection after // it is accepted. // void run() { // Bind a shared pointer into the lambda for the // thread, so the sync_http_con_base is destroyed after // the thread function exits. // std::thread{ &sync_http_con_base::do_run, impl().shared_from_this() }.detach(); } protected: // Called when a failure occurs // void fail(std::string what, error_code ec) { if(ec) { log_ << "[#" << id_ << " " << ep_ << "] " << what << ": " << ec.message() << std::endl; } } private: // This lambda is passed to the service list to handle // the case of sending request objects of varying types. // In C++14 this is more easily accomplished using a generic // lambda, but we want C+11 compatibility so we manually // write the lambda out. // struct send_lambda { // holds "this" sync_http_con_base& self_; // holds the captured error code error_code& ec_; public: // Constructor // // Capture "this" and "ec" // send_lambda(sync_http_con_base& self, error_code& ec) : self_(self) , ec_(ec) { } // Sends a message // // Since this is a synchronous implementation we // just call the write function and block. // template void operator()( beast::http::response&& res) const { beast::http::write(self_.impl().stream(), res, ec_); } }; void do_run() { error_code ec; // Give the derived class a chance to do stuff before we // enter the main loop. This is for SSL connections really. // impl().do_handshake(ec); if(ec) return fail("handshake", ec); // The main connection loop, we alternate between // reading a request and sending a response. On // error we log and return, which destroys the thread // and the stream (thus closing the connection) // for(;;) { // Arguments passed to the parser constructor are // forwarded to the message object. A single argument // is forwarded to the body constructor. // // We construct the dynamic body with a 1MB limit // to prevent vulnerability to buffer attacks. // beast::http::request_parser parser(1024* 1024); // Read the header first beast::http::read_header(impl().stream(), buffer_, parser, ec); // This happens when the other end closes gracefully // if(ec == beast::http::error::end_of_stream) { // Give the derived class a chance to do stuff impl().do_shutdown(ec); if(ec) return fail("shutdown", ec); return; } // Any other error and we fail the connection if(ec) return fail("read_header", ec); send_lambda send{*this, ec}; auto& req = parser.get(); // See if they are specifying Expect: 100-continue // if(rfc7231::is_expect_100_continue(req)) { // They want to know if they should continue, // so send the appropriate response synchronously. // send(this->continue_100(req)); // This happens when we send an HTTP message // whose semantics indicate that the connection // should be closed afterwards. For example if // we send a Connection: close. // if(ec == beast::http::error::end_of_stream) { // Give the derived class a chance to do stuff impl().do_shutdown(ec); if(ec) return fail("shutdown", ec); return; } // Have to check the error every time we call the lambda // if(ec) return fail("write", ec); } // Read the rest of the message, if any. // beast::http::read(impl().stream(), buffer_, parser, ec); // Shouldn't be getting end_of_stream here; // that would mean that we got an incomplete // message, counting as an error. // if(ec) return fail("read", ec); // Give each service a chance to handle the request // if(! services_.respond( std::move(impl().stream()), ep_, std::move(req), send)) { // No service handled the request, // send a Bad Request result to the client. // send(this->bad_request(req)); // This happens when we send an HTTP message // whose semantics indicate that the connection // should be closed afterwards. For example if // we send a Connection: close. // if(ec == beast::http::error::end_of_stream) { // Give the derived class a chance to do stuff impl().do_shutdown(ec); if(ec) return fail("shutdown", ec); return; } // Have to check the error every time we call the lambda // if(ec) return fail("write", ec); } else { // This happens when we send an HTTP message // whose semantics indicate that the connection // should be closed afterwards. For example if // we send a Connection: close. // if(ec == beast::http::error::end_of_stream) { // Give the derived class a chance to do stuff impl().do_shutdown(ec); if(ec) return fail("shutdown", ec); return; } // Have to check the error every time we call the lambda // if(ec) return fail("write", ec); // See if the service that handled the // response took ownership of the stream. if(! impl().stream().lowest_layer().is_open()) { // They took ownership so just return and // let this sync_http_con_base object get destroyed. return; } } // Theres no pipelining possible in a synchronous server // because we can't do reads and writes at the same time. } } }; //------------------------------------------------------------------------------ // This class represents a synchronous HTTP connection which // uses a plain TCP/IP socket (no encryption) as the stream. // template class sync_http_con // Note that we give this object the `enable_shared_from_this`, and have // the base class call `impl().shared_from_this()` when needed. // : public std::enable_shared_from_this> // We want the socket to be created before the base class so we use // the "base from member" idiom which Boost provides as a class. // , public base_from_member // Declare this base last now that everything else got set up first. // , public sync_http_con_base, Services...> { public: // Construct the plain connection. // template sync_http_con( socket_type&& sock, Args&&... args) : base_from_member(std::move(sock)) , sync_http_con_base, Services...>( std::forward(args)...) { } // Returns the stream. // // The base class calls this to obtain the object to // use for reading and writing HTTP messages. // socket_type& stream() { return this->member; } private: // Base class needs to be a friend to call our private members friend class sync_http_con_base, Services...>; // This is called by the base before running the main loop. // There's nothing to do for a plain connection. // void do_handshake(error_code& ec) { // This is required by the specifications for error_code // ec = {}; } // This is called when the other end closes the connection gracefully. // void do_shutdown(error_code& ec) { stream().shutdown(socket_type::shutdown_both, ec); } }; //------------------------------------------------------------------------------ /* A synchronous HTTP port handler This type meets the requirements of @b PortHandler. It supports variable list of HTTP services in its template parameter list, and provides a synchronous connection implementation to service */ template class http_sync_port { server& instance_; std::ostream& log_; service_list services_; public: /** Constructor @param instance The server instance which owns this port @param log The stream to use for logging */ http_sync_port( server& instance, std::ostream& log) : instance_(instance) , log_(log) { } /** Initialize a service Every service in the list must be initialized exactly once. @param ec Set to the error, if any occurred @param args Optional arguments forwarded to the service constructor. @tparam Index The 0-based index of the service to initialize. */ template void init(error_code& ec, Args&&... args) { services_.template init( ec, std::forward(args)...); } /** Called by the server to provide ownership of the socket for a new connection @param sock The socket whose ownership is to be transferred @ep The remote endpoint */ void on_accept(socket_type&& sock, endpoint_type ep) { // Create a plain http connection object // and transfer ownership of the socket. // std::make_shared>( std::move(sock), "http_sync_port", log_, services_, instance_.next_id(), ep )->run(); } }; } // framework #endif