From 7e61a486072c75d027358e14cdf08b28eff15d6e Mon Sep 17 00:00:00 2001 From: chriskohlhoff Date: Mon, 3 Jul 2017 23:03:01 +1000 Subject: [PATCH] Various improvements to http_server_fast.cpp: fix #578 - Receive request in a single read - Use fields_alloc for response - Fix command line usage information - Add command line option to spin the io_service --- CHANGELOG.md | 1 + example/http-server-fast/http_server_fast.cpp | 101 +++++++++--------- 2 files changed, 49 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26dd255e..16fa22e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ HTTP: * Refactor file_body for best practices * Add http-server-threaded example * Documentation tidying +* Various improvements to http_server_fast.cpp WebSocket: diff --git a/example/http-server-fast/http_server_fast.cpp b/example/http-server-fast/http_server_fast.cpp index f5c2c206..84c09a56 100644 --- a/example/http-server-fast/http_server_fast.cpp +++ b/example/http-server-fast/http_server_fast.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -34,11 +35,7 @@ public: http_worker(tcp::acceptor& acceptor, const std::string& doc_root) : acceptor_(acceptor), - doc_root_(doc_root), - socket_(acceptor.get_io_service()), - alloc_(8192), - request_deadline_(acceptor.get_io_service(), - std::chrono::steady_clock::time_point::max()) + doc_root_(doc_root) { } @@ -49,6 +46,7 @@ public: } private: + using alloc_t = fields_alloc; using request_body_t = http::basic_dynamic_body>; // The acceptor used to listen for incoming connections. @@ -58,24 +56,26 @@ private: std::string doc_root_; // The socket for the currently connected client. - tcp::socket socket_; + tcp::socket socket_{acceptor_.get_io_service()}; // The buffer for performing reads beast::static_buffer_n<8192> buffer_; + // The allocator used for the fields in the request and reply. + alloc_t alloc_{8192}; + // The parser for reading the requests - using alloc_type = fields_alloc; - alloc_type alloc_; - boost::optional> parser_; + boost::optional> parser_; // The timer putting a time limit on requests. - boost::asio::basic_waitable_timer request_deadline_; + boost::asio::basic_waitable_timer request_deadline_{ + acceptor_.get_io_service(), (std::chrono::steady_clock::time_point::max)()}; // The response message. - http::response response_; + boost::optional>> response_; // The response serializer. - boost::optional> serializer_; + boost::optional>> serializer_; void accept() { @@ -98,12 +98,12 @@ private: request_deadline_.expires_from_now( std::chrono::seconds(60)); - read_header(); + read_request(); } }); } - void read_header() + void read_request() { // On each read the parser needs to be destroyed and // recreated. We store it in a boost::optional to @@ -121,21 +121,6 @@ private: std::make_tuple(), std::make_tuple(alloc_)); - http::async_read_header( - socket_, - buffer_, - *parser_, - [this](beast::error_code ec) - { - if (ec) - accept(); - else - read_body(); - }); - } - - void read_body() - { http::async_read( socket_, buffer_, @@ -149,29 +134,32 @@ private: }); } - void process_request(http::request> const& req) + void process_request(http::request> const& req) { - response_.version = 11; - response_.set(http::field::connection, "close"); + response_.emplace( + std::piecewise_construct, + std::make_tuple(), + std::make_tuple(alloc_)); + + response_->set(http::field::connection, "close"); switch (req.method()) { case http::verb::get: - response_.result(http::status::ok); - response_.set(http::field::server, "Beast"); + response_->result(http::status::ok); + response_->set(http::field::server, "Beast"); load_file(req.target()); 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"); - response_.body = "Invalid request-method '" + req.method_string().to_string() + "'"; + response_->result(http::status::bad_request); + response_->set(http::field::content_type, "text/plain"); + response_->body = "Invalid request-method '" + req.method_string().to_string() + "'"; break; } - response_.prepare_payload(); write_response(); } @@ -180,9 +168,9 @@ private: // Request path must be absolute and not contain "..". if (target.empty() || target[0] != '/' || target.find("..") != std::string::npos) { - response_.result(http::status::not_found); - response_.set(http::field::content_type, "text/plain"); - response_.body = "File not found\r\n"; + response_->result(http::status::not_found); + response_->set(http::field::content_type, "text/plain"); + response_->body = "File not found\r\n"; return; } @@ -193,22 +181,23 @@ private: std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary); if (!is) { - response_.result(http::status::not_found); - response_.set(http::field::content_type, "text/plain"); - response_.body = "File not found\r\n"; + response_->result(http::status::not_found); + response_->set(http::field::content_type, "text/plain"); + response_->body = "File not found\r\n"; return; } // Fill out the reply to be sent to the client. - response_.set(http::field::content_type, mime_type(target.to_string())); - response_.body.clear(); + response_->set(http::field::content_type, mime_type(target.to_string())); + response_->body.clear(); for (char buf[2048]; is.read(buf, sizeof(buf)).gcount() > 0;) - response_.body.append(buf, static_cast(is.gcount())); + response_->body.append(buf, static_cast(is.gcount())); } void write_response() { - serializer_.emplace(response_); + response_->prepare_payload(); + serializer_.emplace(*response_); http::async_write( socket_, @@ -216,6 +205,8 @@ private: [this](beast::error_code ec) { socket_.shutdown(tcp::socket::shutdown_send, ec); + serializer_.reset(); + response_.reset(); accept(); }); } @@ -247,13 +238,13 @@ int main(int argc, char* argv[]) try { // Check command line arguments. - if (argc != 5) + if (argc != 6) { - std::cerr << "Usage: http_server
\n"; + std::cerr << "Usage: http_server_fast
{spin|block}\n"; std::cerr << " For IPv4, try:\n"; - std::cerr << " receiver 0.0.0.0 80 . 100\n"; + std::cerr << " http_server_fast 0.0.0.0 80 . 100 block\n"; std::cerr << " For IPv6, try:\n"; - std::cerr << " receiver 0::0 80 . 100\n"; + std::cerr << " http_server_fast 0::0 80 . 100 block\n"; return EXIT_FAILURE; } @@ -261,6 +252,7 @@ int main(int argc, char* argv[]) unsigned short port = static_cast(std::atoi(argv[2])); std::string doc_root = argv[3]; int num_workers = std::atoi(argv[4]); + bool spin = (std::strcmp(argv[5], "spin") == 0); boost::asio::io_service ios{1}; tcp::acceptor acceptor{ios, {address, port}}; @@ -272,7 +264,10 @@ int main(int argc, char* argv[]) workers.back().start(); } - ios.run(); + if (spin) + for (;;) ios.poll(); + else + ios.run(); } catch (const std::exception& e) {