http write returns success on connection close (API Change):

fix #767

The write family of HTTP stream algorithms no longer returns
error::end_of_stream when the message indicates that the connection
should be closed.

Actions Required:

* Add code to servers to close the connection after successfully
  writing a message where `message::keep_alive()` would return `false`.
This commit is contained in:
Vinnie Falco
2017-10-22 07:03:04 -07:00
parent 885b9dfe0b
commit d0d4e0a740
16 changed files with 122 additions and 83 deletions
@@ -248,7 +248,8 @@ class session : public std::enable_shared_from_this<session>
&session::on_write,
self_.shared_from_this(),
std::placeholders::_1,
std::placeholders::_2)));
std::placeholders::_2,
! sp->keep_alive())));
}
};
@@ -331,20 +332,21 @@ public:
void
on_write(
boost::system::error_code ec,
std::size_t bytes_transferred)
std::size_t bytes_transferred,
bool close)
{
boost::ignore_unused(bytes_transferred);
if(ec == http::error::end_of_stream)
if(ec)
return fail(ec, "write");
if(close)
{
// This means we should close the connection, usually because
// the response indicated the "Connection: close" semantic.
return do_close();
}
if(ec)
return fail(ec, "write");
// We're done with the response so delete it
res_ = nullptr;
@@ -244,7 +244,8 @@ class session : public std::enable_shared_from_this<session>
&session::on_write,
self_.shared_from_this(),
std::placeholders::_1,
std::placeholders::_2)));
std::placeholders::_2,
! sp->keep_alive())));
}
};
@@ -309,20 +310,21 @@ public:
void
on_write(
boost::system::error_code ec,
std::size_t bytes_transferred)
std::size_t bytes_transferred,
bool close)
{
boost::ignore_unused(bytes_transferred);
if(ec == http::error::end_of_stream)
if(ec)
return fail(ec, "write");
if(close)
{
// This means we should close the connection, usually because
// the response indicated the "Connection: close" semantic.
return do_close();
}
if(ec)
return fail(ec, "write");
// We're done with the response so delete it
res_ = nullptr;
@@ -216,15 +216,18 @@ template<class Stream>
struct send_lambda
{
Stream& stream_;
bool& close_;
boost::system::error_code& ec_;
boost::asio::yield_context yield_;
explicit
send_lambda(
Stream& stream,
bool& close,
boost::system::error_code& ec,
boost::asio::yield_context yield)
: stream_(stream)
, close_(close)
, ec_(ec)
, yield_(yield)
{
@@ -234,6 +237,9 @@ struct send_lambda
void
operator()(http::message<isRequest, Body, Fields>&& msg) const
{
// Determine if we should close the connection after
close_ = ! msg.keep_alive();
// We need the serializer here because the serializer requires
// a non-const file_body, and the message oriented version of
// http::write only works with const messages.
@@ -250,6 +256,7 @@ do_session(
std::string const& doc_root,
boost::asio::yield_context yield)
{
bool close = false;
boost::system::error_code ec;
// Construct the stream around the socket
@@ -264,7 +271,7 @@ do_session(
boost::beast::flat_buffer buffer;
// This lambda is used to send messages
send_lambda<ssl::stream<tcp::socket&>> lambda{stream, ec, yield};
send_lambda<ssl::stream<tcp::socket&>> lambda{stream, close, ec, yield};
for(;;)
{
@@ -278,14 +285,14 @@ do_session(
// Send the response
handle_request(doc_root, std::move(req), lambda);
if(ec == http::error::end_of_stream)
if(ec)
return fail(ec, "write");
if(close)
{
// This means we should close the connection, usually because
// the response indicated the "Connection: close" semantic.
break;
}
if(ec)
return fail(ec, "write");
}
// Perform the SSL shutdown
+11 -4
View File
@@ -212,15 +212,18 @@ template<class Stream>
struct send_lambda
{
Stream& stream_;
bool& close_;
boost::system::error_code& ec_;
boost::asio::yield_context yield_;
explicit
send_lambda(
Stream& stream,
bool& close,
boost::system::error_code& ec,
boost::asio::yield_context yield)
: stream_(stream)
, close_(close)
, ec_(ec)
, yield_(yield)
{
@@ -230,6 +233,9 @@ struct send_lambda
void
operator()(http::message<isRequest, Body, Fields>&& msg) const
{
// Determine if we should close the connection after
close_ = ! msg.keep_alive();
// We need the serializer here because the serializer requires
// a non-const file_body, and the message oriented version of
// http::write only works with const messages.
@@ -245,13 +251,14 @@ do_session(
std::string const& doc_root,
boost::asio::yield_context yield)
{
bool close = false;
boost::system::error_code ec;
// This buffer is required to persist across reads
boost::beast::flat_buffer buffer;
// This lambda is used to send messages
send_lambda<tcp::socket> lambda{socket, ec, yield};
send_lambda<tcp::socket> lambda{socket, close, ec, yield};
for(;;)
{
@@ -265,14 +272,14 @@ do_session(
// Send the response
handle_request(doc_root, std::move(req), lambda);
if(ec == http::error::end_of_stream)
if(ec)
return fail(ec, "write");
if(close)
{
// This means we should close the connection, usually because
// the response indicated the "Connection: close" semantic.
break;
}
if(ec)
return fail(ec, "write");
}
// Send a TCP shutdown
@@ -209,8 +209,8 @@ private:
std::make_tuple(alloc_));
string_response_->result(status);
string_response_->keep_alive(false);
string_response_->set(http::field::server, "Beast");
string_response_->set(http::field::connection, "close");
string_response_->set(http::field::content_type, "text/plain");
string_response_->body() = error;
string_response_->prepare_payload();
@@ -265,8 +265,8 @@ private:
std::make_tuple(alloc_));
file_response_->result(http::status::ok);
file_response_->keep_alive(false);
file_response_->set(http::field::server, "Beast");
file_response_->set(http::field::connection, "close");
file_response_->set(http::field::content_type, mime_type(target.to_string()));
file_response_->body() = std::move(file);
file_response_->prepare_payload();
@@ -259,7 +259,8 @@ class session
&session::on_write,
self_.derived().shared_from_this(),
std::placeholders::_1,
std::placeholders::_2)));
std::placeholders::_2,
! sp->keep_alive())));
}
};
@@ -322,20 +323,21 @@ public:
void
on_write(
boost::system::error_code ec,
std::size_t bytes_transferred)
std::size_t bytes_transferred,
bool close)
{
boost::ignore_unused(bytes_transferred);
if(ec == http::error::end_of_stream)
if(ec)
return fail(ec, "write");
if(close)
{
// This means we should close the connection, usually because
// the response indicated the "Connection: close" semantic.
return derived().do_eof();
}
if(ec)
return fail(ec, "write");
// We're done with the response so delete it
res_ = nullptr;
@@ -100,8 +100,8 @@ private:
void
process_request()
{
response_.version(11);
response_.set(http::field::connection, "close");
response_.version(request_.version());
response_.keep_alive(false);
switch(request_.method())
{
@@ -251,7 +251,8 @@ class session
&session::loop,
self_.shared_from_this(),
std::placeholders::_1,
std::placeholders::_2)));
std::placeholders::_2,
! sp->keep_alive())));
}
};
@@ -283,14 +284,15 @@ public:
void
run()
{
loop({}, 0);
loop({}, 0, false);
}
#include <boost/asio/yield.hpp>
void
loop(
boost::system::error_code ec,
std::size_t bytes_transferred)
std::size_t bytes_transferred,
bool close)
{
boost::ignore_unused(bytes_transferred);
reenter(*this)
@@ -302,7 +304,8 @@ public:
&session::loop,
shared_from_this(),
std::placeholders::_1,
0)));
0,
false)));
if(ec)
return fail(ec, "handshake");
@@ -314,7 +317,8 @@ public:
&session::loop,
shared_from_this(),
std::placeholders::_1,
std::placeholders::_2)));
std::placeholders::_2,
false)));
if(ec == http::error::end_of_stream)
{
// The remote host closed the connection
@@ -325,14 +329,14 @@ public:
// Send the response
yield handle_request(doc_root_, std::move(req_), lambda_);
if(ec == http::error::end_of_stream)
if(ec)
return fail(ec, "write");
if(close)
{
// This means we should close the connection, usually because
// the response indicated the "Connection: close" semantic.
break;
}
if(ec)
return fail(ec, "write");
// We're done with the response so delete it
res_ = nullptr;
@@ -344,7 +348,8 @@ public:
&session::loop,
shared_from_this(),
std::placeholders::_1,
0)));
0,
false)));
if(ec)
return fail(ec, "shutdown");
@@ -248,7 +248,8 @@ class session
&session::loop,
self_.shared_from_this(),
std::placeholders::_1,
std::placeholders::_2)));
std::placeholders::_2,
! sp->keep_alive())));
}
};
@@ -277,14 +278,15 @@ public:
void
run()
{
loop({}, 0);
loop({}, 0, false);
}
#include <boost/asio/yield.hpp>
void
loop(
boost::system::error_code ec,
std::size_t bytes_transferred)
std::size_t bytes_transferred,
bool close)
{
boost::ignore_unused(bytes_transferred);
reenter(*this)
@@ -297,7 +299,8 @@ public:
&session::loop,
shared_from_this(),
std::placeholders::_1,
std::placeholders::_2)));
std::placeholders::_2,
false)));
if(ec == http::error::end_of_stream)
{
// The remote host closed the connection
@@ -308,14 +311,14 @@ public:
// Send the response
yield handle_request(doc_root_, std::move(req_), lambda_);
if(ec == http::error::end_of_stream)
if(ec)
return fail(ec, "write");
if(close)
{
// This means we should close the connection, usually because
// the response indicated the "Connection: close" semantic.
break;
}
if(ec)
return fail(ec, "write");
// We're done with the response so delete it
res_ = nullptr;
@@ -213,13 +213,16 @@ template<class Stream>
struct send_lambda
{
Stream& stream_;
bool& close_;
boost::system::error_code& ec_;
explicit
send_lambda(
Stream& stream,
bool& close,
boost::system::error_code& ec)
: stream_(stream)
, close_(close)
, ec_(ec)
{
}
@@ -228,6 +231,9 @@ struct send_lambda
void
operator()(http::message<isRequest, Body, Fields>&& msg) const
{
// Determine if we should close the connection after
close_ = ! msg.keep_alive();
// We need the serializer here because the serializer requires
// a non-const file_body, and the message oriented version of
// http::write only works with const messages.
@@ -243,6 +249,7 @@ do_session(
ssl::context& ctx,
std::string const& doc_root)
{
bool close = false;
boost::system::error_code ec;
// Construct the stream around the socket
@@ -257,7 +264,7 @@ do_session(
boost::beast::flat_buffer buffer;
// This lambda is used to send messages
send_lambda<ssl::stream<tcp::socket&>> lambda{stream, ec};
send_lambda<ssl::stream<tcp::socket&>> lambda{stream, close, ec};
for(;;)
{
@@ -271,14 +278,14 @@ do_session(
// Send the response
handle_request(doc_root, std::move(req), lambda);
if(ec == http::error::end_of_stream)
if(ec)
return fail(ec, "write");
if(close)
{
// This means we should close the connection, usually because
// the response indicated the "Connection: close" semantic.
break;
}
if(ec)
return fail(ec, "write");
}
// Perform the SSL shutdown
+11 -4
View File
@@ -211,13 +211,16 @@ template<class Stream>
struct send_lambda
{
Stream& stream_;
bool& close_;
boost::system::error_code& ec_;
explicit
send_lambda(
Stream& stream,
bool& close,
boost::system::error_code& ec)
: stream_(stream)
, close_(close)
, ec_(ec)
{
}
@@ -226,6 +229,9 @@ struct send_lambda
void
operator()(http::message<isRequest, Body, Fields>&& msg) const
{
// Determine if we should close the connection after
close_ = ! msg.keep_alive();
// We need the serializer here because the serializer requires
// a non-const file_body, and the message oriented version of
// http::write only works with const messages.
@@ -240,13 +246,14 @@ do_session(
tcp::socket& socket,
std::string const& doc_root)
{
bool close = false;
boost::system::error_code ec;
// This buffer is required to persist across reads
boost::beast::flat_buffer buffer;
// This lambda is used to send messages
send_lambda<tcp::socket> lambda{socket, ec};
send_lambda<tcp::socket> lambda{socket, close, ec};
for(;;)
{
@@ -260,14 +267,14 @@ do_session(
// Send the response
handle_request(doc_root, std::move(req), lambda);
if(ec == http::error::end_of_stream)
if(ec)
return fail(ec, "write");
if(close)
{
// This means we should close the connection, usually because
// the response indicated the "Connection: close" semantic.
break;
}
if(ec)
return fail(ec, "write");
}
// Send a TCP shutdown