diff --git a/CHANGELOG.md b/CHANGELOG.md index 61e9a1fa..dd4bedf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ * CMake hide command lines in .vcxproj Output windows" +API Changes: + +* Provide websocket::stream accept() overloads + -------------------------------------------------------------------------------- 1.0.0-b36 diff --git a/extras/beast/test/string_iostream.hpp b/extras/beast/test/string_iostream.hpp new file mode 100644 index 00000000..81417084 --- /dev/null +++ b/extras/beast/test/string_iostream.hpp @@ -0,0 +1,166 @@ +// +// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BEAST_TEST_STRING_IOSTREAM_HPP +#define BEAST_TEST_STRING_IOSTREAM_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace beast { +namespace test { + +/** A SyncStream and AsyncStream that reads from a string and writes to another string. + + This class behaves like a socket, except that written data is + appended to a string exposed as a public data member, and when + data is read it comes from a string provided at construction. +*/ +class string_iostream +{ + std::string s_; + boost::asio::const_buffer cb_; + boost::asio::io_service& ios_; + std::size_t read_max_; + +public: + std::string str; + + string_iostream(boost::asio::io_service& ios, + std::string s, std::size_t read_max = + (std::numeric_limits::max)()) + : s_(std::move(s)) + , cb_(boost::asio::buffer(s_)) + , ios_(ios) + , read_max_(read_max) + { + } + + boost::asio::io_service& + get_io_service() + { + return ios_; + } + + template + std::size_t + read_some(MutableBufferSequence const& buffers) + { + error_code ec; + auto const n = read_some(buffers, ec); + if(ec) + throw system_error{ec}; + return n; + } + + template + std::size_t + read_some(MutableBufferSequence const& buffers, + error_code& ec) + { + auto const n = boost::asio::buffer_copy( + buffers, prepare_buffer(read_max_, cb_)); + if(n > 0) + cb_ = cb_ + n; + else + ec = boost::asio::error::eof; + return n; + } + + template + typename async_completion::result_type + async_read_some(MutableBufferSequence const& buffers, + ReadHandler&& handler) + { + auto const n = boost::asio::buffer_copy( + buffers, boost::asio::buffer(s_)); + error_code ec; + if(n > 0) + s_.erase(0, n); + else + ec = boost::asio::error::eof; + async_completion completion{handler}; + ios_.post(bind_handler( + completion.handler, ec, n)); + return completion.result.get(); + } + + template + std::size_t + write_some(ConstBufferSequence const& buffers) + { + error_code ec; + auto const n = write_some(buffers, ec); + if(ec) + throw system_error{ec}; + return n; + } + + template + std::size_t + write_some( + ConstBufferSequence const& buffers, error_code&) + { + auto const n = buffer_size(buffers); + using boost::asio::buffer_size; + using boost::asio::buffer_cast; + str.reserve(str.size() + n); + for(auto const& buffer : buffers) + str.append(buffer_cast(buffer), + buffer_size(buffer)); + return n; + } + + template + typename async_completion< + WriteHandler, void(error_code)>::result_type + async_write_some(ConstBufferSequence const& buffers, + WriteHandler&& handler) + { + error_code ec; + auto const bytes_transferred = write_some(buffers, ec); + async_completion< + WriteHandler, void(error_code, std::size_t) + > completion{handler}; + get_io_service().post( + bind_handler(completion.handler, ec, bytes_transferred)); + return completion.result.get(); + } + + friend + void + teardown(websocket::teardown_tag, + string_iostream& stream, + boost::system::error_code& ec) + { + } + + template + friend + void + async_teardown(websocket::teardown_tag, + string_iostream& stream, + TeardownHandler&& handler) + { + stream.get_io_service().post( + bind_handler(std::move(handler), + error_code{})); + } +}; + +} // test +} // beast + +#endif diff --git a/extras/beast/test/string_istream.hpp b/extras/beast/test/string_istream.hpp index e7c32064..c630505c 100644 --- a/extras/beast/test/string_istream.hpp +++ b/extras/beast/test/string_istream.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -125,6 +126,26 @@ public: error_code{}, boost::asio::buffer_size(buffers))); return completion.result.get(); } + + friend + void + teardown(websocket::teardown_tag, + string_istream& stream, + boost::system::error_code& ec) + { + } + + template + friend + void + async_teardown(websocket::teardown_tag, + string_istream& stream, + TeardownHandler&& handler) + { + stream.get_io_service().post( + bind_handler(std::move(handler), + error_code{})); + } }; } // test diff --git a/extras/beast/test/string_ostream.hpp b/extras/beast/test/string_ostream.hpp index a939da7a..52a9bf5d 100644 --- a/extras/beast/test/string_ostream.hpp +++ b/extras/beast/test/string_ostream.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -110,6 +111,26 @@ public: bind_handler(completion.handler, ec, bytes_transferred)); return completion.result.get(); } + + friend + void + teardown(websocket::teardown_tag, + string_ostream& stream, + boost::system::error_code& ec) + { + } + + template + friend + void + async_teardown(websocket::teardown_tag, + string_ostream& stream, + TeardownHandler&& handler) + { + stream.get_io_service().post( + bind_handler(std::move(handler), + error_code{})); + } }; } // test diff --git a/include/beast/websocket/impl/accept.ipp b/include/beast/websocket/impl/accept.ipp index 259b3ab8..489440b7 100644 --- a/include/beast/websocket/impl/accept.ipp +++ b/include/beast/websocket/impl/accept.ipp @@ -36,7 +36,6 @@ class stream::response_op bool cont; stream& ws; http::response_header res; - error_code final_ec; int state = 0; template @@ -47,11 +46,22 @@ class stream::response_op , ws(ws_) , res(ws_.build_response(req)) { - // can't call stream::reset() here - // otherwise accept_op will malfunction - // - if(res.status != 101) - final_ec = error::handshake_failed; + } + + template + data(Handler&, stream& ws_, + http::header const& req, + Buffers const& buffers, bool cont_) + : cont(cont_) + , ws(ws_) + , res(ws_.build_response(req)) + { + using boost::asio::buffer_copy; + using boost::asio::buffer_size; + // VFALCO What about catch(std::length_error const&)? + ws.stream_.buffer().commit(buffer_copy( + ws.stream_.buffer().prepare( + buffer_size(buffers)), buffers)); } }; @@ -126,7 +136,8 @@ operator()(error_code ec, bool again) // sent response case 1: d.state = 99; - ec = d.final_ec; + if(d.res.status != 101) + ec = error::handshake_failed; if(! ec) { pmd_read( @@ -154,6 +165,10 @@ class stream::accept_op http::header_parser p; int state = 0; + // VFALCO These lines are formatted to work around a codecov defect + data(Handler& handler, stream& ws_) : cont(beast_asio_helpers::is_continuation(handler)) + , ws(ws_) {} + template data(Handler& handler, stream& ws_, Buffers const& buffers) @@ -163,7 +178,7 @@ class stream::accept_op { using boost::asio::buffer_copy; using boost::asio::buffer_size; - ws.reset(); + // VFALCO What about catch(std::length_error const&)? ws.stream_.buffer().commit(buffer_copy( ws.stream_.buffer().prepare( buffer_size(buffers)), buffers)); @@ -185,11 +200,6 @@ public: (*this)(error_code{}, 0, false); } - void operator()(error_code const& ec) - { - (*this)(ec, 0); - } - void operator()(error_code ec, std::size_t bytes_used, bool again = true); @@ -264,59 +274,7 @@ upcall: d_.invoke(ec); } -template -template -typename async_completion< - AcceptHandler, void(error_code)>::result_type -stream:: -async_accept(AcceptHandler&& handler) -{ - static_assert(is_AsyncStream::value, - "AsyncStream requirements requirements not met"); - return async_accept(boost::asio::null_buffers{}, - std::forward(handler)); -} - -template -template -typename async_completion< - AcceptHandler, void(error_code)>::result_type -stream:: -async_accept(ConstBufferSequence const& bs, AcceptHandler&& handler) -{ - static_assert(is_AsyncStream::value, - "AsyncStream requirements requirements not met"); - static_assert(beast::is_ConstBufferSequence< - ConstBufferSequence>::value, - "ConstBufferSequence requirements not met"); - beast::async_completion< - AcceptHandler, void(error_code) - > completion{handler}; - accept_op{ - completion.handler, *this, bs}; - return completion.result.get(); -} - -template -template -typename async_completion< - AcceptHandler, void(error_code)>::result_type -stream:: -async_accept(http::header const& req, - AcceptHandler&& handler) -{ - static_assert(is_AsyncStream::value, - "AsyncStream requirements requirements not met"); - beast::async_completion< - AcceptHandler, void(error_code) - > completion{handler}; - reset(); - response_op{ - completion.handler, *this, req, - beast_asio_helpers:: - is_continuation(completion.handler)}; - return completion.result.get(); -} +//------------------------------------------------------------------------------ template void @@ -326,7 +284,7 @@ accept() static_assert(is_SyncStream::value, "SyncStream requirements not met"); error_code ec; - accept(boost::asio::null_buffers{}, ec); + accept(ec); if(ec) throw system_error{ec}; } @@ -338,7 +296,8 @@ accept(error_code& ec) { static_assert(is_SyncStream::value, "SyncStream requirements not met"); - accept(boost::asio::null_buffers{}, ec); + reset(); + do_accept(ec); } template @@ -366,35 +325,28 @@ accept(ConstBufferSequence const& buffers, error_code& ec) { static_assert(is_SyncStream::value, "SyncStream requirements not met"); - static_assert(beast::is_ConstBufferSequence< + static_assert(is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); + reset(); using boost::asio::buffer_copy; using boost::asio::buffer_size; - reset(); stream_.buffer().commit(buffer_copy( stream_.buffer().prepare( buffer_size(buffers)), buffers)); - http::header_parser p; - auto const bytes_used = http::read_some( - next_layer(), stream_.buffer(), p, ec); - if(ec) - return; - BOOST_ASSERT(p.got_header()); - stream_.buffer().consume(bytes_used); - accept(p.get(), ec); + do_accept(ec); } template template void stream:: -accept(http::header const& request) +accept(http::header const& req) { static_assert(is_SyncStream::value, "SyncStream requirements not met"); error_code ec; - accept(request, ec); + accept(req, ec); if(ec) throw system_error{ec}; } @@ -409,19 +361,134 @@ accept(http::header const& req, static_assert(is_SyncStream::value, "SyncStream requirements not met"); reset(); - auto const res = build_response(req); - http::write(stream_, res, ec); + do_accept(req, ec); +} + +template +template +void +stream:: +accept( + http::header const& req, + ConstBufferSequence const& buffers) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + static_assert(is_ConstBufferSequence< + ConstBufferSequence>::value, + "ConstBufferSequence requirements not met"); + error_code ec; + accept(req, buffers, ec); if(ec) - return; - if(res.status != 101) - { - ec = error::handshake_failed; - // VFALCO TODO Respect keep alive setting, perform - // teardown if Connection: close. - return; - } - pmd_read(pmd_config_, req.fields); - open(detail::role_type::server); + throw system_error{ec}; +} + +template +template +void +stream:: +accept( + http::header const& req, + ConstBufferSequence const& buffers, + error_code& ec) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + static_assert(is_ConstBufferSequence< + ConstBufferSequence>::value, + "ConstBufferSequence requirements not met"); + reset(); + using boost::asio::buffer_copy; + using boost::asio::buffer_size; + stream_.buffer().commit(buffer_copy( + stream_.buffer().prepare( + buffer_size(buffers)), buffers)); + do_accept(req, ec); +} + +//------------------------------------------------------------------------------ + +template +template +typename async_completion::result_type +stream:: +async_accept(AcceptHandler&& handler) +{ + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); + beast::async_completion completion{handler}; + reset(); + accept_op{ + completion.handler, *this}; + return completion.result.get(); +} + +template +template +typename async_completion::result_type +stream:: +async_accept(ConstBufferSequence const& buffers, + AcceptHandler&& handler) +{ + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); + static_assert(is_ConstBufferSequence< + ConstBufferSequence>::value, + "ConstBufferSequence requirements not met"); + beast::async_completion completion{handler}; + reset(); + accept_op{ + completion.handler, *this, buffers}; + return completion.result.get(); +} + +template +template +typename async_completion::result_type +stream:: +async_accept(http::header const& req, + AcceptHandler&& handler) +{ + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); + beast::async_completion completion{handler}; + reset(); + response_op{ + completion.handler, *this, req, + beast_asio_helpers:: + is_continuation(completion.handler)}; + return completion.result.get(); +} + +template +template +typename async_completion::result_type +stream:: +async_accept(http::header const& req, + ConstBufferSequence const& buffers, + AcceptHandler&& handler) +{ + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); + static_assert(is_ConstBufferSequence< + ConstBufferSequence>::value, + "ConstBufferSequence requirements not met"); + beast::async_completion completion{handler}; + reset(); + response_op{ + completion.handler, *this, req, buffers, + beast_asio_helpers:: + is_continuation(completion.handler)}; + return completion.result.get(); } } // websocket diff --git a/include/beast/websocket/impl/stream.ipp b/include/beast/websocket/impl/stream.ipp index 6e7b8ea8..8bba955d 100644 --- a/include/beast/websocket/impl/stream.ipp +++ b/include/beast/websocket/impl/stream.ipp @@ -82,6 +82,43 @@ reset() stream_.buffer().size()); } +template +void +stream:: +do_accept(error_code& ec) +{ + http::header_parser p; + auto const bytes_used = http::read_some( + next_layer(), stream_.buffer(), p, ec); + if(ec) + return; + BOOST_ASSERT(p.got_header()); + stream_.buffer().consume(bytes_used); + do_accept(p.get(), ec); +} + +template +template +void +stream:: +do_accept(http::header const& req, + error_code& ec) +{ + auto const res = build_response(req); + http::write(stream_, res, ec); + if(ec) + return; + if(res.status != 101) + { + ec = error::handshake_failed; + // VFALCO TODO Respect keep alive setting, perform + // teardown if Connection: close. + return; + } + pmd_read(pmd_config_, req.fields); + open(detail::role_type::server); +} + template http::request_header stream:: diff --git a/include/beast/websocket/stream.hpp b/include/beast/websocket/stream.hpp index 47f48bef..3f6dddab 100644 --- a/include/beast/websocket/stream.hpp +++ b/include/beast/websocket/stream.hpp @@ -13,14 +13,15 @@ #include #include #include -#include #include +#include #include #include #include #include #include #include +#include namespace beast { namespace websocket { @@ -340,25 +341,25 @@ public: /** Read and respond to a WebSocket HTTP Upgrade request. - This function is used to synchronously read a HTTP WebSocket - Upgrade request and send the HTTP response. The call blocks until - one of the following conditions is true: + This function is used to synchronously read an HTTP WebSocket + Upgrade request and send the HTTP response. The call blocks + until one of the following conditions is true: - @li A HTTP request finishes receiving, and a HTTP response finishes - sending. + @li The HTTP request finishes receiving, and the HTTP response + finishes sending. @li An error occurs on the stream. - This function is implemented in terms of one or more calls to the - next layer's `read_some` and `write_some` functions. + This function is implemented in terms of one or more calls to + the next layer's `read_some` and `write_some` functions. - If the stream receives a valid HTTP WebSocket Upgrade request, a - HTTP response is sent back indicating a successful upgrade. When this - call returns, the stream is then ready to send and receive WebSocket - protocol frames and messages. + If the stream receives a valid HTTP WebSocket Upgrade request, + an HTTP response is sent back indicating a successful upgrade. + When this call returns, the stream is then ready to send and + receive WebSocket protocol frames and messages. - If the HTTP Upgrade request is invalid or cannot be satisfied, a - HTTP response is sent indicating the reason and status code + If the HTTP Upgrade request is invalid or cannot be satisfied, + an HTTP response is sent indicating the reason and status code (typically 400, "Bad Request"). This counts as a failure. @throws system_error Thrown on failure. @@ -368,25 +369,25 @@ public: /** Read and respond to a WebSocket HTTP Upgrade request. - This function is used to synchronously read a HTTP WebSocket - Upgrade request and send the HTTP response. The call blocks until - one of the following conditions is true: + This function is used to synchronously read an HTTP WebSocket + Upgrade request and send the HTTP response. The call blocks + until one of the following conditions is true: - @li A HTTP request finishes receiving, and a HTTP response finishes - sending. + @li The HTTP request finishes receiving, and the HTTP response + finishes sending. @li An error occurs on the stream. - This function is implemented in terms of one or more calls to the - next layer's `read_some` and `write_some` functions. + This function is implemented in terms of one or more calls to + the next layer's `read_some` and `write_some` functions. - If the stream receives a valid HTTP WebSocket Upgrade request, a - HTTP response is sent back indicating a successful upgrade. When this - call returns, the stream is then ready to send and receive WebSocket - protocol frames and messages. + If the stream receives a valid HTTP WebSocket Upgrade request, + an HTTP response is sent back indicating a successful upgrade. + When this call returns, the stream is then ready to send and + receive WebSocket protocol frames and messages. - If the HTTP Upgrade request is invalid or cannot be satisfied, a - HTTP response is sent indicating the reason and status code + If the HTTP Upgrade request is invalid or cannot be satisfied, + an HTTP response is sent indicating the reason and status code (typically 400, "Bad Request"). This counts as a failure. @param ec Set to indicate what error occurred, if any. @@ -394,36 +395,245 @@ public: void accept(error_code& ec); + /** Read and respond to a WebSocket HTTP Upgrade request. + + This function is used to synchronously read an HTTP WebSocket + Upgrade request and send the HTTP response. The call blocks + until one of the following conditions is true: + + @li The HTTP request finishes receiving, and the HTTP response + finishes sending. + + @li An error occurs on the stream. + + This function is implemented in terms of one or more calls to + the next layer's `read_some` and `write_some` functions. + + If the stream receives a valid HTTP WebSocket Upgrade request, + an HTTP response is sent back indicating a successful upgrade. + When this call returns, the stream is then ready to send and + receive WebSocket protocol frames and messages. + + If the HTTP Upgrade request is invalid or cannot be satisfied, + an HTTP response is sent indicating the reason and status code + (typically 400, "Bad Request"). This counts as a failure. + + @param buffers Caller provided data that has already been + received on the stream. The implementation will copy the + caller provided data before the function returns. + + @throws system_error Thrown on failure. + */ + template + void + accept(ConstBufferSequence const& buffers); + + /** Read and respond to a WebSocket HTTP Upgrade request. + + This function is used to synchronously read an HTTP WebSocket + Upgrade request and send the HTTP response. The call blocks + until one of the following conditions is true: + + @li The HTTP request finishes receiving, and the HTTP response + finishes sending. + + @li An error occurs on the stream. + + This function is implemented in terms of one or more calls to + the next layer's `read_some` and `write_some` functions. + + If the stream receives a valid HTTP WebSocket Upgrade request, + an HTTP response is sent back indicating a successful upgrade. + When this call returns, the stream is then ready to send and + receive WebSocket protocol frames and messages. + + If the HTTP Upgrade request is invalid or cannot be satisfied, + an HTTP response is sent indicating the reason and status code + (typically 400, "Bad Request"). This counts as a failure. + + @param buffers Caller provided data that has already been + received on the stream. The implementation will copy the + caller provided data before the function returns. + + @param ec Set to indicate what error occurred, if any. + */ + template + void + accept(ConstBufferSequence const& buffers, error_code& ec); + + /** Respond to a WebSocket HTTP Upgrade request + + This function is used to synchronously send the HTTP response + to an HTTP request possibly containing a WebSocket Upgrade. + The call blocks until one of the following conditions is true: + + @li The HTTP response finishes sending. + + @li An error occurs on the stream. + + This function is implemented in terms of one or more calls to + the next layer's `read_some` and `write_some` functions. + + If the stream receives a valid HTTP WebSocket Upgrade request, + an HTTP response is sent back indicating a successful upgrade. + When this call returns, the stream is then ready to send and + receive WebSocket protocol frames and messages. + + If the HTTP Upgrade request is invalid or cannot be satisfied, + an HTTP response is sent indicating the reason and status code + (typically 400, "Bad Request"). This counts as a failure. + + @param req An object containing the HTTP Upgrade request. + Ownership is not transferred, the implementation will not + access this object from other threads. + + @throws system_error Thrown on failure. + */ + template + void + accept(http::header const& req); + + /** Respond to a WebSocket HTTP Upgrade request + + This function is used to synchronously send the HTTP response + to an HTTP request possibly containing a WebSocket Upgrade. + The call blocks until one of the following conditions is true: + + @li The HTTP response finishes sending. + + @li An error occurs on the stream. + + This function is implemented in terms of one or more calls to + the next layer's `read_some` and `write_some` functions. + + If the stream receives a valid HTTP WebSocket Upgrade request, + an HTTP response is sent back indicating a successful upgrade. + When this call returns, the stream is then ready to send and + receive WebSocket protocol frames and messages. + + If the HTTP Upgrade request is invalid or cannot be satisfied, + an HTTP response is sent indicating the reason and status code + (typically 400, "Bad Request"). This counts as a failure. + + @param req An object containing the HTTP Upgrade request. + Ownership is not transferred, the implementation will not + access this object from other threads. + + @param ec Set to indicate what error occurred, if any. + */ + template + void + accept(http::header const& req, error_code& ec); + + /** Respond to a WebSocket HTTP Upgrade request + + This function is used to synchronously send the HTTP response + to an HTTP request possibly containing a WebSocket Upgrade. + The call blocks until one of the following conditions is true: + + @li The HTTP response finishes sending. + + @li An error occurs on the stream. + + This function is implemented in terms of one or more calls to + the next layer's `read_some` and `write_some` functions. + + If the stream receives a valid HTTP WebSocket Upgrade request, + an HTTP response is sent back indicating a successful upgrade. + When this call returns, the stream is then ready to send and + receive WebSocket protocol frames and messages. + + If the HTTP Upgrade request is invalid or cannot be satisfied, + an HTTP response is sent indicating the reason and status code + (typically 400, "Bad Request"). This counts as a failure. + + @param req An object containing the HTTP Upgrade request. + Ownership is not transferred, the implementation will not + access this object from other threads. + + @param buffers Caller provided data that has already been + received on the stream. This must not include the octets + corresponding to the HTTP Upgrade request. The implementation + will copy the caller provided data before the function returns. + + @throws system_error Thrown on failure. + */ + template + void + accept(http::header const& req, + ConstBufferSequence const& buffers); + + /** Respond to a WebSocket HTTP Upgrade request + + This function is used to synchronously send the HTTP response + to an HTTP request possibly containing a WebSocket Upgrade. + The call blocks until one of the following conditions is true: + + @li The HTTP response finishes sending. + + @li An error occurs on the stream. + + This function is implemented in terms of one or more calls to + the next layer's `read_some` and `write_some` functions. + + If the stream receives a valid HTTP WebSocket Upgrade request, + an HTTP response is sent back indicating a successful upgrade. + When this call returns, the stream is then ready to send and + receive WebSocket protocol frames and messages. + + If the HTTP Upgrade request is invalid or cannot be satisfied, + an HTTP response is sent indicating the reason and status code + (typically 400, "Bad Request"). This counts as a failure. + + @param req An object containing the HTTP Upgrade request. + Ownership is not transferred, the implementation will not + access this object from other threads. + + @param buffers Caller provided data that has already been + received on the stream. This must not include the octets + corresponding to the HTTP Upgrade request. The implementation + will copy the caller provided data before the function returns. + + @param ec Set to indicate what error occurred, if any. + */ + template + void + accept(http::header const& req, + ConstBufferSequence const& buffers, error_code& ec); + /** Start reading and responding to a WebSocket HTTP Upgrade request. - This function is used to asynchronously read a HTTP WebSocket + This function is used to asynchronously read an HTTP WebSocket Upgrade request and send the HTTP response. The function call always returns immediately. The asynchronous operation will continue until one of the following conditions is true: - @li A HTTP request finishes receiving, and a HTTP response finishes - sending. + @li The HTTP request finishes receiving, and the HTTP response + finishes sending. @li An error occurs on the stream. - This operation is implemented in terms of one or more calls to the - next layer's `async_read_some` and `async_write_some` functions, and - is known as a composed operation. The program must ensure - that the stream performs no other operations until this operation - completes. + This operation is implemented in terms of one or more calls to + the next layer's `async_read_some` and `async_write_some` + functions, and is known as a composed operation. The + program must ensure that the stream performs no other + asynchronous operations until this operation completes. - If the stream receives a valid HTTP WebSocket Upgrade request, a - HTTP response is sent back indicating a successful upgrade. When - this call returns, the stream is then ready to send and receive - WebSocket protocol frames and messages. + If the stream receives a valid HTTP WebSocket Upgrade request, + an HTTP response is sent back indicating a successful upgrade. + When the completion handler is invoked, the stream is then + ready to send and receive WebSocket protocol frames and + messages. - If the HTTP Upgrade request is invalid or cannot be satisfied, a - HTTP response is sent indicating the reason and status code - (typically 400, "Bad Request"). This counts as a failure. + If the HTTP Upgrade request is invalid or cannot be satisfied, + an HTTP response is sent indicating the reason and status code + (typically 400, "Bad Request"). This counts as a failure, and + the completion handler will be invoked with a suitable error + code set. - @param handler The handler to be called when the request completes. - Copies will be made of the handler as required. The equivalent - function signature of the handler must be: + @param handler The handler to be called when the request + completes. Copies will be made of the handler as required. The + equivalent function signature of the handler must be: @code void handler( error_code const& error // result of operation ); @endcode @@ -441,104 +651,35 @@ public: #endif async_accept(AcceptHandler&& handler); - /** Read and respond to a WebSocket HTTP Upgrade request. - - This function is used to synchronously read a HTTP WebSocket - Upgrade request and send the HTTP response. The call blocks until - one of the following conditions is true: - - @li A HTTP request finishes receiving, and a HTTP response finishes - sending. - - @li An error occurs on the stream. - - This function is implemented in terms of one or more calls to the - next layer's `read_some` and `write_some` functions. - - If the stream receives a valid HTTP WebSocket Upgrade request, a - HTTP response is sent back indicating a successful upgrade. When - this call returns, the stream is then ready to send and receive - WebSocket protocol frames and messages. - - If the HTTP Upgrade request is invalid or cannot be satisfied, a - HTTP response is sent indicating the reason and status code - (typically 400, "Bad Request"). This counts as a failure. - - @param buffers Caller provided data that has already been - received on the stream. This may be used for implementations - allowing multiple protocols on the same stream. The - buffered data will first be applied to the handshake, and - then to received WebSocket frames. The implementation will - copy the caller provided data before the function returns. - - @throws system_error Thrown on failure. - */ - template - void - accept(ConstBufferSequence const& buffers); - - /** Read and respond to a WebSocket HTTP Upgrade request. - - This function is used to synchronously read a HTTP WebSocket - Upgrade request and send the HTTP response. The call blocks until - one of the following conditions is true: - - @li A HTTP request finishes receiving, and a HTTP response finishes - sending. - - @li An error occurs on the stream. - - This function is implemented in terms of one or more calls to the - next layer's `read_some` and `write_some` functions. - - If the stream receives a valid HTTP WebSocket Upgrade request, a - HTTP response is sent back indicating a successful upgrade. When - this call returns, the stream is then ready to send and receive - WebSocket protocol frames and messages. - - If the HTTP Upgrade request is invalid or cannot be satisfied, a - HTTP response is sent indicating the reason and status code - (typically 400, "Bad Request"). This counts as a failure. - - @param buffers Caller provided data that has already been - received on the stream. This may be used for implementations - allowing multiple protocols on the same stream. The - buffered data will first be applied to the handshake, and - then to received WebSocket frames. The implementation will - copy the caller provided data before the function returns. - - @param ec Set to indicate what error occurred, if any. - */ - template - void - accept(ConstBufferSequence const& buffers, error_code& ec); - /** Start reading and responding to a WebSocket HTTP Upgrade request. - This function is used to asynchronously read a HTTP WebSocket + This function is used to asynchronously read an HTTP WebSocket Upgrade request and send the HTTP response. The function call always returns immediately. The asynchronous operation will continue until one of the following conditions is true: - @li A HTTP request finishes receiving, and a HTTP response finishes - sending. + @li The HTTP request finishes receiving, and the HTTP response + finishes sending. @li An error occurs on the stream. - This operation is implemented in terms of one or more calls to the - next layer's `async_read_some` and `async_write_some` functions, and - is known as a composed operation. The program must ensure - that the stream performs no other operations until this operation - completes. + This operation is implemented in terms of one or more calls to + the next layer's `async_read_some` and `async_write_some` + functions, and is known as a composed operation. The + program must ensure that the stream performs no other + asynchronous operations until this operation completes. - If the stream receives a valid HTTP WebSocket Upgrade request, a - HTTP response is sent back indicating a successful upgrade. When - this call returns, the stream is then ready to send and receive - WebSocket protocol frames and messages. + If the stream receives a valid HTTP WebSocket Upgrade request, + an HTTP response is sent back indicating a successful upgrade. + When the completion handler is invoked, the stream is then + ready to send and receive WebSocket protocol frames and + messages. - If the HTTP Upgrade request is invalid or cannot be satisfied, a - HTTP response is sent indicating the reason and status code - (typically 400, "Bad Request"). This counts as a failure. + If the HTTP Upgrade request is invalid or cannot be satisfied, + an HTTP response is sent indicating the reason and status code + (typically 400, "Bad Request"). This counts as a failure, and + the completion handler will be invoked with a suitable error + code set. @param buffers Caller provided data that has already been received on the stream. This may be used for implementations @@ -547,9 +688,9 @@ public: then to received WebSocket frames. The implementation will copy the caller provided data before the function returns. - @param handler The handler to be called when the request completes. - Copies will be made of the handler as required. The equivalent - function signature of the handler must be: + @param handler The handler to be called when the request + completes. Copies will be made of the handler as required. The + equivalent function signature of the handler must be: @code void handler( error_code const& error // result of operation ); @endcode @@ -562,110 +703,49 @@ public: #if BEAST_DOXYGEN void_or_deduced #else - typename async_completion< - AcceptHandler, void(error_code)>::result_type + typename async_completion::result_type #endif async_accept(ConstBufferSequence const& buffers, AcceptHandler&& handler); - /** Respond to a WebSocket HTTP Upgrade request - - This function is used to synchronously send the HTTP response to - a HTTP request possibly containing a WebSocket Upgrade request. - The call blocks until one of the following conditions is true: - - @li A HTTP response finishes sending. - - @li An error occurs on the stream. - - This function is implemented in terms of one or more calls to the - next layer's `write_some` functions. - - If the passed HTTP request is a valid HTTP WebSocket Upgrade - request, a HTTP response is sent back indicating a successful - upgrade. When this call returns, the stream is then ready to send - and receive WebSocket protocol frames and messages. - - If the HTTP request is invalid or cannot be satisfied, a HTTP - response is sent indicating the reason and status code (typically - 400, "Bad Request"). This counts as a failure. - - @param request An object containing the HTTP Upgrade request. - Ownership is not transferred, the implementation will not access - this object from other threads. - - @throws system_error Thrown on failure. - */ - // VFALCO TODO This should also take a DynamicBuffer with any leftover bytes. - template - void - accept(http::header const& request); - - /** Respond to a WebSocket HTTP Upgrade request - - This function is used to synchronously send the HTTP response to - a HTTP request possibly containing a WebSocket Upgrade request. - The call blocks until one of the following conditions is true: - - @li A HTTP response finishes sending. - - @li An error occurs on the stream. - - This function is implemented in terms of one or more calls to the - next layer's `write_some` functions. - - If the passed HTTP request is a valid HTTP WebSocket Upgrade - request, a HTTP response is sent back indicating a successful - upgrade. When this call returns, the stream is then ready to send - and receive WebSocket protocol frames and messages. - - If the HTTP request is invalid or cannot be satisfied, a HTTP - response is sent indicating the reason and status code (typically - 400, "Bad Request"). This counts as a failure. - - @param request An object containing the HTTP Upgrade request. - Ownership is not transferred, the implementation will not access - this object from other threads. - - @param ec Set to indicate what error occurred, if any. - */ - template - void - accept(http::header const& request, error_code& ec); - /** Start responding to a WebSocket HTTP Upgrade request. This function is used to asynchronously send the HTTP response - to a HTTP request possibly containing a WebSocket Upgrade request. - The function call always returns immediately. The asynchronous - operation will continue until one of the following conditions is - true: + to an HTTP request possibly containing a WebSocket Upgrade + request. The function call always returns immediately. The + asynchronous operation will continue until one of the following + conditions is true: - @li A HTTP response finishes sending. + @li The HTTP response finishes sending. @li An error occurs on the stream. - This operation is implemented in terms of one or more calls to the - next layer's `async_write_some` functions, and is known as a - composed operation. The program must ensure that the - stream performs no other operations until this operation completes. + This operation is implemented in terms of one or more calls to + the next layer's `async_write_some` functions, and is known as + a composed operation. The program must ensure that the + stream performs no other operations until this operation + completes. - If the passed HTTP request is a valid HTTP WebSocket Upgrade - request, a HTTP response is sent back indicating a successful - upgrade. When this asynchronous operation completes, the stream is - then ready to send and receive WebSocket protocol frames and messages. + If the stream receives a valid HTTP WebSocket Upgrade request, + an HTTP response is sent back indicating a successful upgrade. + When the completion handler is invoked, the stream is then + ready to send and receive WebSocket protocol frames and + messages. - If the HTTP request is invalid or cannot be satisfied, a HTTP - response is sent indicating the reason and status code (typically - 400, "Bad Request"). This counts as a failure. + If the HTTP Upgrade request is invalid or cannot be satisfied, + an HTTP response is sent indicating the reason and status code + (typically 400, "Bad Request"). This counts as a failure, and + the completion handler will be invoked with a suitable error + code set. - @param request An object containing the HTTP Upgrade request. + @param req An object containing the HTTP Upgrade request. Ownership is not transferred, the implementation will not access this object from other threads. - @param handler The handler to be called when the request completes. - Copies will be made of the handler as required. The equivalent - function signature of the handler must be: + @param handler The handler to be called when the request + completes. Copies will be made of the handler as required. The + equivalent function signature of the handler must be: @code void handler( error_code const& error // result of operation ); @endcode @@ -678,19 +758,83 @@ public: #if BEAST_DOXYGEN void_or_deduced #else - typename async_completion< - AcceptHandler, void(error_code)>::result_type + typename async_completion::result_type #endif - async_accept(http::header const& request, AcceptHandler&& handler); + async_accept(http::header const& req, + AcceptHandler&& handler); - /** Send a HTTP WebSocket Upgrade request and receive the response. + /** Start responding to a WebSocket HTTP Upgrade request. + + This function is used to asynchronously send the HTTP response + to an HTTP request possibly containing a WebSocket Upgrade + request. The function call always returns immediately. The + asynchronous operation will continue until one of the following + conditions is true: + + @li The HTTP response finishes sending. + + @li An error occurs on the stream. + + This operation is implemented in terms of one or more calls to + the next layer's `async_write_some` functions, and is known as + a composed operation. The program must ensure that the + stream performs no other operations until this operation + completes. + + If the stream receives a valid HTTP WebSocket Upgrade request, + an HTTP response is sent back indicating a successful upgrade. + When the completion handler is invoked, the stream is then + ready to send and receive WebSocket protocol frames and + messages. + + If the HTTP Upgrade request is invalid or cannot be satisfied, + an HTTP response is sent indicating the reason and status code + (typically 400, "Bad Request"). This counts as a failure, and + the completion handler will be invoked with a suitable error + code set. + + @param req An object containing the HTTP Upgrade request. + Ownership is not transferred, the implementation will not access + this object from other threads. + + @param buffers Caller provided data that has already been + received on the stream. This may be used for implementations + allowing multiple protocols on the same stream. The + buffered data will first be applied to the handshake, and + then to received WebSocket frames. The implementation will + copy the caller provided data before the function returns. + + @param handler The handler to be called when the request + completes. Copies will be made of the handler as required. The + equivalent function signature of the handler must be: + @code void handler( + error_code const& error // result of operation + ); @endcode + Regardless of whether the asynchronous operation completes + immediately or not, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `boost::asio::io_service::post`. + */ + template +#if BEAST_DOXYGEN + void_or_deduced +#else + typename async_completion::result_type +#endif + async_accept(http::header const& req, + ConstBufferSequence const& buffers, + AcceptHandler&& handler); + + /** Send an HTTP WebSocket Upgrade request and receive the response. This function is used to synchronously send the WebSocket upgrade HTTP request. The call blocks until one of the following conditions is true: - @li A HTTP request finishes sending and a HTTP response finishes + @li A HTTP request finishes sending and an HTTP response finishes receiving. @li An error occurs on the stream @@ -728,13 +872,13 @@ public: handshake(boost::string_ref const& host, boost::string_ref const& resource); - /** Send a HTTP WebSocket Upgrade request and receive the response. + /** Send an HTTP WebSocket Upgrade request and receive the response. This function is used to synchronously send the WebSocket upgrade HTTP request. The call blocks until one of the following conditions is true: - @li A HTTP request finishes sending and a HTTP response finishes + @li A HTTP request finishes sending and an HTTP response finishes receiving. @li An error occurs on the stream @@ -778,7 +922,7 @@ public: operation will continue until one of the following conditions is true: - @li A HTTP request finishes sending and a HTTP response finishes + @li A HTTP request finishes sending and an HTTP response finishes receiving. @li An error occurs on the stream. @@ -1673,6 +1817,14 @@ private: void reset(); + void + do_accept(error_code& ec); + + template + void + do_accept(http::header const& req, + error_code& ec); + http::request_header build_request(boost::string_ref const& host, boost::string_ref const& resource, diff --git a/test/websocket/stream.cpp b/test/websocket/stream.cpp index 867366d4..b08a6697 100644 --- a/test/websocket/stream.cpp +++ b/test/websocket/stream.cpp @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include #include @@ -136,6 +138,274 @@ public: } }; + struct SyncClient + { + template + void + accept(stream& ws) const + { + ws.accept(); + } + + template + void + accept(stream& ws, + Buffers const& buffers) const + { + ws.accept(buffers); + } + + template + void + accept(stream& ws, + http::header const& req) const + { + ws.accept(req); + } + + template + void + accept(stream& ws, + http::header const& req, + Buffers const& buffers) const + { + ws.accept(req, buffers); + } + + template + void + handshake(stream& ws, + boost::string_ref const& uri, + boost::string_ref const& path) const + { + ws.handshake(uri, path); + } + + template + void + ping(stream& ws, + ping_data const& payload) const + { + ws.ping(payload); + } + + template + void + pong(stream& ws, + ping_data const& payload) const + { + ws.pong(payload); + } + + template + void + close(stream& ws, + close_reason const& cr) const + { + ws.close(cr); + } + + template< + class NextLayer, class DynamicBuffer> + void + read(stream& ws, + opcode& op, DynamicBuffer& dynabuf) const + { + ws.read(op, dynabuf); + } + + template< + class NextLayer, class ConstBufferSequence> + void + write(stream& ws, + ConstBufferSequence const& buffers) const + { + ws.write(buffers); + } + + template< + class NextLayer, class ConstBufferSequence> + void + write_frame(stream& ws, bool fin, + ConstBufferSequence const& buffers) const + { + ws.write_frame(fin, buffers); + } + + template< + class NextLayer, class ConstBufferSequence> + void + write_raw(stream& ws, + ConstBufferSequence const& buffers) const + { + boost::asio::write( + ws.next_layer(), buffers); + } + }; + + class AsyncClient + { + yield_context& yield_; + + public: + explicit + AsyncClient(yield_context& yield) + : yield_(yield) + { + } + + template + void + accept(stream& ws) const + { + error_code ec; + ws.async_accept(yield_[ec]); + if(ec) + throw system_error{ec}; + } + + template + void + accept(stream& ws, + Buffers const& buffers) const + { + error_code ec; + ws.async_accept(buffers, yield_[ec]); + if(ec) + throw system_error{ec}; + } + + template + void + accept(stream& ws, + http::header const& req) const + { + error_code ec; + ws.async_accept(req, yield_[ec]); + if(ec) + throw system_error{ec}; + } + + template + void + accept(stream& ws, + http::header const& req, + Buffers const& buffers) const + { + error_code ec; + ws.async_accept(req, buffers, yield_[ec]); + if(ec) + throw system_error{ec}; + } + + template + void + accept(stream& ws, + http::header const& req, + Buffers const& buffers, + error_code& ec) const + { + ws.async_accept(req, buffers, yield_[ec]); + } + + template + void + handshake(stream& ws, + boost::string_ref const& uri, + boost::string_ref const& path) const + { + error_code ec; + ws.async_handshake(uri, path, yield_[ec]); + if(ec) + throw system_error{ec}; + } + + template + void + ping(stream& ws, + ping_data const& payload) const + { + error_code ec; + ws.async_ping(payload, yield_[ec]); + if(ec) + throw system_error{ec}; + } + + template + void + pong(stream& ws, + ping_data const& payload) const + { + error_code ec; + ws.async_pong(payload, yield_[ec]); + if(ec) + throw system_error{ec}; + } + + template + void + close(stream& ws, + close_reason const& cr) const + { + error_code ec; + ws.async_close(cr, yield_[ec]); + if(ec) + throw system_error{ec}; + } + + template< + class NextLayer, class DynamicBuffer> + void + read(stream& ws, + opcode& op, DynamicBuffer& dynabuf) const + { + error_code ec; + ws.async_read(op, dynabuf, yield_[ec]); + if(ec) + throw system_error{ec}; + } + + template< + class NextLayer, class ConstBufferSequence> + void + write(stream& ws, + ConstBufferSequence const& buffers) const + { + error_code ec; + ws.async_write(buffers, yield_[ec]); + if(ec) + throw system_error{ec}; + } + + template< + class NextLayer, class ConstBufferSequence> + void + write_frame(stream& ws, bool fin, + ConstBufferSequence const& buffers) const + { + error_code ec; + ws.async_write_frame(fin, buffers, yield_[ec]); + if(ec) + throw system_error{ec}; + } + + template< + class NextLayer, class ConstBufferSequence> + void + write_raw(stream& ws, + ConstBufferSequence const& buffers) const + { + error_code ec; + boost::asio::async_write( + ws.next_layer(), buffers, yield_[ec]); + if(ec) + throw system_error{ec}; + } + }; + void testOptions() { @@ -166,73 +436,203 @@ public: } } - void testAccept() + template + void + testAccept(Client const& c) { + static std::size_t constexpr limit = 200; + std::size_t n; + for(n = 0; n < limit; ++n) { - static std::size_t constexpr limit = 100; - std::size_t n; - for(n = 0; n < limit; ++n) - { - // valid - http::request_header req; - req.method = "GET"; - req.url = "/"; - req.version = 11; - req.fields.insert("Host", "localhost"); - req.fields.insert("Upgrade", "websocket"); - req.fields.insert("Connection", "upgrade"); - req.fields.insert("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ=="); - req.fields.insert("Sec-WebSocket-Version", "13"); - stream> ws(n, ios_, ""); - try - { - ws.accept(req); - break; - } - catch(system_error const&) - { - } - } - BEAST_EXPECT(n < limit); - } - { - // valid - stream ws(ios_, - "GET / HTTP/1.1\r\n" - "Host: localhost:80\r\n" - "Upgrade: WebSocket\r\n" - "Connection: upgrade\r\n" - "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" - "Sec-WebSocket-Version: 13\r\n" - "\r\n" - ); + test::fail_counter fc{n}; try { - ws.accept(); - pass(); + // request in stream + { + stream> ws{fc, ios_, + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "Upgrade: websocket\r\n" + "Connection: upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n" + , 20}; + c.accept(ws); + //log << ws.next_layer().str << std::endl; + } + // request in buffers + { + stream> ws{fc, ios_}; + c.accept(ws, sbuf( + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "Upgrade: websocket\r\n" + "Connection: upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n" + )); + } + // request in buffers and stream + { + stream> ws{fc, ios_, + "Connection: upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n" + , 16}; + c.accept(ws, sbuf( + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "Upgrade: websocket\r\n" + )); + } + // request in message + { + http::request_header req; + req.method = "GET"; + req.url = "/"; + req.version = 11; + req.fields.insert("Host", "localhost"); + req.fields.insert("Upgrade", "websocket"); + req.fields.insert("Connection", "upgrade"); + req.fields.insert("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ=="); + req.fields.insert("Sec-WebSocket-Version", "13"); + stream> ws{fc, ios_}; + c.accept(ws, req); + } + // request in message, close frame in buffers + { + http::request_header req; + req.method = "GET"; + req.url = "/"; + req.version = 11; + req.fields.insert("Host", "localhost"); + req.fields.insert("Upgrade", "websocket"); + req.fields.insert("Connection", "upgrade"); + req.fields.insert("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ=="); + req.fields.insert("Sec-WebSocket-Version", "13"); + stream> ws{fc, ios_}; + c.accept(ws, req, + cbuf(0x88, 0x82, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x17)); + try + { + opcode op; + streambuf sb; + c.read(ws, op, sb); + fail("success", __FILE__, __LINE__); + } + catch(system_error const& e) + { + if(e.code() != websocket::error::closed) + throw; + } + } + // request in message, close frame in stream + { + http::request_header req; + req.method = "GET"; + req.url = "/"; + req.version = 11; + req.fields.insert("Host", "localhost"); + req.fields.insert("Upgrade", "websocket"); + req.fields.insert("Connection", "upgrade"); + req.fields.insert("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ=="); + req.fields.insert("Sec-WebSocket-Version", "13"); + stream> ws{fc, ios_, + "\x88\x82\xff\xff\xff\xff\xfc\x17"}; + c.accept(ws, req); + try + { + opcode op; + streambuf sb; + c.read(ws, op, sb); + fail("success", __FILE__, __LINE__); + } + catch(system_error const& e) + { + if(e.code() != websocket::error::closed) + throw; + } + } + // request in message, close frame in stream and buffers + { + http::request_header req; + req.method = "GET"; + req.url = "/"; + req.version = 11; + req.fields.insert("Host", "localhost"); + req.fields.insert("Upgrade", "websocket"); + req.fields.insert("Connection", "upgrade"); + req.fields.insert("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ=="); + req.fields.insert("Sec-WebSocket-Version", "13"); + stream> ws{fc, ios_, + "xff\xff\xfc\x17"}; + c.accept(ws, req, + cbuf(0x88, 0x82, 0xff, 0xff)); + try + { + opcode op; + streambuf sb; + c.read(ws, op, sb); + fail("success", __FILE__, __LINE__); + } + catch(system_error const& e) + { + if(e.code() != websocket::error::closed) + throw; + } + } + // failed handshake (missing Sec-WebSocket-Key) + { + stream> ws{fc, ios_, + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "Upgrade: websocket\r\n" + "Connection: upgrade\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n" + , 20}; + try + { + c.accept(ws); + fail("success", __FILE__, __LINE__); + } + catch(system_error const& e) + { + if(e.code() != + websocket::error::handshake_failed) + throw; + } + } } catch(system_error const&) { - fail(); + continue; } + break; } - { - // invalid - stream ws(ios_, - "GET / HTTP/1.0\r\n" - "\r\n" - ); - try + BEAST_EXPECT(n < limit); + } + + void + testAccept() + { + testAccept(SyncClient{}); + yield_to( + [&](yield_context yield) { - ws.accept(); - fail(); - } - catch(system_error const&) - { - pass(); - } - } + testAccept(AsyncClient{yield}); + }); } void testBadHandshakes() @@ -863,185 +1263,6 @@ public: } } - struct SyncClient - { - template - void - handshake(stream& ws, - boost::string_ref const& uri, - boost::string_ref const& path) const - { - ws.handshake(uri, path); - } - - template - void - ping(stream& ws, - ping_data const& payload) const - { - ws.ping(payload); - } - - template - void - pong(stream& ws, - ping_data const& payload) const - { - ws.pong(payload); - } - - template - void - close(stream& ws, - close_reason const& cr) const - { - ws.close(cr); - } - - template< - class NextLayer, class DynamicBuffer> - void - read(stream& ws, - opcode& op, DynamicBuffer& dynabuf) const - { - ws.read(op, dynabuf); - } - - template< - class NextLayer, class ConstBufferSequence> - void - write(stream& ws, - ConstBufferSequence const& buffers) const - { - ws.write(buffers); - } - - template< - class NextLayer, class ConstBufferSequence> - void - write_frame(stream& ws, bool fin, - ConstBufferSequence const& buffers) const - { - ws.write_frame(fin, buffers); - } - - template< - class NextLayer, class ConstBufferSequence> - void - write_raw(stream& ws, - ConstBufferSequence const& buffers) const - { - boost::asio::write( - ws.next_layer(), buffers); - } - }; - - class AsyncClient - { - yield_context& yield_; - - public: - explicit - AsyncClient(yield_context& yield) - : yield_(yield) - { - } - - template - void - handshake(stream& ws, - boost::string_ref const& uri, - boost::string_ref const& path) const - { - error_code ec; - ws.async_handshake(uri, path, yield_[ec]); - if(ec) - throw system_error{ec}; - } - - template - void - ping(stream& ws, - ping_data const& payload) const - { - error_code ec; - ws.async_ping(payload, yield_[ec]); - if(ec) - throw system_error{ec}; - } - - template - void - pong(stream& ws, - ping_data const& payload) const - { - error_code ec; - ws.async_pong(payload, yield_[ec]); - if(ec) - throw system_error{ec}; - } - - template - void - close(stream& ws, - close_reason const& cr) const - { - error_code ec; - ws.async_close(cr, yield_[ec]); - if(ec) - throw system_error{ec}; - } - - template< - class NextLayer, class DynamicBuffer> - void - read(stream& ws, - opcode& op, DynamicBuffer& dynabuf) const - { - error_code ec; - ws.async_read(op, dynabuf, yield_[ec]); - if(ec) - throw system_error{ec}; - } - - template< - class NextLayer, class ConstBufferSequence> - void - write(stream& ws, - ConstBufferSequence const& buffers) const - { - error_code ec; - ws.async_write(buffers, yield_[ec]); - if(ec) - throw system_error{ec}; - } - - template< - class NextLayer, class ConstBufferSequence> - void - write_frame(stream& ws, bool fin, - ConstBufferSequence const& buffers) const - { - error_code ec; - ws.async_write_frame(fin, buffers, yield_[ec]); - if(ec) - throw system_error{ec}; - } - - template< - class NextLayer, class ConstBufferSequence> - void - write_raw(stream& ws, - ConstBufferSequence const& buffers) const - { - error_code ec; - boost::asio::async_write( - ws.next_layer(), buffers, yield_[ec]); - if(ec) - throw system_error{ec}; - } - }; - struct abort_test { };