Support expect continue in upgrade.

Closes #264.
This commit is contained in:
Klemens Morgenstern
2022-10-10 17:31:09 +08:00
committed by Klemens Morgenstern
parent b30e4fbbda
commit b0c49748a2
4 changed files with 126 additions and 1 deletions

View File

@@ -1,3 +1,9 @@
Version 340:
* Expect header field with the "100-continue" is handled in upgrade.
--------------------------------------------------------------------------------
Version 339:
* BOOST_BEAST_USE_STD_STRING_VIEW is replaced by boost/core string_view.

View File

@@ -137,7 +137,7 @@ receive_expect_100_continue(
return;
// Check for the Expect field value
if(parser.get()[field::expect] == "100-continue")
if(beast::iequals(parser.get()[field::expect], "100-continue"))
{
// send 100 response
response<empty_body> res;

View File

@@ -171,6 +171,8 @@ class stream<NextLayer, deflateSupported>::response_op
boost::weak_ptr<impl_type> wp_;
error_code result_; // must come before res_
response_type& res_;
http::response<http::empty_body> res_100_;
bool needs_res_100_{false};
public:
template<
@@ -192,6 +194,15 @@ public:
, res_(beast::allocate_stable<response_type>(*this,
sp->build_response(req, decorator, result_)))
{
auto itr = req.find(http::field::expect);
if (itr != req.end() && iequals(itr->value(), "100-continue")) // do
{
res_100_.version(res_.version());
res_100_.set(http::field::server, res_[http::field::server]);
res_100_.result(http::status::continue_);
res_100_.prepare_payload();
needs_res_100_ = true;
}
(*this)({}, 0, cont);
}
@@ -213,6 +224,16 @@ public:
impl.change_status(status::handshake);
impl.update_timer(this->get_executor());
if (needs_res_100_)
{
BOOST_ASIO_CORO_YIELD
{
BOOST_ASIO_HANDLER_LOCATION((__FILE__, __LINE__, "websocket::async_accept"));
http::async_write(
impl.stream(), res_100_, std::move(*this));
}
}
// Send response
BOOST_ASIO_CORO_YIELD
{
@@ -323,6 +344,7 @@ public:
// the handler.
auto const req = p_.release();
auto const decorator = d_;
response_op<Handler>(
this->release_handler(),
sp, req, decorator, true);
@@ -417,6 +439,20 @@ do_accept(
error_code result;
auto const res = impl_->build_response(req, decorator, result);
auto itr = req.find(http::field::expect);
if (itr != req.end() && iequals(itr->value(), "100-continue")) // do
{
http::response<http::empty_body> res_100;
res_100.version(res.version());
res_100.set(http::field::server, res[http::field::server]);
res_100.result(http::status::continue_);
res_100.prepare_payload();
http::write(impl_->stream(), res_100, ec);
if (ec)
return;
}
http::write(impl_->stream(), res, ec);
if(ec)
return;

View File

@@ -816,6 +816,87 @@ public:
}
}
void testIssue264Sync()
{
net::io_context ioc;
using tcp = net::ip::tcp;
stream<tcp::socket> ws1(ioc);
tcp::socket s2(ioc);
test::connect(ws1.next_layer(), s2);
http::request<http::empty_body> req{http::verb::get, "/api", 11};
req.set(http::field::connection, "upgrade");
req.set(http::field::upgrade, "websocket");
req.set(http::field::expect, "100-continue");
req.set(http::field::host, "test");
req.set(http::field::sec_websocket_version, "13");
req.set(http::field::sec_websocket_key, "1234");
req.prepare_payload();
std::thread thr{
[&]
{
beast::error_code ec;
ws1.accept(ec);
BEAST_EXPECTS(!ec, ec.message());
}};
http::async_write(s2, req, test::success_handler());
http::response<http::empty_body> res1, res2;
flat_buffer buf;
http::async_read(s2, buf, res1,
[&](error_code ec, std::size_t)
{
BEAST_EXPECTS(!ec, ec.message());
BEAST_EXPECTS(res1.result() == http::status::continue_, obsolete_reason(res1.result()));
if (res1.result() == http::status::continue_)
http::async_read(s2, buf, res2, test::success_handler());
});
test::run_for(ioc, std::chrono::seconds(1));
thr.join();
}
void testIssue264Async()
{
net::io_context ioc;
using tcp = net::ip::tcp;
stream<tcp::socket> ws1(ioc);
tcp::socket s2(ioc);
test::connect(ws1.next_layer(), s2);
http::request<http::empty_body> req{http::verb::get, "/api", 11};
req.set(http::field::connection, "upgrade");
req.set(http::field::upgrade, "websocket");
req.set(http::field::expect, "100-continue");
req.set(http::field::host, "test");
req.set(http::field::sec_websocket_version, "13");
req.set(http::field::sec_websocket_key, "1234");
req.prepare_payload();
http::async_write(s2, req, test::success_handler());
http::response<http::empty_body> res1, res2;
ws1.async_accept(test::success_handler());
flat_buffer buf;
http::async_read(s2, buf, res1,
[&](error_code ec, std::size_t)
{
BEAST_EXPECTS(!ec, ec.message());
BEAST_EXPECTS(res1.result() == http::status::continue_, obsolete_reason(res1.result()));
if (res1.result() == http::status::continue_)
http::async_read(s2, buf, res2, test::success_handler());
});
test::run_for(ioc, std::chrono::seconds(1));
}
#if BOOST_ASIO_HAS_CO_AWAIT
void testAwaitableCompiles(
stream<net::ip::tcp::socket>& s,
@@ -850,6 +931,8 @@ public:
#if BOOST_ASIO_HAS_CO_AWAIT
boost::ignore_unused(&accept_test::testAwaitableCompiles);
#endif
testIssue264Sync();
testIssue264Async();
}
};