Skip to content
This repository has been archived by the owner on Jun 12, 2018. It is now read-only.

Support multiple calls to Response::send #229

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
The MIT License (MIT)

Copyright (c) 2014-2017 Ole Christian Eidheim
Copyright (c) 2018 Caterpillar Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
10 changes: 9 additions & 1 deletion server_http.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,17 @@ namespace SimpleWeb {

/// 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 {
// 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);
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();
auto lock = self->session->connection->handler_runner->continue_lock();
if(!lock)
Expand Down
23 changes: 23 additions & 0 deletions tests/io_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,20 @@ int main() {
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) {
response->write(request->content.string());
};
Expand Down Expand Up @@ -202,6 +216,15 @@ int main() {
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;
auto r = client.request("GET", "/info", "", {{"Test Parameter", "test value"}});
Expand Down