From 0662cbdfb737ff2a0dd84e7ade565e20222acaa9 Mon Sep 17 00:00:00 2001 From: eidheim Date: Tue, 4 Jul 2017 10:43:37 +0200 Subject: [PATCH] Added timeout to Server::Response::send, and some cleanup --- client_http.hpp | 62 ++++++++--------- client_https.hpp | 2 +- server_http.hpp | 157 +++++++++++++++++++++---------------------- tests/parse_test.cpp | 4 +- 4 files changed, 110 insertions(+), 115 deletions(-) diff --git a/client_http.hpp b/client_http.hpp index 7e2a008..965472a 100644 --- a/client_http.hpp +++ b/client_http.hpp @@ -72,6 +72,32 @@ namespace SimpleWeb { asio::streambuf content_buffer; Response() : content(content_buffer) {} + + void parse_header() { + std::string line; + getline(content, line); + size_t version_end = line.find(' '); + if(version_end != std::string::npos) { + if(5 < line.size()) + http_version = line.substr(5, version_end - 5); + if((version_end + 1) < line.size()) + status_code = line.substr(version_end + 1, line.size() - (version_end + 1) - 1); + + getline(content, line); + size_t param_end; + while((param_end = line.find(':')) != std::string::npos) { + size_t value_start = param_end + 1; + if((value_start) < line.size()) { + if(line[value_start] == ' ') + value_start++; + if(value_start < line.size()) + header.insert(std::make_pair(line.substr(0, param_end), line.substr(value_start, line.size() - value_start - 1))); + } + + getline(content, line); + } + } + } }; class Config { @@ -107,9 +133,9 @@ namespace SimpleWeb { class Session { public: - Session(std::shared_ptr> &&self, const std::shared_ptr &connection, std::unique_ptr &&request_buffer) - : self(std::move(self)), connection(connection), request_buffer(std::move(request_buffer)), response(new Response()) {} - std::shared_ptr> self; + Session(const std::shared_ptr> &client, const std::shared_ptr &connection, std::unique_ptr &&request_buffer) + : client(client), connection(connection), request_buffer(std::move(request_buffer)), response(new Response()) {} + std::shared_ptr> client; std::shared_ptr connection; std::unique_ptr request_buffer; std::shared_ptr response; @@ -119,7 +145,7 @@ namespace SimpleWeb { void set_timeout(long seconds = 0) { if(seconds == 0) - seconds = self->config.timeout; + seconds = client->config.timeout; if(seconds == 0) { timer = nullptr; return; @@ -372,32 +398,6 @@ namespace SimpleWeb { return parsed_host_port; } - void parse_response_header(std::shared_ptr &response) const { - std::string line; - getline(response->content, line); - size_t version_end = line.find(' '); - if(version_end != std::string::npos) { - if(5 < line.size()) - response->http_version = line.substr(5, version_end - 5); - if((version_end + 1) < line.size()) - response->status_code = line.substr(version_end + 1, line.size() - (version_end + 1) - 1); - - getline(response->content, line); - size_t param_end; - while((param_end = line.find(':')) != std::string::npos) { - size_t value_start = param_end + 1; - if((value_start) < line.size()) { - if(line[value_start] == ' ') - value_start++; - if(value_start < line.size()) - response->header.insert(std::make_pair(line.substr(0, param_end), line.substr(value_start, line.size() - value_start - 1))); - } - - getline(response->content, line); - } - } - } - void write(std::shared_ptr &session) { session->set_timeout(); asio::async_write(*session->connection->socket, session->request_buffer->data(), [this, session](const error_code &ec, size_t /*bytes_transferred*/) mutable { @@ -420,7 +420,7 @@ namespace SimpleWeb { size_t num_additional_bytes = session->response->content_buffer.size() - bytes_transferred; - this->parse_response_header(session->response); + session->response->parse_header(); auto header_it = session->response->header.find("Content-Length"); if(header_it != session->response->header.end()) { diff --git a/client_https.hpp b/client_https.hpp index dd3b480..999ec72 100644 --- a/client_https.hpp +++ b/client_https.hpp @@ -80,7 +80,7 @@ namespace SimpleWeb { asio::async_read_until(session->connection->socket->next_layer(), response->content_buffer, "\r\n\r\n", [this, session, response](const error_code &ec, size_t /*bytes_transferred*/) mutable { session->cancel_timeout(); if(!ec) { - this->parse_response_header(response); + response->parse_header(); if(response->status_code.empty() || response->status_code.compare(0, 3, "200") != 0) { session->connection->close(); session->callback(make_error_code::make_error_code(errc::permission_denied)); diff --git a/server_http.hpp b/server_http.hpp index f208643..9dd4d55 100644 --- a/server_http.hpp +++ b/server_http.hpp @@ -59,11 +59,9 @@ namespace SimpleWeb { asio::streambuf streambuf; - std::shared_ptr io_service; - std::shared_ptr socket; + std::shared_ptr session; - Response(const std::shared_ptr &io_service, const std::shared_ptr &socket) - : std::ostream(&streambuf), io_service(io_service), socket(socket) {} + Response(const std::shared_ptr &session) : std::ostream(&streambuf), session(session) {} template void write_header(const CaseInsensitiveMultimap &header, size_type size) { @@ -91,7 +89,8 @@ namespace SimpleWeb { /// Use this function if you need to recursively send parts of a longer message void send(const std::function &callback = nullptr) { auto self = this->shared_from_this(); - asio::async_write(*socket, streambuf, [self, callback](const error_code &ec, size_t /*bytes_transferred*/) { + session->set_timeout(session->server->config.timeout_content); + asio::async_write(*session->socket, streambuf, [self, callback](const error_code &ec, size_t /*bytes_transferred*/) { if(callback) callback(ec); }); @@ -191,6 +190,8 @@ namespace SimpleWeb { } private: + asio::streambuf streambuf; + Request(const socket_type &socket) : content(streambuf) { try { remote_endpoint_address = socket.lowest_layer().remote_endpoint().address().to_string(); @@ -200,16 +201,70 @@ namespace SimpleWeb { } } - asio::streambuf streambuf; + bool parse() { + std::string line; + getline(content, line); + size_t method_end; + if((method_end = line.find(' ')) != std::string::npos) { + method = line.substr(0, method_end); + + size_t query_start = std::string::npos; + size_t path_and_query_string_end = std::string::npos; + for(size_t i = method_end + 1; i < line.size(); ++i) { + if(line[i] == '?' && (i + 1) < line.size()) + query_start = i + 1; + else if(line[i] == ' ') { + path_and_query_string_end = i; + break; + } + } + if(path_and_query_string_end != std::string::npos) { + if(query_start != std::string::npos) { + path = line.substr(method_end + 1, query_start - method_end - 2); + query_string = line.substr(query_start, path_and_query_string_end - query_start); + } + else + path = line.substr(method_end + 1, path_and_query_string_end - method_end - 1); + + size_t protocol_end; + if((protocol_end = line.find('/', path_and_query_string_end + 1)) != std::string::npos) { + if(line.compare(path_and_query_string_end + 1, protocol_end - path_and_query_string_end - 1, "HTTP") != 0) + return false; + http_version = line.substr(protocol_end + 1, line.size() - protocol_end - 2); + } + else + return false; + + getline(content, line); + size_t param_end; + while((param_end = line.find(':')) != std::string::npos) { + size_t value_start = param_end + 1; + if(value_start < line.size()) { + if(line[value_start] == ' ') + value_start++; + if(value_start < line.size()) + header.emplace(line.substr(0, param_end), line.substr(value_start, line.size() - value_start - 1)); + } + + getline(content, line); + } + } + else + return false; + } + else + return false; + return true; + } }; protected: class Session { public: - Session(const std::shared_ptr> &self, const std::shared_ptr &socket) - : self(self), socket(socket), request(new Request(*this->socket)) {} + Session(const std::shared_ptr> &server, const std::shared_ptr &socket) + : server(server), socket(socket), request(new Request(*this->socket)) {} - std::shared_ptr> self; + std::shared_ptr> server; std::shared_ptr socket; std::shared_ptr request; @@ -359,7 +414,7 @@ namespace SimpleWeb { //streambuf (maybe some bytes of the content) is appended to in the async_read-function below (for retrieving content). size_t num_additional_bytes = session->request->streambuf.size() - bytes_transferred; - if(!this->parse_request(session)) + if(!session->request->parse()) return; //If content, read that as well @@ -395,62 +450,6 @@ namespace SimpleWeb { }); } - bool parse_request(std::shared_ptr &session) const { - std::string line; - getline(session->request->content, line); - size_t method_end; - if((method_end = line.find(' ')) != std::string::npos) { - session->request->method = line.substr(0, method_end); - - size_t query_start = std::string::npos; - size_t path_and_query_string_end = std::string::npos; - for(size_t i = method_end + 1; i < line.size(); ++i) { - if(line[i] == '?' && (i + 1) < line.size()) - query_start = i + 1; - else if(line[i] == ' ') { - path_and_query_string_end = i; - break; - } - } - if(path_and_query_string_end != std::string::npos) { - if(query_start != std::string::npos) { - session->request->path = line.substr(method_end + 1, query_start - method_end - 2); - session->request->query_string = line.substr(query_start, path_and_query_string_end - query_start); - } - else - session->request->path = line.substr(method_end + 1, path_and_query_string_end - method_end - 1); - - size_t protocol_end; - if((protocol_end = line.find('/', path_and_query_string_end + 1)) != std::string::npos) { - if(line.compare(path_and_query_string_end + 1, protocol_end - path_and_query_string_end - 1, "HTTP") != 0) - return false; - session->request->http_version = line.substr(protocol_end + 1, line.size() - protocol_end - 2); - } - else - return false; - - getline(session->request->content, line); - size_t param_end; - while((param_end = line.find(':')) != std::string::npos) { - size_t value_start = param_end + 1; - if(value_start < line.size()) { - if(line[value_start] == ' ') - value_start++; - if(value_start < line.size()) - session->request->header.emplace(line.substr(0, param_end), line.substr(value_start, line.size() - value_start - 1)); - } - - getline(session->request->content, line); - } - } - else - return false; - } - else - return false; - return true; - } - void find_resource(std::shared_ptr &session) { //Upgrade connection if(on_upgrade) { @@ -479,37 +478,33 @@ namespace SimpleWeb { void write_response(std::shared_ptr &session, std::function::Response> &, std::shared_ptr::Request> &)> &resource_function) { - auto self = session->self; - auto request = session->request; session->set_timeout(config.timeout_content); - auto timer = session->timer; - auto response = std::shared_ptr(new Response(io_service, session->socket), [self, request, timer](Response *response_ptr) mutable { + auto response = std::shared_ptr(new Response(session), [this](Response *response_ptr) mutable { auto response = std::shared_ptr(response_ptr); - response->send([self, response, request, timer](const error_code &ec) mutable { - if(timer) - timer->cancel(); + response->send([this, response](const error_code &ec) mutable { + response->session->cancel_timeout(); if(!ec) { if(response->close_connection_after_response) return; - auto range = request->header.equal_range("Connection"); + auto range = response->session->request->header.equal_range("Connection"); for(auto it = range.first; it != range.second; it++) { if(case_insensitive_equal(it->second, "close")) return; else if(case_insensitive_equal(it->second, "keep-alive")) { - auto session = std::make_shared(self, response->socket); - self->read_request_and_content(session); + auto new_session = std::make_shared(response->session->server, response->session->socket); + this->read_request_and_content(new_session); return; } } - if(request->http_version >= "1.1") { - auto session = std::make_shared(self, response->socket); - self->read_request_and_content(session); + if(response->session->request->http_version >= "1.1") { + auto new_session = std::make_shared(response->session->server, response->session->socket); + this->read_request_and_content(new_session); return; } } - else if(self->on_error) - self->on_error(request, ec); + else if(this->on_error) + this->on_error(response->session->request, ec); }); }); diff --git a/tests/parse_test.cpp b/tests/parse_test.cpp index 8be35fd..fe33de5 100644 --- a/tests/parse_test.cpp +++ b/tests/parse_test.cpp @@ -23,7 +23,7 @@ public: stream << "TestHeader3:test3b\r\n"; stream << "\r\n"; - assert(parse_request(session)); + assert(session->request->parse()); assert(session->request->method == "GET"); assert(session->request->path == "/test/"); @@ -81,7 +81,7 @@ public: stream << "TestHeader3:test3b\r\n"; stream << "\r\n"; - parse_response_header(response); + response->parse_header(); assert(response->http_version == "1.1"); assert(response->status_code == "200 OK");