Added timeout to Server::Response::send, and some cleanup
This commit is contained in:
parent
cc1a8fc614
commit
0662cbdfb7
4 changed files with 110 additions and 115 deletions
|
|
@ -72,6 +72,32 @@ namespace SimpleWeb {
|
||||||
asio::streambuf content_buffer;
|
asio::streambuf content_buffer;
|
||||||
|
|
||||||
Response() : content(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 {
|
class Config {
|
||||||
|
|
@ -107,9 +133,9 @@ namespace SimpleWeb {
|
||||||
|
|
||||||
class Session {
|
class Session {
|
||||||
public:
|
public:
|
||||||
Session(std::shared_ptr<ClientBase<socket_type>> &&self, const std::shared_ptr<Connection> &connection, std::unique_ptr<asio::streambuf> &&request_buffer)
|
Session(const std::shared_ptr<ClientBase<socket_type>> &client, const std::shared_ptr<Connection> &connection, std::unique_ptr<asio::streambuf> &&request_buffer)
|
||||||
: self(std::move(self)), connection(connection), request_buffer(std::move(request_buffer)), response(new Response()) {}
|
: client(client), connection(connection), request_buffer(std::move(request_buffer)), response(new Response()) {}
|
||||||
std::shared_ptr<ClientBase<socket_type>> self;
|
std::shared_ptr<ClientBase<socket_type>> client;
|
||||||
std::shared_ptr<Connection> connection;
|
std::shared_ptr<Connection> connection;
|
||||||
std::unique_ptr<asio::streambuf> request_buffer;
|
std::unique_ptr<asio::streambuf> request_buffer;
|
||||||
std::shared_ptr<Response> response;
|
std::shared_ptr<Response> response;
|
||||||
|
|
@ -119,7 +145,7 @@ namespace SimpleWeb {
|
||||||
|
|
||||||
void set_timeout(long seconds = 0) {
|
void set_timeout(long seconds = 0) {
|
||||||
if(seconds == 0)
|
if(seconds == 0)
|
||||||
seconds = self->config.timeout;
|
seconds = client->config.timeout;
|
||||||
if(seconds == 0) {
|
if(seconds == 0) {
|
||||||
timer = nullptr;
|
timer = nullptr;
|
||||||
return;
|
return;
|
||||||
|
|
@ -372,32 +398,6 @@ namespace SimpleWeb {
|
||||||
return parsed_host_port;
|
return parsed_host_port;
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse_response_header(std::shared_ptr<Response> &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) {
|
void write(std::shared_ptr<Session> &session) {
|
||||||
session->set_timeout();
|
session->set_timeout();
|
||||||
asio::async_write(*session->connection->socket, session->request_buffer->data(), [this, session](const error_code &ec, size_t /*bytes_transferred*/) mutable {
|
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;
|
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");
|
auto header_it = session->response->header.find("Content-Length");
|
||||||
if(header_it != session->response->header.end()) {
|
if(header_it != session->response->header.end()) {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
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();
|
session->cancel_timeout();
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
this->parse_response_header(response);
|
response->parse_header();
|
||||||
if(response->status_code.empty() || response->status_code.compare(0, 3, "200") != 0) {
|
if(response->status_code.empty() || response->status_code.compare(0, 3, "200") != 0) {
|
||||||
session->connection->close();
|
session->connection->close();
|
||||||
session->callback(make_error_code::make_error_code(errc::permission_denied));
|
session->callback(make_error_code::make_error_code(errc::permission_denied));
|
||||||
|
|
|
||||||
157
server_http.hpp
157
server_http.hpp
|
|
@ -59,11 +59,9 @@ namespace SimpleWeb {
|
||||||
|
|
||||||
asio::streambuf streambuf;
|
asio::streambuf streambuf;
|
||||||
|
|
||||||
std::shared_ptr<asio::io_service> io_service;
|
std::shared_ptr<Session> session;
|
||||||
std::shared_ptr<socket_type> socket;
|
|
||||||
|
|
||||||
Response(const std::shared_ptr<asio::io_service> &io_service, const std::shared_ptr<socket_type> &socket)
|
Response(const std::shared_ptr<Session> &session) : std::ostream(&streambuf), session(session) {}
|
||||||
: std::ostream(&streambuf), io_service(io_service), socket(socket) {}
|
|
||||||
|
|
||||||
template <class size_type>
|
template <class size_type>
|
||||||
void write_header(const CaseInsensitiveMultimap &header, size_type size) {
|
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
|
/// Use this function if you need to recursively send parts of a longer message
|
||||||
void send(const std::function<void(const error_code &)> &callback = nullptr) {
|
void send(const std::function<void(const error_code &)> &callback = nullptr) {
|
||||||
auto self = this->shared_from_this();
|
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)
|
if(callback)
|
||||||
callback(ec);
|
callback(ec);
|
||||||
});
|
});
|
||||||
|
|
@ -191,6 +190,8 @@ namespace SimpleWeb {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
asio::streambuf streambuf;
|
||||||
|
|
||||||
Request(const socket_type &socket) : content(streambuf) {
|
Request(const socket_type &socket) : content(streambuf) {
|
||||||
try {
|
try {
|
||||||
remote_endpoint_address = socket.lowest_layer().remote_endpoint().address().to_string();
|
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:
|
protected:
|
||||||
class Session {
|
class Session {
|
||||||
public:
|
public:
|
||||||
Session(const std::shared_ptr<ServerBase<socket_type>> &self, const std::shared_ptr<socket_type> &socket)
|
Session(const std::shared_ptr<ServerBase<socket_type>> &server, const std::shared_ptr<socket_type> &socket)
|
||||||
: self(self), socket(socket), request(new Request(*this->socket)) {}
|
: server(server), socket(socket), request(new Request(*this->socket)) {}
|
||||||
|
|
||||||
std::shared_ptr<ServerBase<socket_type>> self;
|
std::shared_ptr<ServerBase<socket_type>> server;
|
||||||
std::shared_ptr<socket_type> socket;
|
std::shared_ptr<socket_type> socket;
|
||||||
std::shared_ptr<Request> request;
|
std::shared_ptr<Request> 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).
|
//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;
|
size_t num_additional_bytes = session->request->streambuf.size() - bytes_transferred;
|
||||||
|
|
||||||
if(!this->parse_request(session))
|
if(!session->request->parse())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//If content, read that as well
|
//If content, read that as well
|
||||||
|
|
@ -395,62 +450,6 @@ namespace SimpleWeb {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool parse_request(std::shared_ptr<Session> &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> &session) {
|
void find_resource(std::shared_ptr<Session> &session) {
|
||||||
//Upgrade connection
|
//Upgrade connection
|
||||||
if(on_upgrade) {
|
if(on_upgrade) {
|
||||||
|
|
@ -479,37 +478,33 @@ namespace SimpleWeb {
|
||||||
|
|
||||||
void write_response(std::shared_ptr<Session> &session,
|
void write_response(std::shared_ptr<Session> &session,
|
||||||
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response> &, std::shared_ptr<typename ServerBase<socket_type>::Request> &)> &resource_function) {
|
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response> &, std::shared_ptr<typename ServerBase<socket_type>::Request> &)> &resource_function) {
|
||||||
auto self = session->self;
|
|
||||||
auto request = session->request;
|
|
||||||
session->set_timeout(config.timeout_content);
|
session->set_timeout(config.timeout_content);
|
||||||
auto timer = session->timer;
|
auto response = std::shared_ptr<Response>(new Response(session), [this](Response *response_ptr) mutable {
|
||||||
auto response = std::shared_ptr<Response>(new Response(io_service, session->socket), [self, request, timer](Response *response_ptr) mutable {
|
|
||||||
auto response = std::shared_ptr<Response>(response_ptr);
|
auto response = std::shared_ptr<Response>(response_ptr);
|
||||||
response->send([self, response, request, timer](const error_code &ec) mutable {
|
response->send([this, response](const error_code &ec) mutable {
|
||||||
if(timer)
|
response->session->cancel_timeout();
|
||||||
timer->cancel();
|
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
if(response->close_connection_after_response)
|
if(response->close_connection_after_response)
|
||||||
return;
|
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++) {
|
for(auto it = range.first; it != range.second; it++) {
|
||||||
if(case_insensitive_equal(it->second, "close"))
|
if(case_insensitive_equal(it->second, "close"))
|
||||||
return;
|
return;
|
||||||
else if(case_insensitive_equal(it->second, "keep-alive")) {
|
else if(case_insensitive_equal(it->second, "keep-alive")) {
|
||||||
auto session = std::make_shared<Session>(self, response->socket);
|
auto new_session = std::make_shared<Session>(response->session->server, response->session->socket);
|
||||||
self->read_request_and_content(session);
|
this->read_request_and_content(new_session);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(request->http_version >= "1.1") {
|
if(response->session->request->http_version >= "1.1") {
|
||||||
auto session = std::make_shared<Session>(self, response->socket);
|
auto new_session = std::make_shared<Session>(response->session->server, response->session->socket);
|
||||||
self->read_request_and_content(session);
|
this->read_request_and_content(new_session);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(self->on_error)
|
else if(this->on_error)
|
||||||
self->on_error(request, ec);
|
this->on_error(response->session->request, ec);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ public:
|
||||||
stream << "TestHeader3:test3b\r\n";
|
stream << "TestHeader3:test3b\r\n";
|
||||||
stream << "\r\n";
|
stream << "\r\n";
|
||||||
|
|
||||||
assert(parse_request(session));
|
assert(session->request->parse());
|
||||||
|
|
||||||
assert(session->request->method == "GET");
|
assert(session->request->method == "GET");
|
||||||
assert(session->request->path == "/test/");
|
assert(session->request->path == "/test/");
|
||||||
|
|
@ -81,7 +81,7 @@ public:
|
||||||
stream << "TestHeader3:test3b\r\n";
|
stream << "TestHeader3:test3b\r\n";
|
||||||
stream << "\r\n";
|
stream << "\r\n";
|
||||||
|
|
||||||
parse_response_header(response);
|
response->parse_header();
|
||||||
|
|
||||||
assert(response->http_version == "1.1");
|
assert(response->http_version == "1.1");
|
||||||
assert(response->status_code == "200 OK");
|
assert(response->status_code == "200 OK");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue