Allow calling Response::send multiple times: copy the buffer before async_write
This commit is contained in:
parent
d59b7c234c
commit
6b91839cb6
2 changed files with 32 additions and 1 deletions
|
|
@ -89,9 +89,17 @@ 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) noexcept {
|
void send(const std::function<void(const error_code &)> &callback = nullptr) noexcept {
|
||||||
|
// Take a snapshot of the stream buffer. Otherwise async_write may not
|
||||||
|
// have consumed the buffer before the next call, in which case the
|
||||||
|
// data from the first call may be sent twice. Use a captured shared
|
||||||
|
// pointer to keep the snapshot alive during the async call.
|
||||||
|
auto stream_snapshot = std::make_shared<asio::streambuf>();
|
||||||
|
std::ostream snapshot_writer(stream_snapshot.get());
|
||||||
|
snapshot_writer << &streambuf;
|
||||||
|
|
||||||
session->connection->set_timeout(timeout_content);
|
session->connection->set_timeout(timeout_content);
|
||||||
auto self = this->shared_from_this(); // Keep Response instance alive through the following async_write
|
auto self = this->shared_from_this(); // Keep Response instance alive through the following async_write
|
||||||
asio::async_write(*session->connection->socket, streambuf, [self, callback](const error_code &ec, std::size_t /*bytes_transferred*/) {
|
asio::async_write(*session->connection->socket, *stream_snapshot, [self, callback, stream_snapshot](const error_code &ec, std::size_t /*bytes_transferred*/) {
|
||||||
self->session->connection->cancel_timeout();
|
self->session->connection->cancel_timeout();
|
||||||
auto lock = self->session->connection->handler_runner->continue_lock();
|
auto lock = self->session->connection->handler_runner->continue_lock();
|
||||||
if(!lock)
|
if(!lock)
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,20 @@ int main() {
|
||||||
assert(request->remote_endpoint_port() != 0);
|
assert(request->remote_endpoint_port() != 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
server.resource["^/string/dup$"]["POST"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
|
||||||
|
auto content = request->content.string();
|
||||||
|
|
||||||
|
// Send content twice, before it has a chance to be written to the socket.
|
||||||
|
*response << "HTTP/1.1 200 OK\r\nContent-Length: " << (content.length() * 2) << "\r\n\r\n"
|
||||||
|
<< content;
|
||||||
|
response->send();
|
||||||
|
*response << content;
|
||||||
|
response->send();
|
||||||
|
|
||||||
|
assert(!request->remote_endpoint_address().empty());
|
||||||
|
assert(request->remote_endpoint_port() != 0);
|
||||||
|
};
|
||||||
|
|
||||||
server.resource["^/string2$"]["POST"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
|
server.resource["^/string2$"]["POST"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
|
||||||
response->write(request->content.string());
|
response->write(request->content.string());
|
||||||
};
|
};
|
||||||
|
|
@ -202,6 +216,15 @@ int main() {
|
||||||
assert(output.str() == "A string");
|
assert(output.str() == "A string");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Test rapid calls to Response::send
|
||||||
|
stringstream output;
|
||||||
|
stringstream content("A string\n");
|
||||||
|
auto r = client.request("POST", "/string/dup", content);
|
||||||
|
output << r->content.rdbuf();
|
||||||
|
assert(output.str() == "A string\nA string\n");
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
stringstream output;
|
stringstream output;
|
||||||
auto r = client.request("GET", "/info", "", {{"Test Parameter", "test value"}});
|
auto r = client.request("GET", "/info", "", {{"Test Parameter", "test value"}});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue