diff --git a/include/beast/websocket/impl/accept.ipp b/include/beast/websocket/impl/accept.ipp new file mode 100644 index 00000000..99b7d2f8 --- /dev/null +++ b/include/beast/websocket/impl/accept.ipp @@ -0,0 +1,435 @@ +// +// Copyright (c) 2013-2016 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_WEBSOCKET_IMPL_ACCEPT_IPP +#define BEAST_WEBSOCKET_IMPL_ACCEPT_IPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace beast { +namespace websocket { + +//------------------------------------------------------------------------------ + +// Respond to an upgrade HTTP request +template +template +class stream::response_op +{ + using alloc_type = + handler_alloc; + + struct data + { + stream& ws; + http::response resp; + Handler h; + error_code final_ec; + bool cont; + int state = 0; + + template + data(DeducedHandler&& h_, stream& ws_, + http::request const& req, + bool cont_) + : ws(ws_) + , resp(ws_.build_response(req)) + , h(std::forward(h_)) + , cont(cont_) + { + // can't call stream::reset() here + // otherwise accept_op will malfunction + // + if(resp.status != 101) + final_ec = error::handshake_failed; + } + }; + + std::shared_ptr d_; + +public: + response_op(response_op&&) = default; + response_op(response_op const&) = default; + + template + response_op(DeducedHandler&& h, + stream& ws, Args&&... args) + : d_(std::allocate_shared(alloc_type{h}, + std::forward(h), ws, + std::forward(args)...)) + { + (*this)(error_code{}, false); + } + + void operator()( + error_code ec, bool again = true); + + friend + void* asio_handler_allocate( + std::size_t size, response_op* op) + { + return boost_asio_handler_alloc_helpers:: + allocate(size, op->d_->h); + } + + friend + void asio_handler_deallocate( + void* p, std::size_t size, response_op* op) + { + return boost_asio_handler_alloc_helpers:: + deallocate(p, size, op->d_->h); + } + + friend + bool asio_handler_is_continuation(response_op* op) + { + return op->d_->cont; + } + + template + friend + void asio_handler_invoke(Function&& f, response_op* op) + { + return boost_asio_handler_invoke_helpers:: + invoke(f, op->d_->h); + } +}; + +template +template +void +stream::response_op:: +operator()(error_code ec, bool again) +{ + auto& d = *d_; + d.cont = d.cont || again; + while(! ec && d.state != 99) + { + switch(d.state) + { + case 0: + // send response + d.state = 1; + http::async_write(d.ws.next_layer(), + d.resp, std::move(*this)); + return; + + // sent response + case 1: + d.state = 99; + ec = d.final_ec; + if(! ec) + d.ws.open(detail::role_type::server); + break; + } + } + d.h(ec); +} + +//------------------------------------------------------------------------------ + +// read and respond to an upgrade request +// +template +template +class stream::accept_op +{ + using alloc_type = + handler_alloc; + + struct data + { + stream& ws; + http::request req; + Handler h; + bool cont; + int state = 0; + + template + data(DeducedHandler&& h_, stream& ws_, + Buffers const& buffers) + : ws(ws_) + , h(std::forward(h_)) + , cont(boost_asio_handler_cont_helpers:: + is_continuation(h)) + { + using boost::asio::buffer_copy; + using boost::asio::buffer_size; + ws.reset(); + ws.stream_.buffer().commit(buffer_copy( + ws.stream_.buffer().prepare( + buffer_size(buffers)), buffers)); + } + }; + + std::shared_ptr d_; + +public: + accept_op(accept_op&&) = default; + accept_op(accept_op const&) = default; + + template + accept_op(DeducedHandler&& h, + stream& ws, Args&&... args) + : d_(std::allocate_shared(alloc_type{h}, + std::forward(h), ws, + std::forward(args)...)) + { + (*this)(error_code{}, 0, false); + } + + void operator()(error_code const& ec) + { + (*this)(ec, 0); + } + + void operator()(error_code const& ec, + std::size_t bytes_transferred, bool again = true); + + friend + void* asio_handler_allocate( + std::size_t size, accept_op* op) + { + return boost_asio_handler_alloc_helpers:: + allocate(size, op->d_->h); + } + + friend + void asio_handler_deallocate( + void* p, std::size_t size, accept_op* op) + { + return boost_asio_handler_alloc_helpers:: + deallocate(p, size, op->d_->h); + } + + friend + bool asio_handler_is_continuation(accept_op* op) + { + return op->d_->cont; + } + + template + friend + void asio_handler_invoke(Function&& f, accept_op* op) + { + return boost_asio_handler_invoke_helpers:: + invoke(f, op->d_->h); + } +}; + +template +template +void +stream::accept_op:: +operator()(error_code const& ec, + std::size_t bytes_transferred, bool again) +{ + beast::detail::ignore_unused(bytes_transferred); + auto& d = *d_; + d.cont = d.cont || again; + while(! ec && d.state != 99) + { + switch(d.state) + { + case 0: + // read message + d.state = 1; + http::async_read(d.ws.next_layer(), + d.ws.stream_.buffer(), d.req, + std::move(*this)); + return; + + // got message + case 1: + // respond to request +#if 1 + // VFALCO I have no idea why passing std::move(*this) crashes + d.state = 99; + d.ws.async_accept(d.req, *this); +#else + response_op{ + std::move(d.h), d.ws, d.req, true}; +#endif + return; + } + } + d.h(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::request 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, + boost_asio_handler_cont_helpers:: + is_continuation(completion.handler)}; + return completion.result.get(); +} + +template +void +stream:: +accept() +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + accept(boost::asio::null_buffers{}, ec); + if(ec) + throw system_error{ec}; +} + +template +void +stream:: +accept(error_code& ec) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + accept(boost::asio::null_buffers{}, ec); +} + +template +template +void +stream:: +accept(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(buffers, ec); + if(ec) + throw system_error{ec}; +} + +template +template +void +stream:: +accept(ConstBufferSequence const& buffers, error_code& ec) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + static_assert(beast::is_ConstBufferSequence< + ConstBufferSequence>::value, + "ConstBufferSequence requirements not met"); + using boost::asio::buffer_copy; + using boost::asio::buffer_size; + reset(); + stream_.buffer().commit(buffer_copy( + stream_.buffer().prepare( + buffer_size(buffers)), buffers)); + http::request m; + http::read(next_layer(), stream_.buffer(), m, ec); + if(ec) + return; + accept(m, ec); +} + +template +template +void +stream:: +accept(http::request const& request) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + accept(request, ec); + if(ec) + throw system_error{ec}; +} + +template +template +void +stream:: +accept(http::request const& req, + error_code& ec) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + reset(); + 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; + } + open(detail::role_type::server); +} + +//------------------------------------------------------------------------------ + +} // websocket +} // beast + +#endif diff --git a/include/beast/websocket/impl/accept_op.ipp b/include/beast/websocket/impl/accept_op.ipp deleted file mode 100644 index 82524fff..00000000 --- a/include/beast/websocket/impl/accept_op.ipp +++ /dev/null @@ -1,156 +0,0 @@ -// -// Copyright (c) 2013-2016 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_WEBSOCKET_IMPL_ACCEPT_OP_HPP -#define BEAST_WEBSOCKET_IMPL_ACCEPT_OP_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace beast { -namespace websocket { - -// read and respond to an upgrade request -// -template -template -class stream::accept_op -{ - using alloc_type = - handler_alloc; - - struct data - { - stream& ws; - http::request req; - Handler h; - bool cont; - int state = 0; - - template - data(DeducedHandler&& h_, stream& ws_, - Buffers const& buffers) - : ws(ws_) - , h(std::forward(h_)) - , cont(boost_asio_handler_cont_helpers:: - is_continuation(h)) - { - using boost::asio::buffer_copy; - using boost::asio::buffer_size; - ws.reset(); - ws.stream_.buffer().commit(buffer_copy( - ws.stream_.buffer().prepare( - buffer_size(buffers)), buffers)); - } - }; - - std::shared_ptr d_; - -public: - accept_op(accept_op&&) = default; - accept_op(accept_op const&) = default; - - template - accept_op(DeducedHandler&& h, - stream& ws, Args&&... args) - : d_(std::allocate_shared(alloc_type{h}, - std::forward(h), ws, - std::forward(args)...)) - { - (*this)(error_code{}, 0, false); - } - - void operator()(error_code const& ec) - { - (*this)(ec, 0); - } - - void operator()(error_code const& ec, - std::size_t bytes_transferred, bool again = true); - - friend - void* asio_handler_allocate( - std::size_t size, accept_op* op) - { - return boost_asio_handler_alloc_helpers:: - allocate(size, op->d_->h); - } - - friend - void asio_handler_deallocate( - void* p, std::size_t size, accept_op* op) - { - return boost_asio_handler_alloc_helpers:: - deallocate(p, size, op->d_->h); - } - - friend - bool asio_handler_is_continuation(accept_op* op) - { - return op->d_->cont; - } - - template - friend - void asio_handler_invoke(Function&& f, accept_op* op) - { - return boost_asio_handler_invoke_helpers:: - invoke(f, op->d_->h); - } -}; - -template -template -void -stream::accept_op:: -operator()(error_code const& ec, - std::size_t bytes_transferred, bool again) -{ - beast::detail::ignore_unused(bytes_transferred); - auto& d = *d_; - d.cont = d.cont || again; - while(! ec && d.state != 99) - { - switch(d.state) - { - case 0: - // read message - d.state = 1; - http::async_read(d.ws.next_layer(), - d.ws.stream_.buffer(), d.req, - std::move(*this)); - return; - - // got message - case 1: - // respond to request -#if 1 - // VFALCO I have no idea why passing std::move(*this) crashes - d.state = 99; - d.ws.async_accept(d.req, *this); -#else - response_op{ - std::move(d.h), d.ws, d.req, true}; -#endif - return; - } - } - d.h(ec); -} - -} // websocket -} // beast - -#endif diff --git a/include/beast/websocket/impl/close_op.ipp b/include/beast/websocket/impl/close.ipp similarity index 75% rename from include/beast/websocket/impl/close_op.ipp rename to include/beast/websocket/impl/close.ipp index bed4e81c..d426907a 100644 --- a/include/beast/websocket/impl/close_op.ipp +++ b/include/beast/websocket/impl/close.ipp @@ -5,16 +5,19 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_WEBSOCKET_IMPL_CLOSE_OP_HPP -#define BEAST_WEBSOCKET_IMPL_CLOSE_OP_HPP +#ifndef BEAST_WEBSOCKET_IMPL_CLOSE_IPP +#define BEAST_WEBSOCKET_IMPL_CLOSE_IPP #include #include +#include #include namespace beast { namespace websocket { +//------------------------------------------------------------------------------ + // send the close message and wait for the response // template @@ -188,6 +191,53 @@ upcall: d.h(ec); } +template +template +typename async_completion< + CloseHandler, void(error_code)>::result_type +stream:: +async_close(close_reason const& cr, CloseHandler&& handler) +{ + static_assert(is_AsyncStream::value, + "AsyncStream requirements not met"); + beast::async_completion< + CloseHandler, void(error_code) + > completion(handler); + close_op{ + completion.handler, *this, cr}; + return completion.result.get(); +} + +template +void +stream:: +close(close_reason const& cr) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + close(cr, ec); + if(ec) + throw system_error{ec}; +} + +template +void +stream:: +close(close_reason const& cr, error_code& ec) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + BOOST_ASSERT(! wr_close_); + wr_close_ = true; + detail::frame_streambuf fb; + write_close(fb, cr); + boost::asio::write(stream_, fb.data(), ec); + failed_ = ec != 0; +} + +//------------------------------------------------------------------------------ + } // websocket } // beast diff --git a/include/beast/websocket/impl/handshake_op.ipp b/include/beast/websocket/impl/handshake.ipp similarity index 67% rename from include/beast/websocket/impl/handshake_op.ipp rename to include/beast/websocket/impl/handshake.ipp index 2526c5df..ba96de20 100644 --- a/include/beast/websocket/impl/handshake_op.ipp +++ b/include/beast/websocket/impl/handshake.ipp @@ -5,20 +5,23 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_WEBSOCKET_IMPL_HANDSHAKE_OP_HPP -#define BEAST_WEBSOCKET_IMPL_HANDSHAKE_OP_HPP +#ifndef BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP +#define BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP #include #include #include #include #include +#include #include #include namespace beast { namespace websocket { +//------------------------------------------------------------------------------ + // send the upgrade request and process the response // template @@ -147,6 +150,61 @@ operator()(error_code ec, bool again) d.h(ec); } +template +template +typename async_completion< + HandshakeHandler, void(error_code)>::result_type +stream:: +async_handshake(boost::string_ref const& host, + boost::string_ref const& resource, HandshakeHandler&& handler) +{ + static_assert(is_AsyncStream::value, + "AsyncStream requirements not met"); + beast::async_completion< + HandshakeHandler, void(error_code) + > completion(handler); + handshake_op{ + completion.handler, *this, host, resource}; + return completion.result.get(); +} + +template +void +stream:: +handshake(boost::string_ref const& host, + boost::string_ref const& resource) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + handshake(host, resource, ec); + if(ec) + throw system_error{ec}; +} + +template +void +stream:: +handshake(boost::string_ref const& host, + boost::string_ref const& resource, error_code& ec) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + reset(); + std::string key; + http::write(stream_, + build_request(host, resource, key), ec); + if(ec) + return; + http::response res; + http::read(next_layer(), stream_.buffer(), res, ec); + if(ec) + return; + do_response(res, key, ec); +} + +//------------------------------------------------------------------------------ + } // websocket } // beast diff --git a/include/beast/websocket/impl/ping_op.ipp b/include/beast/websocket/impl/ping.ipp similarity index 78% rename from include/beast/websocket/impl/ping_op.ipp rename to include/beast/websocket/impl/ping.ipp index 4034754a..8fc6b0f8 100644 --- a/include/beast/websocket/impl/ping_op.ipp +++ b/include/beast/websocket/impl/ping.ipp @@ -5,17 +5,20 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_WEBSOCKET_IMPL_PING_OP_HPP -#define BEAST_WEBSOCKET_IMPL_PING_OP_HPP +#ifndef BEAST_WEBSOCKET_IMPL_PING_IPP +#define BEAST_WEBSOCKET_IMPL_PING_IPP #include #include +#include #include #include namespace beast { namespace websocket { +//------------------------------------------------------------------------------ + // write a ping frame // template @@ -187,6 +190,47 @@ upcall: d.h(ec); } +template +template +typename async_completion< + PingHandler, void(error_code)>::result_type +stream:: +async_ping(ping_data const& payload, PingHandler&& handler) +{ + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); + beast::async_completion< + PingHandler, void(error_code) + > completion(handler); + ping_op{ + completion.handler, *this, payload}; + return completion.result.get(); +} + +template +void +stream:: +ping(ping_data const& payload) +{ + error_code ec; + ping(payload, ec); + if(ec) + throw system_error{ec}; +} + +template +void +stream:: +ping(ping_data const& payload, error_code& ec) +{ + detail::frame_streambuf db; + write_ping( + db, opcode::ping, payload); + boost::asio::write(stream_, db.data(), ec); +} + +//------------------------------------------------------------------------------ + } // websocket } // beast diff --git a/include/beast/websocket/impl/read_frame_op.ipp b/include/beast/websocket/impl/read.ipp similarity index 61% rename from include/beast/websocket/impl/read_frame_op.ipp rename to include/beast/websocket/impl/read.ipp index 670db2c8..d4acfee5 100644 --- a/include/beast/websocket/impl/read_frame_op.ipp +++ b/include/beast/websocket/impl/read.ipp @@ -5,13 +5,15 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_WEBSOCKET_IMPL_READ_FRAME_OP_HPP -#define BEAST_WEBSOCKET_IMPL_READ_FRAME_OP_HPP +#ifndef BEAST_WEBSOCKET_IMPL_READ_IPP +#define BEAST_WEBSOCKET_IMPL_READ_IPP #include +#include #include #include #include +#include #include #include #include @@ -19,6 +21,8 @@ namespace beast { namespace websocket { +//------------------------------------------------------------------------------ + // Reads a single message frame, // processes any received control frames. // @@ -541,6 +545,368 @@ upcall: d.h(ec); } +template +template +typename async_completion< + ReadHandler, void(error_code)>::result_type +stream:: +async_read_frame(frame_info& fi, + DynamicBuffer& dynabuf, ReadHandler&& handler) +{ + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); + static_assert(beast::is_DynamicBuffer::value, + "DynamicBuffer requirements not met"); + beast::async_completion< + ReadHandler, void(error_code)> completion(handler); + read_frame_op{ + completion.handler, *this, fi, dynabuf}; + return completion.result.get(); +} + +template +template +void +stream:: +read_frame(frame_info& fi, DynamicBuffer& dynabuf) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + static_assert(beast::is_DynamicBuffer::value, + "DynamicBuffer requirements not met"); + error_code ec; + read_frame(fi, dynabuf, ec); + if(ec) + throw system_error{ec}; +} + +template +template +void +stream:: +read_frame(frame_info& fi, DynamicBuffer& dynabuf, error_code& ec) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + static_assert(beast::is_DynamicBuffer::value, + "DynamicBuffer requirements not met"); + close_code::value code{}; + for(;;) + { + if(rd_need_ == 0) + { + // read header + detail::frame_streambuf fb; + do_read_fh(fb, code, ec); + failed_ = ec != 0; + if(failed_) + return; + if(code != close_code::none) + break; + if(detail::is_control(rd_fh_.op)) + { + // read control payload + if(rd_fh_.len > 0) + { + auto const mb = fb.prepare( + static_cast(rd_fh_.len)); + fb.commit(boost::asio::read(stream_, mb, ec)); + failed_ = ec != 0; + if(failed_) + return; + if(rd_fh_.mask) + detail::mask_inplace(mb, rd_key_); + fb.commit(static_cast(rd_fh_.len)); + } + if(rd_fh_.op == opcode::ping) + { + ping_data data; + detail::read(data, fb.data()); + fb.reset(); + write_ping( + fb, opcode::pong, data); + boost::asio::write(stream_, fb.data(), ec); + failed_ = ec != 0; + if(failed_) + return; + continue; + } + else if(rd_fh_.op == opcode::pong) + { + ping_data payload; + detail::read(payload, fb.data()); + if(pong_cb_) + pong_cb_(payload); + continue; + } + BOOST_ASSERT(rd_fh_.op == opcode::close); + { + detail::read(cr_, fb.data(), code); + if(code != close_code::none) + break; + if(! wr_close_) + { + auto cr = cr_; + if(cr.code == close_code::none) + cr.code = close_code::normal; + cr.reason = ""; + fb.reset(); + wr_close_ = true; + write_close(fb, cr); + boost::asio::write(stream_, fb.data(), ec); + failed_ = ec != 0; + if(failed_) + return; + } + break; + } + } + if(rd_need_ == 0 && ! rd_fh_.fin) + { + // empty frame + continue; + } + } + // read payload + auto smb = dynabuf.prepare( + detail::clamp(rd_need_)); + auto const bytes_transferred = + stream_.read_some(smb, ec); + failed_ = ec != 0; + if(failed_) + return; + rd_need_ -= bytes_transferred; + auto const pb = prepare_buffers( + bytes_transferred, smb); + if(rd_fh_.mask) + detail::mask_inplace(pb, rd_key_); + if(rd_opcode_ == opcode::text) + { + if(! rd_utf8_check_.write(pb) || + (rd_need_ == 0 && rd_fh_.fin && + ! rd_utf8_check_.finish())) + { + code = close_code::bad_payload; + break; + } + } + dynabuf.commit(bytes_transferred); + fi.op = rd_opcode_; + fi.fin = rd_fh_.fin && rd_need_ == 0; + return; + } + if(code != close_code::none) + { + // Fail the connection (per rfc6455) + if(! wr_close_) + { + wr_close_ = true; + detail::frame_streambuf fb; + write_close(fb, code); + boost::asio::write(stream_, fb.data(), ec); + failed_ = ec != 0; + if(failed_) + return; + } + websocket_helpers::call_teardown(next_layer(), ec); + failed_ = ec != 0; + if(failed_) + return; + ec = error::failed; + failed_ = true; + return; + } + if(! ec) + websocket_helpers::call_teardown(next_layer(), ec); + if(! ec) + ec = error::closed; + failed_ = ec != 0; +} + +//------------------------------------------------------------------------------ + +// read an entire message +// +template +template +class stream::read_op +{ + using alloc_type = + handler_alloc; + + struct data + { + stream& ws; + opcode& op; + DynamicBuffer& db; + Handler h; + frame_info fi; + bool cont; + int state = 0; + + template + data(DeducedHandler&& h_, + stream& ws_, opcode& op_, + DynamicBuffer& sb_) + : ws(ws_) + , op(op_) + , db(sb_) + , h(std::forward(h_)) + , cont(boost_asio_handler_cont_helpers:: + is_continuation(h)) + { + } + }; + + std::shared_ptr d_; + +public: + read_op(read_op&&) = default; + read_op(read_op const&) = default; + + template + read_op(DeducedHandler&& h, + stream& ws, Args&&... args) + : d_(std::allocate_shared(alloc_type{h}, + std::forward(h), ws, + std::forward(args)...)) + { + (*this)(error_code{}, false); + } + + void operator()( + error_code const& ec, bool again = true); + + friend + void* asio_handler_allocate( + std::size_t size, read_op* op) + { + return boost_asio_handler_alloc_helpers:: + allocate(size, op->d_->h); + } + + friend + void asio_handler_deallocate( + void* p, std::size_t size, read_op* op) + { + return boost_asio_handler_alloc_helpers:: + deallocate(p, size, op->d_->h); + } + + friend + bool asio_handler_is_continuation(read_op* op) + { + return op->d_->cont; + } + + template + friend + void asio_handler_invoke(Function&& f, read_op* op) + { + return boost_asio_handler_invoke_helpers:: + invoke(f, op->d_->h); + } +}; + +template +template +void +stream::read_op:: +operator()(error_code const& ec, bool again) +{ + auto& d = *d_; + d.cont = d.cont || again; + while(! ec) + { + switch(d.state) + { + case 0: + // read payload + d.state = 1; +#if 0 + // VFALCO This causes dereference of null, because + // the handler is moved from the data block + // before asio_handler_deallocate is called. + d.ws.async_read_frame( + d.fi, d.db, std::move(*this)); +#else + d.ws.async_read_frame(d.fi, d.db, *this); +#endif + return; + + // got payload + case 1: + d.op = d.fi.op; + if(d.fi.fin) + goto upcall; + d.state = 0; + break; + } + } +upcall: + d.h(ec); +} + +template +template +typename async_completion< + ReadHandler, void(error_code)>::result_type +stream:: +async_read(opcode& op, + DynamicBuffer& dynabuf, ReadHandler&& handler) +{ + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); + static_assert(beast::is_DynamicBuffer::value, + "DynamicBuffer requirements not met"); + beast::async_completion< + ReadHandler, void(error_code) + > completion(handler); + read_op{ + completion.handler, *this, op, dynabuf}; + return completion.result.get(); +} + +template +template +void +stream:: +read(opcode& op, DynamicBuffer& dynabuf) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + static_assert(beast::is_DynamicBuffer::value, + "DynamicBuffer requirements not met"); + error_code ec; + read(op, dynabuf, ec); + if(ec) + throw system_error{ec}; +} + +template +template +void +stream:: +read(opcode& op, DynamicBuffer& dynabuf, error_code& ec) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + static_assert(beast::is_DynamicBuffer::value, + "DynamicBuffer requirements not met"); + frame_info fi; + for(;;) + { + read_frame(fi, dynabuf, ec); + if(ec) + break; + op = fi.op; + if(fi.fin) + break; + } +} + +//------------------------------------------------------------------------------ + } // websocket } // beast diff --git a/include/beast/websocket/impl/read_op.ipp b/include/beast/websocket/impl/read_op.ipp deleted file mode 100644 index 35fa3a0c..00000000 --- a/include/beast/websocket/impl/read_op.ipp +++ /dev/null @@ -1,142 +0,0 @@ -// -// Copyright (c) 2013-2016 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_WEBSOCKET_IMPL_READ_OP_HPP -#define BEAST_WEBSOCKET_IMPL_READ_OP_HPP - -#include -#include - -namespace beast { -namespace websocket { - -// read an entire message -// -template -template -class stream::read_op -{ - using alloc_type = - handler_alloc; - - struct data - { - stream& ws; - opcode& op; - DynamicBuffer& db; - Handler h; - frame_info fi; - bool cont; - int state = 0; - - template - data(DeducedHandler&& h_, - stream& ws_, opcode& op_, - DynamicBuffer& sb_) - : ws(ws_) - , op(op_) - , db(sb_) - , h(std::forward(h_)) - , cont(boost_asio_handler_cont_helpers:: - is_continuation(h)) - { - } - }; - - std::shared_ptr d_; - -public: - read_op(read_op&&) = default; - read_op(read_op const&) = default; - - template - read_op(DeducedHandler&& h, - stream& ws, Args&&... args) - : d_(std::allocate_shared(alloc_type{h}, - std::forward(h), ws, - std::forward(args)...)) - { - (*this)(error_code{}, false); - } - - void operator()( - error_code const& ec, bool again = true); - - friend - void* asio_handler_allocate( - std::size_t size, read_op* op) - { - return boost_asio_handler_alloc_helpers:: - allocate(size, op->d_->h); - } - - friend - void asio_handler_deallocate( - void* p, std::size_t size, read_op* op) - { - return boost_asio_handler_alloc_helpers:: - deallocate(p, size, op->d_->h); - } - - friend - bool asio_handler_is_continuation(read_op* op) - { - return op->d_->cont; - } - - template - friend - void asio_handler_invoke(Function&& f, read_op* op) - { - return boost_asio_handler_invoke_helpers:: - invoke(f, op->d_->h); - } -}; - -template -template -void -stream::read_op:: -operator()(error_code const& ec, bool again) -{ - auto& d = *d_; - d.cont = d.cont || again; - while(! ec) - { - switch(d.state) - { - case 0: - // read payload - d.state = 1; -#if 0 - // VFALCO This causes dereference of null, because - // the handler is moved from the data block - // before asio_handler_deallocate is called. - d.ws.async_read_frame( - d.fi, d.db, std::move(*this)); -#else - d.ws.async_read_frame(d.fi, d.db, *this); -#endif - return; - - // got payload - case 1: - d.op = d.fi.op; - if(d.fi.fin) - goto upcall; - d.state = 0; - break; - } - } -upcall: - d.h(ec); -} - -} // websocket -} // beast - -#endif diff --git a/include/beast/websocket/impl/response_op.ipp b/include/beast/websocket/impl/response_op.ipp deleted file mode 100644 index 2c9d5446..00000000 --- a/include/beast/websocket/impl/response_op.ipp +++ /dev/null @@ -1,139 +0,0 @@ -// -// Copyright (c) 2013-2016 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_WEBSOCKET_IMPL_RESPONSE_OP_HPP -#define BEAST_WEBSOCKET_IMPL_RESPONSE_OP_HPP - -#include -#include -#include -#include -#include - -namespace beast { -namespace websocket { - -// Respond to an upgrade HTTP request -template -template -class stream::response_op -{ - using alloc_type = - handler_alloc; - - struct data - { - stream& ws; - http::response resp; - Handler h; - error_code final_ec; - bool cont; - int state = 0; - - template - data(DeducedHandler&& h_, stream& ws_, - http::request const& req, - bool cont_) - : ws(ws_) - , resp(ws_.build_response(req)) - , h(std::forward(h_)) - , cont(cont_) - { - // can't call stream::reset() here - // otherwise accept_op will malfunction - // - if(resp.status != 101) - final_ec = error::handshake_failed; - } - }; - - std::shared_ptr d_; - -public: - response_op(response_op&&) = default; - response_op(response_op const&) = default; - - template - response_op(DeducedHandler&& h, - stream& ws, Args&&... args) - : d_(std::allocate_shared(alloc_type{h}, - std::forward(h), ws, - std::forward(args)...)) - { - (*this)(error_code{}, false); - } - - void operator()( - error_code ec, bool again = true); - - friend - void* asio_handler_allocate( - std::size_t size, response_op* op) - { - return boost_asio_handler_alloc_helpers:: - allocate(size, op->d_->h); - } - - friend - void asio_handler_deallocate( - void* p, std::size_t size, response_op* op) - { - return boost_asio_handler_alloc_helpers:: - deallocate(p, size, op->d_->h); - } - - friend - bool asio_handler_is_continuation(response_op* op) - { - return op->d_->cont; - } - - template - friend - void asio_handler_invoke(Function&& f, response_op* op) - { - return boost_asio_handler_invoke_helpers:: - invoke(f, op->d_->h); - } -}; - -template -template -void -stream::response_op:: -operator()(error_code ec, bool again) -{ - auto& d = *d_; - d.cont = d.cont || again; - while(! ec && d.state != 99) - { - switch(d.state) - { - case 0: - // send response - d.state = 1; - http::async_write(d.ws.next_layer(), - d.resp, std::move(*this)); - return; - - // sent response - case 1: - d.state = 99; - ec = d.final_ec; - if(! ec) - d.ws.open(detail::role_type::server); - break; - } - } - d.h(ec); -} - -} // websocket -} // beast - -#endif diff --git a/include/beast/websocket/impl/stream.ipp b/include/beast/websocket/impl/stream.ipp index 6675461c..671dfd9c 100644 --- a/include/beast/websocket/impl/stream.ipp +++ b/include/beast/websocket/impl/stream.ipp @@ -10,15 +10,6 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include @@ -46,779 +37,6 @@ stream(Args&&... args) { } -template -void -stream:: -accept() -{ - static_assert(is_SyncStream::value, - "SyncStream requirements not met"); - error_code ec; - accept(boost::asio::null_buffers{}, ec); - if(ec) - throw system_error{ec}; -} - -template -void -stream:: -accept(error_code& ec) -{ - static_assert(is_SyncStream::value, - "SyncStream requirements not met"); - accept(boost::asio::null_buffers{}, 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 -void -stream:: -accept(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(buffers, ec); - if(ec) - throw system_error{ec}; -} - -template -template -void -stream:: -accept(ConstBufferSequence const& buffers, error_code& ec) -{ - static_assert(is_SyncStream::value, - "SyncStream requirements not met"); - static_assert(beast::is_ConstBufferSequence< - ConstBufferSequence>::value, - "ConstBufferSequence requirements not met"); - using boost::asio::buffer_copy; - using boost::asio::buffer_size; - reset(); - stream_.buffer().commit(buffer_copy( - stream_.buffer().prepare( - buffer_size(buffers)), buffers)); - http::request m; - http::read(next_layer(), stream_.buffer(), m, ec); - if(ec) - return; - accept(m, ec); -} - -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 -void -stream:: -accept(http::request const& request) -{ - static_assert(is_SyncStream::value, - "SyncStream requirements not met"); - error_code ec; - accept(request, ec); - if(ec) - throw system_error{ec}; -} - -template -template -void -stream:: -accept(http::request const& req, - error_code& ec) -{ - static_assert(is_SyncStream::value, - "SyncStream requirements not met"); - reset(); - 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; - } - open(detail::role_type::server); -} - -template -template -typename async_completion< - AcceptHandler, void(error_code)>::result_type -stream:: -async_accept(http::request 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, - boost_asio_handler_cont_helpers:: - is_continuation(completion.handler)}; - return completion.result.get(); -} - -template -void -stream:: -handshake(boost::string_ref const& host, - boost::string_ref const& resource) -{ - static_assert(is_SyncStream::value, - "SyncStream requirements not met"); - error_code ec; - handshake(host, resource, ec); - if(ec) - throw system_error{ec}; -} - -template -void -stream:: -handshake(boost::string_ref const& host, - boost::string_ref const& resource, error_code& ec) -{ - static_assert(is_SyncStream::value, - "SyncStream requirements not met"); - reset(); - std::string key; - http::write(stream_, - build_request(host, resource, key), ec); - if(ec) - return; - http::response res; - http::read(next_layer(), stream_.buffer(), res, ec); - if(ec) - return; - do_response(res, key, ec); -} - -template -template -typename async_completion< - HandshakeHandler, void(error_code)>::result_type -stream:: -async_handshake(boost::string_ref const& host, - boost::string_ref const& resource, HandshakeHandler&& handler) -{ - static_assert(is_AsyncStream::value, - "AsyncStream requirements not met"); - beast::async_completion< - HandshakeHandler, void(error_code) - > completion(handler); - handshake_op{ - completion.handler, *this, host, resource}; - return completion.result.get(); -} - -template -void -stream:: -close(close_reason const& cr) -{ - static_assert(is_SyncStream::value, - "SyncStream requirements not met"); - error_code ec; - close(cr, ec); - if(ec) - throw system_error{ec}; -} - -template -void -stream:: -close(close_reason const& cr, error_code& ec) -{ - static_assert(is_SyncStream::value, - "SyncStream requirements not met"); - BOOST_ASSERT(! wr_close_); - wr_close_ = true; - detail::frame_streambuf fb; - write_close(fb, cr); - boost::asio::write(stream_, fb.data(), ec); - failed_ = ec != 0; -} - -template -template -typename async_completion< - CloseHandler, void(error_code)>::result_type -stream:: -async_close(close_reason const& cr, CloseHandler&& handler) -{ - static_assert(is_AsyncStream::value, - "AsyncStream requirements not met"); - beast::async_completion< - CloseHandler, void(error_code) - > completion(handler); - close_op{ - completion.handler, *this, cr}; - return completion.result.get(); -} - -template -void -stream:: -ping(ping_data const& payload) -{ - error_code ec; - ping(payload, ec); - if(ec) - throw system_error{ec}; -} - -template -void -stream:: -ping(ping_data const& payload, error_code& ec) -{ - detail::frame_streambuf db; - write_ping( - db, opcode::ping, payload); - boost::asio::write(stream_, db.data(), ec); -} - -template -template -typename async_completion< - PingHandler, void(error_code)>::result_type -stream:: -async_ping(ping_data const& payload, PingHandler&& handler) -{ - static_assert(is_AsyncStream::value, - "AsyncStream requirements requirements not met"); - beast::async_completion< - PingHandler, void(error_code) - > completion(handler); - ping_op{ - completion.handler, *this, payload}; - return completion.result.get(); -} - -template -template -void -stream:: -read(opcode& op, DynamicBuffer& dynabuf) -{ - static_assert(is_SyncStream::value, - "SyncStream requirements not met"); - static_assert(beast::is_DynamicBuffer::value, - "DynamicBuffer requirements not met"); - error_code ec; - read(op, dynabuf, ec); - if(ec) - throw system_error{ec}; -} - -template -template -void -stream:: -read(opcode& op, DynamicBuffer& dynabuf, error_code& ec) -{ - static_assert(is_SyncStream::value, - "SyncStream requirements not met"); - static_assert(beast::is_DynamicBuffer::value, - "DynamicBuffer requirements not met"); - frame_info fi; - for(;;) - { - read_frame(fi, dynabuf, ec); - if(ec) - break; - op = fi.op; - if(fi.fin) - break; - } -} - -template -template -typename async_completion< - ReadHandler, void(error_code)>::result_type -stream:: -async_read(opcode& op, - DynamicBuffer& dynabuf, ReadHandler&& handler) -{ - static_assert(is_AsyncStream::value, - "AsyncStream requirements requirements not met"); - static_assert(beast::is_DynamicBuffer::value, - "DynamicBuffer requirements not met"); - beast::async_completion< - ReadHandler, void(error_code) - > completion(handler); - read_op{ - completion.handler, *this, op, dynabuf}; - return completion.result.get(); -} - -template -template -void -stream:: -read_frame(frame_info& fi, DynamicBuffer& dynabuf) -{ - static_assert(is_SyncStream::value, - "SyncStream requirements not met"); - static_assert(beast::is_DynamicBuffer::value, - "DynamicBuffer requirements not met"); - error_code ec; - read_frame(fi, dynabuf, ec); - if(ec) - throw system_error{ec}; -} - -template -template -void -stream:: -read_frame(frame_info& fi, DynamicBuffer& dynabuf, error_code& ec) -{ - static_assert(is_SyncStream::value, - "SyncStream requirements not met"); - static_assert(beast::is_DynamicBuffer::value, - "DynamicBuffer requirements not met"); - close_code::value code{}; - for(;;) - { - if(rd_need_ == 0) - { - // read header - detail::frame_streambuf fb; - do_read_fh(fb, code, ec); - failed_ = ec != 0; - if(failed_) - return; - if(code != close_code::none) - break; - if(detail::is_control(rd_fh_.op)) - { - // read control payload - if(rd_fh_.len > 0) - { - auto const mb = fb.prepare( - static_cast(rd_fh_.len)); - fb.commit(boost::asio::read(stream_, mb, ec)); - failed_ = ec != 0; - if(failed_) - return; - if(rd_fh_.mask) - detail::mask_inplace(mb, rd_key_); - fb.commit(static_cast(rd_fh_.len)); - } - if(rd_fh_.op == opcode::ping) - { - ping_data data; - detail::read(data, fb.data()); - fb.reset(); - write_ping( - fb, opcode::pong, data); - boost::asio::write(stream_, fb.data(), ec); - failed_ = ec != 0; - if(failed_) - return; - continue; - } - else if(rd_fh_.op == opcode::pong) - { - ping_data payload; - detail::read(payload, fb.data()); - if(pong_cb_) - pong_cb_(payload); - continue; - } - BOOST_ASSERT(rd_fh_.op == opcode::close); - { - detail::read(cr_, fb.data(), code); - if(code != close_code::none) - break; - if(! wr_close_) - { - auto cr = cr_; - if(cr.code == close_code::none) - cr.code = close_code::normal; - cr.reason = ""; - fb.reset(); - wr_close_ = true; - write_close(fb, cr); - boost::asio::write(stream_, fb.data(), ec); - failed_ = ec != 0; - if(failed_) - return; - } - break; - } - } - if(rd_need_ == 0 && ! rd_fh_.fin) - { - // empty frame - continue; - } - } - // read payload - auto smb = dynabuf.prepare( - detail::clamp(rd_need_)); - auto const bytes_transferred = - stream_.read_some(smb, ec); - failed_ = ec != 0; - if(failed_) - return; - rd_need_ -= bytes_transferred; - auto const pb = prepare_buffers( - bytes_transferred, smb); - if(rd_fh_.mask) - detail::mask_inplace(pb, rd_key_); - if(rd_opcode_ == opcode::text) - { - if(! rd_utf8_check_.write(pb) || - (rd_need_ == 0 && rd_fh_.fin && - ! rd_utf8_check_.finish())) - { - code = close_code::bad_payload; - break; - } - } - dynabuf.commit(bytes_transferred); - fi.op = rd_opcode_; - fi.fin = rd_fh_.fin && rd_need_ == 0; - return; - } - if(code != close_code::none) - { - // Fail the connection (per rfc6455) - if(! wr_close_) - { - wr_close_ = true; - detail::frame_streambuf fb; - write_close(fb, code); - boost::asio::write(stream_, fb.data(), ec); - failed_ = ec != 0; - if(failed_) - return; - } - websocket_helpers::call_teardown(next_layer(), ec); - failed_ = ec != 0; - if(failed_) - return; - ec = error::failed; - failed_ = true; - return; - } - if(! ec) - websocket_helpers::call_teardown(next_layer(), ec); - if(! ec) - ec = error::closed; - failed_ = ec != 0; -} - -template -template -typename async_completion< - ReadHandler, void(error_code)>::result_type -stream:: -async_read_frame(frame_info& fi, - DynamicBuffer& dynabuf, ReadHandler&& handler) -{ - static_assert(is_AsyncStream::value, - "AsyncStream requirements requirements not met"); - static_assert(beast::is_DynamicBuffer::value, - "DynamicBuffer requirements not met"); - beast::async_completion< - ReadHandler, void(error_code)> completion(handler); - read_frame_op{ - completion.handler, *this, fi, dynabuf}; - return completion.result.get(); -} - -template -template -void -stream:: -write(ConstBufferSequence const& buffers) -{ - static_assert(is_SyncStream::value, - "SyncStream requirements not met"); - static_assert(beast::is_ConstBufferSequence< - ConstBufferSequence>::value, - "ConstBufferSequence requirements not met"); - error_code ec; - write(buffers, ec); - if(ec) - throw system_error{ec}; -} - -template -template -void -stream:: -write(ConstBufferSequence const& buffers, error_code& ec) -{ - static_assert(is_SyncStream::value, - "SyncStream requirements not met"); - static_assert(beast::is_ConstBufferSequence< - ConstBufferSequence>::value, - "ConstBufferSequence requirements not met"); - write_frame(true, buffers, ec); -} - -template -template -typename async_completion< - WriteHandler, void(error_code)>::result_type -stream:: -async_write(ConstBufferSequence const& bs, WriteHandler&& handler) -{ - static_assert(is_AsyncStream::value, - "AsyncStream requirements not met"); - static_assert(beast::is_ConstBufferSequence< - ConstBufferSequence>::value, - "ConstBufferSequence requirements not met"); - beast::async_completion< - WriteHandler, void(error_code)> completion(handler); - write_op{ - completion.handler, *this, bs}; - return completion.result.get(); -} - -template -template -void -stream:: -write_frame(bool fin, ConstBufferSequence const& buffers) -{ - static_assert(is_SyncStream::value, - "SyncStream requirements not met"); - static_assert(beast::is_ConstBufferSequence< - ConstBufferSequence>::value, - "ConstBufferSequence requirements not met"); - error_code ec; - write_frame(fin, buffers, ec); - if(ec) - throw system_error{ec}; -} - -/* -if(compress) - compress buffers into write_buffer - if(write_buffer_avail == write_buffer_size || fin`) - if(mask) - apply mask to write buffer - write frame header, write_buffer as one frame -else if(auto-fragment) - if(fin || write_buffer_avail + buffers size == write_buffer_size) - if(mask) - append buffers to write buffer - apply mask to write buffer - write frame header, write buffer as one frame - - else: - write frame header, write buffer, and buffers as one frame - else: - append buffers to write buffer -else if(mask) - copy buffers to write_buffer - apply mask to write_buffer - write frame header and possibly full write_buffer in a single call - loop: - copy buffers to write_buffer - apply mask to write_buffer - write write_buffer in a single call -else - write frame header, buffers as one frame -*/ -template -template -void -stream:: -write_frame(bool fin, - ConstBufferSequence const& buffers, error_code& ec) -{ - static_assert(is_SyncStream::value, - "SyncStream requirements not met"); - static_assert(beast::is_ConstBufferSequence< - ConstBufferSequence>::value, - "ConstBufferSequence requirements not met"); - using boost::asio::buffer; - using boost::asio::buffer_copy; - using boost::asio::buffer_size; - bool const compress = false; - if(! wr_.cont) - wr_prepare(compress); - detail::frame_header fh; - fh.op = wr_.cont ? opcode::cont : wr_opcode_; - fh.rsv1 = false; - fh.rsv2 = false; - fh.rsv3 = false; - fh.mask = role_ == detail::role_type::client; - auto remain = buffer_size(buffers); - if(compress) - { - // TODO - } - else if(wr_.autofrag) - { - consuming_buffers cb(buffers); - do - { - auto const room = wr_.max - wr_.size; - if(! fin && remain < room) - { - buffer_copy( - buffer(wr_.buf.get() + wr_.size, remain), cb); - wr_.size += remain; - return; - } - auto const n = detail::clamp(remain, room); - buffer_copy( - buffer(wr_.buf.get() + wr_.size, n), cb); - auto const mb = buffer(wr_.buf.get(), wr_.size + n); - if(fh.mask) - { - fh.key = maskgen_(); - detail::prepared_key_type key; - detail::prepare_key(key, fh.key); - detail::mask_inplace(mb, key); - } - fh.fin = fin && n == remain; - fh.len = buffer_size(mb); - detail::fh_streambuf fh_buf; - detail::write(fh_buf, fh); - // send header and payload - boost::asio::write(stream_, - buffer_cat(fh_buf.data(), mb), ec); - failed_ = ec != 0; - if(failed_) - return; - remain -= n; - cb.consume(n); - wr_.size = 0; - fh.op = opcode::cont; - } - while(remain > 0); - wr_.cont = ! fh.fin; - return; - } - else if(fh.mask) - { - consuming_buffers cb(buffers); - fh.fin = fin; - fh.len = remain; - fh.key = maskgen_(); - wr_.cont = ! fh.fin; - detail::fh_streambuf fh_buf; - detail::write(fh_buf, fh); - detail::prepared_key_type key; - detail::prepare_key(key, fh.key); - { - auto const n = detail::clamp(remain, wr_.max); - auto const mb = buffer(wr_.buf.get(), n); - buffer_copy(mb, cb); - cb.consume(n); - remain -= n; - detail::mask_inplace(mb, key); - // send header and payload - boost::asio::write(stream_, - buffer_cat(fh_buf.data(), mb), ec); - failed_ = ec != 0; - if(failed_) - return; - } - while(remain > 0) - { - auto const n = detail::clamp(remain, wr_.max); - auto const mb = buffer(wr_.buf.get(), n); - buffer_copy(mb, cb); - cb.consume(n); - remain -= n; - detail::mask_inplace(mb, key); - // send payload - boost::asio::write(stream_, mb, ec); - failed_ = ec != 0; - if(failed_) - return; - } - return; - } - { - // send header and payload - fh.fin = fin; - fh.len = remain; - wr_.cont = ! fh.fin; - detail::fh_streambuf fh_buf; - detail::write(fh_buf, fh); - boost::asio::write(stream_, - buffer_cat(fh_buf.data(), buffers), ec); - failed_ = ec != 0; - } -} - -template -template -typename async_completion< - WriteHandler, void(error_code)>::result_type -stream:: -async_write_frame(bool fin, - ConstBufferSequence const& bs, WriteHandler&& handler) -{ - static_assert(is_AsyncStream::value, - "AsyncStream requirements not met"); - static_assert(beast::is_ConstBufferSequence< - ConstBufferSequence>::value, - "ConstBufferSequence requirements not met"); - beast::async_completion< - WriteHandler, void(error_code) - > completion(handler); - write_frame_op{completion.handler, - *this, fin, bs}; - return completion.result.get(); -} - //------------------------------------------------------------------------------ template diff --git a/include/beast/websocket/impl/write.ipp b/include/beast/websocket/impl/write.ipp new file mode 100644 index 00000000..ab34abbe --- /dev/null +++ b/include/beast/websocket/impl/write.ipp @@ -0,0 +1,682 @@ +// +// Copyright (c) 2013-2016 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_WEBSOCKET_IMPL_WRITE_IPP +#define BEAST_WEBSOCKET_IMPL_WRITE_IPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace beast { +namespace websocket { + +/* + template + void + write_frame(bool fin, ConstBufferSequence const& buffer) + + Depending on the settings of autofragment role, and compression, + different algorithms are used. + + 1. autofragment: false + compression: false + + In the server role, this will send a single frame in one + system call, by concatenating the frame header and the payload. + + In the client role, this will send a single frame in one system + calls, using the write buffer to calculate masked data. + + 2. autofragment: true + compression: false + + In the server role, this will send one or more frames in one + system call per sent frame. Each frame is sent by concatenating + the frame header and payload. The size of each sent frame will + not exceed the write buffer size option. + + In the client role, this will send one or more frames in one + system call per sent frame, using the write buffer to calculate + masked data. The size of each sent frame will not exceed the + write buffer size option. + + 3. autofragment: false + compression: true + + In the server role, this will... + +*/ +/* + if(compress) + compress buffers into write_buffer + if(write_buffer_avail == write_buffer_size || fin`) + if(mask) + apply mask to write buffer + write frame header, write_buffer as one frame + else if(auto-fragment) + if(fin || write_buffer_avail + buffers size == write_buffer_size) + if(mask) + append buffers to write buffer + apply mask to write buffer + write frame header, write buffer as one frame + + else: + write frame header, write buffer, and buffers as one frame + else: + append buffers to write buffer + else if(mask) + copy buffers to write_buffer + apply mask to write_buffer + write frame header and possibly full write_buffer in a single call + loop: + copy buffers to write_buffer + apply mask to write_buffer + write write_buffer in a single call + else + write frame header, buffers as one frame +*/ + +//------------------------------------------------------------------------------ + +template +template +class stream::write_frame_op +{ + using alloc_type = + handler_alloc; + + struct data : op + { + stream& ws; + consuming_buffers cb; + Handler h; + detail::frame_header fh; + detail::fh_streambuf fh_buf; + detail::prepared_key_type key; + void* tmp; + std::size_t tmp_size; + std::uint64_t remain; + bool cont; + int state = 0; + + template + data(DeducedHandler&& h_, stream& ws_, + bool fin, Buffers const& bs) + : ws(ws_) + , cb(bs) + , h(std::forward(h_)) + , cont(boost_asio_handler_cont_helpers:: + is_continuation(h)) + { + fh.op = ws.wr_.cont ? + opcode::cont : ws.wr_opcode_; + ws.wr_.cont = ! fin; + fh.fin = fin; + fh.rsv1 = false; + fh.rsv2 = false; + fh.rsv3 = false; + fh.len = boost::asio::buffer_size(cb); + fh.mask = ws.role_ == detail::role_type::client; + if(fh.mask) + { + fh.key = ws.maskgen_(); + detail::prepare_key(key, fh.key); + tmp_size = detail::clamp( + fh.len, ws.wr_buf_size_); + tmp = boost_asio_handler_alloc_helpers:: + allocate(tmp_size, h); + remain = fh.len; + } + else + { + tmp = nullptr; + } + detail::write(fh_buf, fh); + } + + ~data() + { + if(tmp) + boost_asio_handler_alloc_helpers:: + deallocate(tmp, tmp_size, h); + } + }; + + std::shared_ptr d_; + +public: + write_frame_op(write_frame_op&&) = default; + write_frame_op(write_frame_op const&) = default; + + template + write_frame_op(DeducedHandler&& h, + stream& ws, Args&&... args) + : d_(std::make_shared( + std::forward(h), ws, + std::forward(args)...)) + { + (*this)(error_code{}, false); + } + + void operator()() + { + (*this)(error_code{}); + } + + void operator()(error_code ec, std::size_t); + + void operator()(error_code ec, bool again = true); + + friend + void* asio_handler_allocate( + std::size_t size, write_frame_op* op) + { + return boost_asio_handler_alloc_helpers:: + allocate(size, op->d_->h); + } + + friend + void asio_handler_deallocate( + void* p, std::size_t size, write_frame_op* op) + { + return boost_asio_handler_alloc_helpers:: + deallocate(p, size, op->d_->h); + } + + friend + bool asio_handler_is_continuation(write_frame_op* op) + { + return op->d_->cont; + } + + template + friend + void asio_handler_invoke(Function&& f, write_frame_op* op) + { + return boost_asio_handler_invoke_helpers:: + invoke(f, op->d_->h); + } +}; + +template +template +void +stream:: +write_frame_op:: +operator()(error_code ec, std::size_t) +{ + auto& d = *d_; + if(ec) + d.ws.failed_ = true; + (*this)(ec); +} + +template +template +void +stream:: +write_frame_op:: +operator()(error_code ec, bool again) +{ + using boost::asio::buffer_copy; + using boost::asio::mutable_buffers_1; + auto& d = *d_; + d.cont = d.cont || again; + if(ec) + goto upcall; + for(;;) + { + switch(d.state) + { + case 0: + if(d.ws.wr_block_) + { + // suspend + d.state = 3; + d.ws.wr_op_.template emplace< + write_frame_op>(std::move(*this)); + return; + } + if(d.ws.failed_ || d.ws.wr_close_) + { + // call handler + d.state = 99; + d.ws.get_io_service().post( + bind_handler(std::move(*this), + boost::asio::error::operation_aborted)); + return; + } + // fall through + + case 1: + { + if(! d.fh.mask) + { + // send header and entire payload + d.state = 99; + BOOST_ASSERT(! d.ws.wr_block_); + d.ws.wr_block_ = &d; + boost::asio::async_write(d.ws.stream_, + buffer_cat(d.fh_buf.data(), d.cb), + std::move(*this)); + return; + } + auto const n = + detail::clamp(d.remain, d.tmp_size); + mutable_buffers_1 mb{d.tmp, n}; + buffer_copy(mb, d.cb); + d.cb.consume(n); + d.remain -= n; + detail::mask_inplace(mb, d.key); + // send header and payload + d.state = d.remain > 0 ? 2 : 99; + BOOST_ASSERT(! d.ws.wr_block_); + d.ws.wr_block_ = &d; + boost::asio::async_write(d.ws.stream_, + buffer_cat(d.fh_buf.data(), + mb), std::move(*this)); + return; + } + + // sent masked payload + case 2: + { + auto const n = + detail::clamp(d.remain, d.tmp_size); + mutable_buffers_1 mb{d.tmp, + static_cast(n)}; + buffer_copy(mb, d.cb); + d.cb.consume(n); + d.remain -= n; + detail::mask_inplace(mb, d.key); + // send payload + if(d.remain == 0) + d.state = 99; + BOOST_ASSERT(d.ws.wr_block_ == &d); + boost::asio::async_write( + d.ws.stream_, mb, std::move(*this)); + return; + } + + case 3: + d.state = 4; + d.ws.get_io_service().post(bind_handler( + std::move(*this), ec)); + return; + + case 4: + if(d.ws.failed_ || d.ws.wr_close_) + { + // call handler + ec = boost::asio::error::operation_aborted; + goto upcall; + } + d.state = 1; + break; + + case 99: + goto upcall; + } + } +upcall: + if(d.tmp) + { + boost_asio_handler_alloc_helpers:: + deallocate(d.tmp, d.tmp_size, d.h); + d.tmp = nullptr; + } + if(d.ws.wr_block_ == &d) + d.ws.wr_block_ = nullptr; + d.ws.rd_op_.maybe_invoke(); + d.h(ec); +} + +template +template +typename async_completion< + WriteHandler, void(error_code)>::result_type +stream:: +async_write_frame(bool fin, + ConstBufferSequence const& bs, WriteHandler&& handler) +{ + static_assert(is_AsyncStream::value, + "AsyncStream requirements not met"); + static_assert(beast::is_ConstBufferSequence< + ConstBufferSequence>::value, + "ConstBufferSequence requirements not met"); + beast::async_completion< + WriteHandler, void(error_code) + > completion(handler); + write_frame_op{completion.handler, + *this, fin, bs}; + return completion.result.get(); +} + +template +template +void +stream:: +write_frame(bool fin, ConstBufferSequence const& buffers) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + static_assert(beast::is_ConstBufferSequence< + ConstBufferSequence>::value, + "ConstBufferSequence requirements not met"); + error_code ec; + write_frame(fin, buffers, ec); + if(ec) + throw system_error{ec}; +} + +template +template +void +stream:: +write_frame(bool fin, + ConstBufferSequence const& buffers, error_code& ec) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + static_assert(beast::is_ConstBufferSequence< + ConstBufferSequence>::value, + "ConstBufferSequence requirements not met"); + using boost::asio::buffer; + using boost::asio::buffer_copy; + using boost::asio::buffer_size; + bool const compress = false; + if(! wr_.cont) + wr_prepare(compress); + detail::frame_header fh; + fh.op = wr_.cont ? opcode::cont : wr_opcode_; + fh.rsv1 = false; + fh.rsv2 = false; + fh.rsv3 = false; + fh.mask = role_ == detail::role_type::client; + auto remain = buffer_size(buffers); + if(compress) + { + // TODO + } + else if(wr_.autofrag) + { + consuming_buffers cb(buffers); + do + { + auto const room = wr_.max - wr_.size; + if(! fin && remain < room) + { + buffer_copy( + buffer(wr_.buf.get() + wr_.size, remain), cb); + wr_.size += remain; + return; + } + auto const n = detail::clamp(remain, room); + buffer_copy( + buffer(wr_.buf.get() + wr_.size, n), cb); + auto const mb = buffer(wr_.buf.get(), wr_.size + n); + if(fh.mask) + { + fh.key = maskgen_(); + detail::prepared_key_type key; + detail::prepare_key(key, fh.key); + detail::mask_inplace(mb, key); + } + fh.fin = fin && n == remain; + fh.len = buffer_size(mb); + detail::fh_streambuf fh_buf; + detail::write(fh_buf, fh); + // send header and payload + boost::asio::write(stream_, + buffer_cat(fh_buf.data(), mb), ec); + failed_ = ec != 0; + if(failed_) + return; + remain -= n; + cb.consume(n); + wr_.size = 0; + fh.op = opcode::cont; + } + while(remain > 0); + wr_.cont = ! fh.fin; + return; + } + else if(fh.mask) + { + consuming_buffers cb(buffers); + fh.fin = fin; + fh.len = remain; + fh.key = maskgen_(); + wr_.cont = ! fh.fin; + detail::fh_streambuf fh_buf; + detail::write(fh_buf, fh); + detail::prepared_key_type key; + detail::prepare_key(key, fh.key); + { + auto const n = detail::clamp(remain, wr_.max); + auto const mb = buffer(wr_.buf.get(), n); + buffer_copy(mb, cb); + cb.consume(n); + remain -= n; + detail::mask_inplace(mb, key); + // send header and payload + boost::asio::write(stream_, + buffer_cat(fh_buf.data(), mb), ec); + failed_ = ec != 0; + if(failed_) + return; + } + while(remain > 0) + { + auto const n = detail::clamp(remain, wr_.max); + auto const mb = buffer(wr_.buf.get(), n); + buffer_copy(mb, cb); + cb.consume(n); + remain -= n; + detail::mask_inplace(mb, key); + // send payload + boost::asio::write(stream_, mb, ec); + failed_ = ec != 0; + if(failed_) + return; + } + return; + } + { + // send header and payload + fh.fin = fin; + fh.len = remain; + wr_.cont = ! fh.fin; + detail::fh_streambuf fh_buf; + detail::write(fh_buf, fh); + boost::asio::write(stream_, + buffer_cat(fh_buf.data(), buffers), ec); + failed_ = ec != 0; + } +} + +//------------------------------------------------------------------------------ + +template +template +class stream::write_op +{ + using alloc_type = + handler_alloc; + + struct data : op + { + stream& ws; + consuming_buffers cb; + Handler h; + std::size_t remain; + bool cont; + int state = 0; + + template + data(DeducedHandler&& h_, + stream& ws_, Buffers const& bs) + : ws(ws_) + , cb(bs) + , h(std::forward(h_)) + , remain(boost::asio::buffer_size(cb)) + , cont(boost_asio_handler_cont_helpers:: + is_continuation(h)) + { + } + }; + + std::shared_ptr d_; + +public: + write_op(write_op&&) = default; + write_op(write_op const&) = default; + + template + explicit + write_op(DeducedHandler&& h, + stream& ws, Args&&... args) + : d_(std::allocate_shared(alloc_type{h}, + std::forward(h), ws, + std::forward(args)...)) + { + (*this)(error_code{}, false); + } + + void operator()(error_code ec, bool again = true); + + friend + void* asio_handler_allocate( + std::size_t size, write_op* op) + { + return boost_asio_handler_alloc_helpers:: + allocate(size, op->d_->h); + } + + friend + void asio_handler_deallocate( + void* p, std::size_t size, write_op* op) + { + return boost_asio_handler_alloc_helpers:: + deallocate(p, size, op->d_->h); + } + + friend + bool asio_handler_is_continuation(write_op* op) + { + return op->d_->cont; + } + + template + friend + void asio_handler_invoke(Function&& f, write_op* op) + { + return boost_asio_handler_invoke_helpers:: + invoke(f, op->d_->h); + } +}; + +template +template +void +stream:: +write_op:: +operator()(error_code ec, bool again) +{ + auto& d = *d_; + d.cont = d.cont || again; + if(! ec) + { + switch(d.state) + { + case 0: + { + auto const n = d.remain; + d.remain -= n; + auto const fin = d.remain <= 0; + if(fin) + d.state = 99; + auto const pb = prepare_buffers(n, d.cb); + d.cb.consume(n); + d.ws.async_write_frame(fin, pb, std::move(*this)); + return; + } + + case 99: + break; + } + } + d.h(ec); +} + +template +template +typename async_completion< + WriteHandler, void(error_code)>::result_type +stream:: +async_write(ConstBufferSequence const& bs, WriteHandler&& handler) +{ + static_assert(is_AsyncStream::value, + "AsyncStream requirements not met"); + static_assert(beast::is_ConstBufferSequence< + ConstBufferSequence>::value, + "ConstBufferSequence requirements not met"); + beast::async_completion< + WriteHandler, void(error_code)> completion(handler); + write_op{ + completion.handler, *this, bs}; + return completion.result.get(); +} + +template +template +void +stream:: +write(ConstBufferSequence const& buffers) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + static_assert(beast::is_ConstBufferSequence< + ConstBufferSequence>::value, + "ConstBufferSequence requirements not met"); + error_code ec; + write(buffers, ec); + if(ec) + throw system_error{ec}; +} + +template +template +void +stream:: +write(ConstBufferSequence const& buffers, error_code& ec) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + static_assert(beast::is_ConstBufferSequence< + ConstBufferSequence>::value, + "ConstBufferSequence requirements not met"); + write_frame(true, buffers, ec); +} + +//------------------------------------------------------------------------------ + +} // websocket +} // beast + +#endif diff --git a/include/beast/websocket/impl/write_frame_op.ipp b/include/beast/websocket/impl/write_frame_op.ipp deleted file mode 100644 index 5bfee8af..00000000 --- a/include/beast/websocket/impl/write_frame_op.ipp +++ /dev/null @@ -1,282 +0,0 @@ -// -// Copyright (c) 2013-2016 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_WEBSOCKET_IMPL_WRITE_FRAME_OP_HPP -#define BEAST_WEBSOCKET_IMPL_WRITE_FRAME_OP_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace beast { -namespace websocket { - -// write a frame -// -template -template -class stream::write_frame_op -{ - using alloc_type = - handler_alloc; - - struct data : op - { - stream& ws; - consuming_buffers cb; - Handler h; - detail::frame_header fh; - detail::fh_streambuf fh_buf; - detail::prepared_key_type key; - void* tmp; - std::size_t tmp_size; - std::uint64_t remain; - bool cont; - int state = 0; - - template - data(DeducedHandler&& h_, stream& ws_, - bool fin, Buffers const& bs) - : ws(ws_) - , cb(bs) - , h(std::forward(h_)) - , cont(boost_asio_handler_cont_helpers:: - is_continuation(h)) - { - fh.op = ws.wr_.cont ? - opcode::cont : ws.wr_opcode_; - ws.wr_.cont = ! fin; - fh.fin = fin; - fh.rsv1 = false; - fh.rsv2 = false; - fh.rsv3 = false; - fh.len = boost::asio::buffer_size(cb); - fh.mask = ws.role_ == detail::role_type::client; - if(fh.mask) - { - fh.key = ws.maskgen_(); - detail::prepare_key(key, fh.key); - tmp_size = detail::clamp( - fh.len, ws.wr_buf_size_); - tmp = boost_asio_handler_alloc_helpers:: - allocate(tmp_size, h); - remain = fh.len; - } - else - { - tmp = nullptr; - } - detail::write(fh_buf, fh); - } - - ~data() - { - if(tmp) - boost_asio_handler_alloc_helpers:: - deallocate(tmp, tmp_size, h); - } - }; - - std::shared_ptr d_; - -public: - write_frame_op(write_frame_op&&) = default; - write_frame_op(write_frame_op const&) = default; - - template - write_frame_op(DeducedHandler&& h, - stream& ws, Args&&... args) - : d_(std::make_shared( - std::forward(h), ws, - std::forward(args)...)) - { - (*this)(error_code{}, false); - } - - void operator()() - { - (*this)(error_code{}); - } - - void operator()(error_code ec, std::size_t); - - void operator()(error_code ec, bool again = true); - - friend - void* asio_handler_allocate( - std::size_t size, write_frame_op* op) - { - return boost_asio_handler_alloc_helpers:: - allocate(size, op->d_->h); - } - - friend - void asio_handler_deallocate( - void* p, std::size_t size, write_frame_op* op) - { - return boost_asio_handler_alloc_helpers:: - deallocate(p, size, op->d_->h); - } - - friend - bool asio_handler_is_continuation(write_frame_op* op) - { - return op->d_->cont; - } - - template - friend - void asio_handler_invoke(Function&& f, write_frame_op* op) - { - return boost_asio_handler_invoke_helpers:: - invoke(f, op->d_->h); - } -}; - -template -template -void -stream:: -write_frame_op:: -operator()(error_code ec, std::size_t) -{ - auto& d = *d_; - if(ec) - d.ws.failed_ = true; - (*this)(ec); -} - -template -template -void -stream:: -write_frame_op:: -operator()(error_code ec, bool again) -{ - using boost::asio::buffer_copy; - using boost::asio::mutable_buffers_1; - auto& d = *d_; - d.cont = d.cont || again; - if(ec) - goto upcall; - for(;;) - { - switch(d.state) - { - case 0: - if(d.ws.wr_block_) - { - // suspend - d.state = 3; - d.ws.wr_op_.template emplace< - write_frame_op>(std::move(*this)); - return; - } - if(d.ws.failed_ || d.ws.wr_close_) - { - // call handler - d.state = 99; - d.ws.get_io_service().post( - bind_handler(std::move(*this), - boost::asio::error::operation_aborted)); - return; - } - // fall through - - case 1: - { - if(! d.fh.mask) - { - // send header and entire payload - d.state = 99; - BOOST_ASSERT(! d.ws.wr_block_); - d.ws.wr_block_ = &d; - boost::asio::async_write(d.ws.stream_, - buffer_cat(d.fh_buf.data(), d.cb), - std::move(*this)); - return; - } - auto const n = - detail::clamp(d.remain, d.tmp_size); - mutable_buffers_1 mb{d.tmp, n}; - buffer_copy(mb, d.cb); - d.cb.consume(n); - d.remain -= n; - detail::mask_inplace(mb, d.key); - // send header and payload - d.state = d.remain > 0 ? 2 : 99; - BOOST_ASSERT(! d.ws.wr_block_); - d.ws.wr_block_ = &d; - boost::asio::async_write(d.ws.stream_, - buffer_cat(d.fh_buf.data(), - mb), std::move(*this)); - return; - } - - // sent masked payload - case 2: - { - auto const n = - detail::clamp(d.remain, d.tmp_size); - mutable_buffers_1 mb{d.tmp, - static_cast(n)}; - buffer_copy(mb, d.cb); - d.cb.consume(n); - d.remain -= n; - detail::mask_inplace(mb, d.key); - // send payload - if(d.remain == 0) - d.state = 99; - BOOST_ASSERT(d.ws.wr_block_ == &d); - boost::asio::async_write( - d.ws.stream_, mb, std::move(*this)); - return; - } - - case 3: - d.state = 4; - d.ws.get_io_service().post(bind_handler( - std::move(*this), ec)); - return; - - case 4: - if(d.ws.failed_ || d.ws.wr_close_) - { - // call handler - ec = boost::asio::error::operation_aborted; - goto upcall; - } - d.state = 1; - break; - - case 99: - goto upcall; - } - } -upcall: - if(d.tmp) - { - boost_asio_handler_alloc_helpers:: - deallocate(d.tmp, d.tmp_size, d.h); - d.tmp = nullptr; - } - if(d.ws.wr_block_ == &d) - d.ws.wr_block_ = nullptr; - d.ws.rd_op_.maybe_invoke(); - d.h(ec); -} - -} // websocket -} // beast - -#endif diff --git a/include/beast/websocket/impl/write_op.ipp b/include/beast/websocket/impl/write_op.ipp deleted file mode 100644 index 91811aa4..00000000 --- a/include/beast/websocket/impl/write_op.ipp +++ /dev/null @@ -1,139 +0,0 @@ -// -// Copyright (c) 2013-2016 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_WEBSOCKET_IMPL_WRITE_OP_HPP -#define BEAST_WEBSOCKET_IMPL_WRITE_OP_HPP - -#include -#include -#include -#include -#include -#include -#include - -namespace beast { -namespace websocket { - -// write a message -// -template -template -class stream::write_op -{ - using alloc_type = - handler_alloc; - - struct data : op - { - stream& ws; - consuming_buffers cb; - Handler h; - std::size_t remain; - bool cont; - int state = 0; - - template - data(DeducedHandler&& h_, - stream& ws_, Buffers const& bs) - : ws(ws_) - , cb(bs) - , h(std::forward(h_)) - , remain(boost::asio::buffer_size(cb)) - , cont(boost_asio_handler_cont_helpers:: - is_continuation(h)) - { - } - }; - - std::shared_ptr d_; - -public: - write_op(write_op&&) = default; - write_op(write_op const&) = default; - - template - explicit - write_op(DeducedHandler&& h, - stream& ws, Args&&... args) - : d_(std::allocate_shared(alloc_type{h}, - std::forward(h), ws, - std::forward(args)...)) - { - (*this)(error_code{}, false); - } - - void operator()(error_code ec, bool again = true); - - friend - void* asio_handler_allocate( - std::size_t size, write_op* op) - { - return boost_asio_handler_alloc_helpers:: - allocate(size, op->d_->h); - } - - friend - void asio_handler_deallocate( - void* p, std::size_t size, write_op* op) - { - return boost_asio_handler_alloc_helpers:: - deallocate(p, size, op->d_->h); - } - - friend - bool asio_handler_is_continuation(write_op* op) - { - return op->d_->cont; - } - - template - friend - void asio_handler_invoke(Function&& f, write_op* op) - { - return boost_asio_handler_invoke_helpers:: - invoke(f, op->d_->h); - } -}; - -template -template -void -stream:: -write_op:: -operator()(error_code ec, bool again) -{ - auto& d = *d_; - d.cont = d.cont || again; - if(! ec) - { - switch(d.state) - { - case 0: - { - auto const n = d.remain; - d.remain -= n; - auto const fin = d.remain <= 0; - if(fin) - d.state = 99; - auto const pb = prepare_buffers(n, d.cb); - d.cb.consume(n); - d.ws.async_write_frame(fin, pb, std::move(*this)); - return; - } - - case 99: - break; - } - } - d.h(ec); -} - -} // websocket -} // beast - -#endif diff --git a/include/beast/websocket/stream.hpp b/include/beast/websocket/stream.hpp index ee46911c..504e4b52 100644 --- a/include/beast/websocket/stream.hpp +++ b/include/beast/websocket/stream.hpp @@ -1552,6 +1552,12 @@ private: } // websocket } // beast +#include +#include +#include +#include +#include #include +#include #endif