From c0e5f147027ed49480f54bbdf9d65acfc11e1946 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sun, 3 Sep 2017 06:18:07 -0700 Subject: [PATCH] WebSocket writes return the bytes transferred (API Change): * Stream write operations now return the number of bytes transferred from the caller's input buffers. Actions Required: * Modify websocket write completion handlers to receive the extra std::size_t bytes_transferred parameter. --- CHANGELOG.md | 13 +++ .../server-flex/advanced_server_flex.cpp | 18 +++- example/advanced/server/advanced_server.cpp | 18 +++- .../async-ssl/websocket_client_async_ssl.cpp | 18 +++- .../client/async/websocket_client_async.cpp | 18 +++- .../async-ssl/websocket_server_async_ssl.cpp | 18 +++- .../server/async/websocket_server_async.cpp | 18 +++- .../server/fast/websocket_server_fast.cpp | 18 +++- .../websocket_server_stackless_ssl.cpp | 20 ++-- .../stackless/websocket_server_stackless.cpp | 16 +++- .../beast/websocket/detail/pmd_extension.hpp | 2 + include/boost/beast/websocket/impl/write.ipp | 91 ++++++++++++------- include/boost/beast/websocket/stream.hpp | 64 ++++++++----- test/beast/websocket/close.cpp | 16 ++-- test/beast/websocket/ping.cpp | 6 +- test/beast/websocket/read.cpp | 3 +- test/beast/websocket/test.hpp | 32 ++++--- test/beast/websocket/write.cpp | 25 +++-- test/doc/websocket_snippets.cpp | 2 +- 19 files changed, 287 insertions(+), 129 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22f1be72..8ff0cd03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +Version 112: + +API Changes: + +* WebSocket writes return the bytes transferred + +Actions Required: + +* Modify websocket write completion handlers to receive + the extra std::size_t bytes_transferred parameter. + +-------------------------------------------------------------------------------- + Version 111: WebSocket: diff --git a/example/advanced/server-flex/advanced_server_flex.cpp b/example/advanced/server-flex/advanced_server_flex.cpp index 9355ccd8..698ce54a 100644 --- a/example/advanced/server-flex/advanced_server_flex.cpp +++ b/example/advanced/server-flex/advanced_server_flex.cpp @@ -310,12 +310,17 @@ public: strand_.wrap(std::bind( &websocket_session::on_read, derived().shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + std::placeholders::_2))); } void - on_read(boost::system::error_code ec) + on_read( + boost::system::error_code ec, + std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + // Happens when the timer closes the socket if(ec == boost::asio::error::operation_aborted) return; @@ -334,12 +339,17 @@ public: strand_.wrap(std::bind( &websocket_session::on_write, derived().shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + std::placeholders::_2))); } void - on_write(boost::system::error_code ec) + on_write( + boost::system::error_code ec, + std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + // Happens when the timer closes the socket if(ec == boost::asio::error::operation_aborted) return; diff --git a/example/advanced/server/advanced_server.cpp b/example/advanced/server/advanced_server.cpp index a9f5bac7..fb1926a4 100644 --- a/example/advanced/server/advanced_server.cpp +++ b/example/advanced/server/advanced_server.cpp @@ -301,12 +301,17 @@ public: strand_.wrap(std::bind( &websocket_session::on_read, shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + std::placeholders::_2))); } void - on_read(boost::system::error_code ec) + on_read( + boost::system::error_code ec, + std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + // Happens when the timer closes the socket if(ec == boost::asio::error::operation_aborted) return; @@ -325,12 +330,17 @@ public: strand_.wrap(std::bind( &websocket_session::on_write, shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + std::placeholders::_2))); } void - on_write(boost::system::error_code ec) + on_write( + boost::system::error_code ec, + std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + // Happens when the timer closes the socket if(ec == boost::asio::error::operation_aborted) return; diff --git a/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp b/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp index f1823b5a..e1ee6238 100644 --- a/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp +++ b/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp @@ -137,12 +137,17 @@ public: std::bind( &session::on_write, shared_from_this(), - std::placeholders::_1)); + std::placeholders::_1, + std::placeholders::_2)); } void - on_write(boost::system::error_code ec) + on_write( + boost::system::error_code ec, + std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + if(ec) return fail(ec, "write"); @@ -152,12 +157,17 @@ public: std::bind( &session::on_read, shared_from_this(), - std::placeholders::_1)); + std::placeholders::_1, + std::placeholders::_2)); } void - on_read(boost::system::error_code ec) + on_read( + boost::system::error_code ec, + std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + if(ec) return fail(ec, "read"); diff --git a/example/websocket/client/async/websocket_client_async.cpp b/example/websocket/client/async/websocket_client_async.cpp index 66d53fbd..757a8fcd 100644 --- a/example/websocket/client/async/websocket_client_async.cpp +++ b/example/websocket/client/async/websocket_client_async.cpp @@ -117,12 +117,17 @@ public: std::bind( &session::on_write, shared_from_this(), - std::placeholders::_1)); + std::placeholders::_1, + std::placeholders::_2)); } void - on_write(boost::system::error_code ec) + on_write( + boost::system::error_code ec, + std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + if(ec) return fail(ec, "write"); @@ -132,12 +137,17 @@ public: std::bind( &session::on_read, shared_from_this(), - std::placeholders::_1)); + std::placeholders::_1, + std::placeholders::_2)); } void - on_read(boost::system::error_code ec) + on_read( + boost::system::error_code ec, + std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + if(ec) return fail(ec, "read"); diff --git a/example/websocket/server/async-ssl/websocket_server_async_ssl.cpp b/example/websocket/server/async-ssl/websocket_server_async_ssl.cpp index bb07ed4a..e4075979 100644 --- a/example/websocket/server/async-ssl/websocket_server_async_ssl.cpp +++ b/example/websocket/server/async-ssl/websocket_server_async_ssl.cpp @@ -106,12 +106,17 @@ public: strand_.wrap(std::bind( &session::on_read, shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + std::placeholders::_2))); } void - on_read(boost::system::error_code ec) + on_read( + boost::system::error_code ec, + std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + // This indicates that the session was closed if(ec == websocket::error::closed) return; @@ -126,12 +131,17 @@ public: strand_.wrap(std::bind( &session::on_write, shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + std::placeholders::_2))); } void - on_write(boost::system::error_code ec) + on_write( + boost::system::error_code ec, + std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + if(ec) return fail(ec, "write"); diff --git a/example/websocket/server/async/websocket_server_async.cpp b/example/websocket/server/async/websocket_server_async.cpp index 7e1bbbc1..716eea42 100644 --- a/example/websocket/server/async/websocket_server_async.cpp +++ b/example/websocket/server/async/websocket_server_async.cpp @@ -85,12 +85,17 @@ public: strand_.wrap(std::bind( &session::on_read, shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + std::placeholders::_2))); } void - on_read(boost::system::error_code ec) + on_read( + boost::system::error_code ec, + std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + // This indicates that the session was closed if(ec == websocket::error::closed) return; @@ -105,12 +110,17 @@ public: strand_.wrap(std::bind( &session::on_write, shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + std::placeholders::_2))); } void - on_write(boost::system::error_code ec) + on_write( + boost::system::error_code ec, + std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + if(ec) return fail(ec, "write"); diff --git a/example/websocket/server/fast/websocket_server_fast.cpp b/example/websocket/server/fast/websocket_server_fast.cpp index 0fb28a2f..d0570465 100644 --- a/example/websocket/server/fast/websocket_server_fast.cpp +++ b/example/websocket/server/fast/websocket_server_fast.cpp @@ -187,12 +187,17 @@ public: strand_.wrap(std::bind( &async_session::on_read, shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + std::placeholders::_2))); } void - on_read(boost::system::error_code ec) + on_read( + boost::system::error_code ec, + std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + // This indicates that the async_session was closed if(ec == websocket::error::closed) return; @@ -207,12 +212,17 @@ public: strand_.wrap(std::bind( &async_session::on_write, shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + std::placeholders::_2))); } void - on_write(boost::system::error_code ec) + on_write( + boost::system::error_code ec, + std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + if(ec) return fail(ec, "write"); diff --git a/example/websocket/server/stackless-ssl/websocket_server_stackless_ssl.cpp b/example/websocket/server/stackless-ssl/websocket_server_stackless_ssl.cpp index 21b4fcbd..165019c5 100644 --- a/example/websocket/server/stackless-ssl/websocket_server_stackless_ssl.cpp +++ b/example/websocket/server/stackless-ssl/websocket_server_stackless_ssl.cpp @@ -67,13 +67,17 @@ public: void run() { - loop(); + loop({}, 0); } #include void - loop(boost::system::error_code ec = {}) + loop( + boost::system::error_code ec, + std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + reenter(*this) { // Perform the SSL handshake @@ -82,7 +86,8 @@ public: strand_.wrap(std::bind( &session::loop, shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + 0))); if(ec) return fail(ec, "handshake"); @@ -91,7 +96,8 @@ public: strand_.wrap(std::bind( &session::loop, shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + 0))); if(ec) return fail(ec, "accept"); @@ -103,7 +109,8 @@ public: strand_.wrap(std::bind( &session::loop, shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + std::placeholders::_2))); if(ec == websocket::error::closed) { // This indicates that the session was closed @@ -119,7 +126,8 @@ public: strand_.wrap(std::bind( &session::loop, shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + std::placeholders::_2))); if(ec) return fail(ec, "write"); diff --git a/example/websocket/server/stackless/websocket_server_stackless.cpp b/example/websocket/server/stackless/websocket_server_stackless.cpp index eeaf5414..79149387 100644 --- a/example/websocket/server/stackless/websocket_server_stackless.cpp +++ b/example/websocket/server/stackless/websocket_server_stackless.cpp @@ -61,13 +61,16 @@ public: void run() { - loop(); + loop({}, 0); } #include void - loop(boost::system::error_code ec = {}) + loop( + boost::system::error_code ec, + std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); reenter(*this) { // Accept the websocket handshake @@ -75,7 +78,8 @@ public: strand_.wrap(std::bind( &session::loop, shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + 0))); if(ec) return fail(ec, "accept"); @@ -87,7 +91,8 @@ public: strand_.wrap(std::bind( &session::loop, shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + std::placeholders::_2))); if(ec == websocket::error::closed) { // This indicates that the session was closed @@ -103,7 +108,8 @@ public: strand_.wrap(std::bind( &session::loop, shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + std::placeholders::_2))); if(ec) return fail(ec, "write"); diff --git a/include/boost/beast/websocket/detail/pmd_extension.hpp b/include/boost/beast/websocket/detail/pmd_extension.hpp index cb540d1f..129987a1 100644 --- a/include/boost/beast/websocket/detail/pmd_extension.hpp +++ b/include/boost/beast/websocket/detail/pmd_extension.hpp @@ -367,6 +367,7 @@ deflate( boost::asio::mutable_buffer& out, consuming_buffers& cb, bool fin, + std::size_t& total_in, error_code& ec) { using boost::asio::buffer; @@ -401,6 +402,7 @@ deflate( } BOOST_ASSERT(zs.avail_in == 0); } + total_in = zs.total_in; cb.consume(zs.total_in); if(zs.avail_out > 0 && fin) { diff --git a/include/boost/beast/websocket/impl/write.ipp b/include/boost/beast/websocket/impl/write.ipp index 86ff0075..aa0cc4de 100644 --- a/include/boost/beast/websocket/impl/write.ipp +++ b/include/boost/beast/websocket/impl/write.ipp @@ -44,7 +44,9 @@ class stream::write_some_op consuming_buffers cb_; detail::frame_header fh_; detail::prepared_key key_; + std::size_t bytes_transferred_ = 0; std::size_t remain_; + std::size_t in_; token tok_; int how_; bool fin_; @@ -248,6 +250,7 @@ operator()( std::move(*this)); if(! ws_.check_ok(ec)) goto upcall; + bytes_transferred_ += clamp(fh_.len); goto upcall; } @@ -257,8 +260,9 @@ operator()( { for(;;) { - fh_.len = clamp(remain_, ws_.wr_buf_size_); - remain_ -= clamp(fh_.len); + n = clamp(remain_, ws_.wr_buf_size_); + fh_.len = n; + remain_ -= n; fh_.fin = fin_ ? remain_ == 0 : false; ws_.wr_fb_.reset(); detail::write( @@ -273,10 +277,11 @@ operator()( std::move(*this)); if(! ws_.check_ok(ec)) goto upcall; + n = clamp(fh_.len); // because yield + bytes_transferred_ += n; if(remain_ == 0) break; - cb_.consume( - bytes_transferred - ws_.wr_fb_.size()); + cb_.consume(n); fh_.op = detail::opcode::cont; // Allow outgoing control frames to // be sent in between message frames @@ -320,6 +325,8 @@ operator()( std::move(*this)); if(! ws_.check_ok(ec)) goto upcall; + bytes_transferred_ += + bytes_transferred - ws_.wr_fb_.size(); while(remain_ > 0) { cb_.consume(ws_.wr_buf_size_); @@ -336,6 +343,7 @@ operator()( std::move(*this)); if(! ws_.check_ok(ec)) goto upcall; + bytes_transferred_ += bytes_transferred; } goto upcall; } @@ -368,10 +376,11 @@ operator()( std::move(*this)); if(! ws_.check_ok(ec)) goto upcall; + n = bytes_transferred - ws_.wr_fb_.size(); + bytes_transferred_ += n; if(remain_ == 0) break; - cb_.consume( - bytes_transferred - ws_.wr_fb_.size()); + cb_.consume(n); fh_.op = detail::opcode::cont; // Allow outgoing control frames to // be sent in between message frames: @@ -396,8 +405,8 @@ operator()( { b = buffer(ws_.wr_buf_.get(), ws_.wr_buf_size_); - more_ = detail::deflate( - ws_.pmd_->zo, b, cb_, fin_, ec); + more_ = detail::deflate(ws_.pmd_->zo, + b, cb_, fin_, in_, ec); if(! ws_.check_ok(ec)) goto upcall; n = buffer_size(b); @@ -430,6 +439,7 @@ operator()( mutable_buffers_1{b}), std::move(*this)); if(! ws_.check_ok(ec)) goto upcall; + bytes_transferred_ += in_; if(more_) { fh_.op = detail::opcode::cont; @@ -469,8 +479,8 @@ operator()( ws_.paused_ping_.maybe_invoke(); if(! cont_) return ws_.stream_.get_io_service().post( - bind_handler(h_, ec)); - h_(ec); + bind_handler(h_, ec, bytes_transferred_)); + h_(ec, bytes_transferred_); } } @@ -478,7 +488,7 @@ operator()( template template -void +std::size_t stream:: write_some(bool fin, ConstBufferSequence const& buffers) { @@ -488,14 +498,16 @@ write_some(bool fin, ConstBufferSequence const& buffers) ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); error_code ec; - write_some(fin, buffers, ec); + auto const bytes_transferred = + write_some(fin, buffers, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); + return bytes_transferred; } template template -void +std::size_t stream:: write_some(bool fin, ConstBufferSequence const& buffers, error_code& ec) @@ -509,10 +521,11 @@ write_some(bool fin, using boost::asio::buffer; using boost::asio::buffer_copy; using boost::asio::buffer_size; + std::size_t bytes_transferred = 0; ec.assign(0, ec.category()); // Make sure the stream is open if(! check_open(ec)) - return; + return bytes_transferred; detail::frame_header fh; if(! wr_cont_) { @@ -538,9 +551,10 @@ write_some(bool fin, auto b = buffer( wr_buf_.get(), wr_buf_size_); auto const more = detail::deflate( - pmd_->zo, b, cb, fin, ec); + pmd_->zo, b, cb, fin, + bytes_transferred, ec); if(! check_ok(ec)) - return; + return bytes_transferred; auto const n = buffer_size(b); if(n == 0) { @@ -568,7 +582,7 @@ write_some(bool fin, boost::asio::write(stream_, buffer_cat(fh_buf.data(), b), ec); if(! check_ok(ec)) - return; + return bytes_transferred; if(! more) break; fh.op = detail::opcode::cont; @@ -595,7 +609,8 @@ write_some(bool fin, boost::asio::write(stream_, buffer_cat(fh_buf.data(), buffers), ec); if(! check_ok(ec)) - return; + return bytes_transferred; + bytes_transferred += remain; } else { @@ -617,7 +632,8 @@ write_some(bool fin, buffer_cat(fh_buf.data(), buffer_prefix(n, cb)), ec); if(! check_ok(ec)) - return; + return bytes_transferred; + bytes_transferred += n; if(remain == 0) break; fh.op = detail::opcode::cont; @@ -649,7 +665,8 @@ write_some(bool fin, boost::asio::write(stream_, buffer_cat(fh_buf.data(), b), ec); if(! check_ok(ec)) - return; + return bytes_transferred; + bytes_transferred += n; } while(remain > 0) { @@ -661,7 +678,8 @@ write_some(bool fin, detail::mask_inplace(b, key); boost::asio::write(stream_, b, ec); if(! check_ok(ec)) - return; + return bytes_transferred; + bytes_transferred += n; } } else @@ -689,19 +707,21 @@ write_some(bool fin, boost::asio::write(stream_, buffer_cat(fh_buf.data(), b), ec); if(! check_ok(ec)) - return; + return bytes_transferred; + bytes_transferred += n; if(remain == 0) break; fh.op = detail::opcode::cont; cb.consume(n); } } + return bytes_transferred; } template template async_return_type< - WriteHandler, void(error_code)> + WriteHandler, void(error_code, std::size_t)> stream:: async_write_some(bool fin, ConstBufferSequence const& bs, WriteHandler&& handler) @@ -712,10 +732,11 @@ async_write_some(bool fin, ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); async_completion init{handler}; + void(error_code, std::size_t)> init{handler}; write_some_op>{init.completion_handler, - *this, fin, bs}({}, 0, false); + WriteHandler, void(error_code, std::size_t)>>{ + init.completion_handler, *this, fin, bs}( + {}, 0, false); return init.result.get(); } @@ -723,7 +744,7 @@ async_write_some(bool fin, template template -void +std::size_t stream:: write(ConstBufferSequence const& buffers) { @@ -733,14 +754,15 @@ write(ConstBufferSequence const& buffers) ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); error_code ec; - write(buffers, ec); + auto const bytes_transferred = write(buffers, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); + return bytes_transferred; } template template -void +std::size_t stream:: write(ConstBufferSequence const& buffers, error_code& ec) { @@ -749,13 +771,13 @@ write(ConstBufferSequence const& buffers, error_code& ec) static_assert(beast::is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); - write_some(true, buffers, ec); + return write_some(true, buffers, ec); } template template async_return_type< - WriteHandler, void(error_code)> + WriteHandler, void(error_code, std::size_t)> stream:: async_write( ConstBufferSequence const& bs, WriteHandler&& handler) @@ -766,10 +788,11 @@ async_write( ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); async_completion init{handler}; + void(error_code, std::size_t)> init{handler}; write_some_op>{init.completion_handler, - *this, true, bs}({}, 0, false); + WriteHandler, void(error_code, std::size_t)>>{ + init.completion_handler, *this, true, bs}( + {}, 0, false); return init.result.get(); } diff --git a/include/boost/beast/websocket/stream.hpp b/include/boost/beast/websocket/stream.hpp index cecd7f54..41f274a8 100644 --- a/include/boost/beast/websocket/stream.hpp +++ b/include/boost/beast/websocket/stream.hpp @@ -3157,13 +3157,17 @@ public: the memory locations pointed to by buffers remains valid until the completion handler is called. + @return The number of bytes written from the buffers. + If an error occurred, this will be less than the sum + of the buffer sizes. + @throws system_error Thrown on failure. @note This function always sends an entire message. To send a message in fragments, use @ref write_some. */ template - void + std::size_t write(ConstBufferSequence const& buffers); /** Write a message to the stream. @@ -3192,6 +3196,10 @@ public: the memory locations pointed to by buffers remains valid until the completion handler is called. + @return The number of bytes written from the buffers. + If an error occurred, this will be less than the sum + of the buffer sizes. + @param ec Set to indicate what error occurred, if any. @throws system_error Thrown on failure. @@ -3200,7 +3208,7 @@ public: send a message in fragments, use @ref write_some. */ template - void + std::size_t write(ConstBufferSequence const& buffers, error_code& ec); /** Start an asynchronous operation to write a message to the stream. @@ -3239,7 +3247,11 @@ public: function signature of the handler must be: @code void handler( - error_code const& ec // Result of operation + error_code const& ec, // Result of operation + std::size_t bytes_transferred // Number of bytes written from the + // buffers. If an error occurred, + // this will be less than the sum + // of the buffer sizes. ); @endcode Regardless of whether the asynchronous operation completes @@ -3247,14 +3259,18 @@ public: this function. Invocation of the handler will be performed in a manner equivalent to using `boost::asio::io_service::post`. */ - template + template< + class ConstBufferSequence, + class WriteHandler> #if BOOST_BEAST_DOXYGEN void_or_deduced #else async_return_type< - WriteHandler, void(error_code)> + WriteHandler, + void(error_code, std::size_t)> #endif - async_write(ConstBufferSequence const& buffers, + async_write( + ConstBufferSequence const& buffers, WriteHandler&& handler); /** Write partial message data on the stream. @@ -3277,16 +3293,18 @@ public: the @ref binary option. The actual payload sent may be transformed as per the WebSocket protocol settings. - @param fin `true` if this is the last frame in the message. + @param fin `true` if this is the last part of the message. @param buffers The input buffer sequence holding the data to write. - @return The number of bytes consumed in the input buffers. + @return The number of bytes written from the buffers. + If an error occurred, this will be less than the sum + of the buffer sizes. @throws system_error Thrown on failure. */ template - void + std::size_t write_some(bool fin, ConstBufferSequence const& buffers); /** Write partial message data on the stream. @@ -3309,16 +3327,18 @@ public: the @ref binary option. The actual payload sent may be transformed as per the WebSocket protocol settings. - @param fin `true` if this is the last frame in the message. + @param fin `true` if this is the last part of the message. - @param buffers The input buffer sequence holding the data to write. + @return The number of bytes written from the buffers. + If an error occurred, this will be less than the sum + of the buffer sizes. @param ec Set to indicate what error occurred, if any. @return The number of bytes consumed in the input buffers. */ template - void + std::size_t write_some(bool fin, ConstBufferSequence const& buffers, error_code& ec); @@ -3346,8 +3366,7 @@ public: the @ref binary option. The actual payload sent may be transformed as per the WebSocket protocol settings. - @param fin A bool indicating whether or not the frame is the - last frame in the corresponding WebSockets message. + @param fin `true` if this is the last part of the message. @param buffers A object meeting the requirements of ConstBufferSequence which holds the payload data before any @@ -3360,27 +3379,24 @@ public: Copies will be made of the handler as required. The equivalent function signature of the handler must be: @code void handler( - error_code const& ec // Result of operation + error_code const& ec, // Result of operation + std::size_t bytes_transferred // Number of bytes written from the + // buffers. If an error occurred, + // this will be less than the sum + // of the buffer sizes. ); @endcode */ template #if BOOST_BEAST_DOXYGEN void_or_deduced #else - async_return_type< - WriteHandler, void(error_code)> + async_return_type #endif async_write_some(bool fin, ConstBufferSequence const& buffers, WriteHandler&& handler); private: - enum class fail_how - { - code = 1, // send close code, teardown, finish with error::failed - close = 2, // send frame in fb, teardown, finish with error::closed - teardown = 3 // teardown, finish with error::failed - }; - template class accept_op; template class close_op; template class fail_op; diff --git a/test/beast/websocket/close.cpp b/test/beast/websocket/close.cpp index a0af5eaa..e51ff4cd 100644 --- a/test/beast/websocket/close.cpp +++ b/test/beast/websocket/close.cpp @@ -212,12 +212,13 @@ public: ws.handshake("localhost", "/"); std::size_t count = 0; ws.async_write(sbuf("*"), - [&](error_code ec) + [&](error_code ec, std::size_t n) { ++count; if(ec) BOOST_THROW_EXCEPTION( system_error{ec}); + BEAST_EXPECT(n == 1); }); BEAST_EXPECT(ws.wr_block_); BEAST_EXPECT(count == 0); @@ -371,11 +372,12 @@ public: std::size_t count = 0; std::string const s = "Hello, world!"; ws.async_write(buffer(s), - [&](error_code ec) + [&](error_code ec, std::size_t n) { if(ec) BOOST_THROW_EXCEPTION( system_error{ec}); + BEAST_EXPECT(n == s.size()); BEAST_EXPECT(++count == 1); }); multi_buffer b; @@ -415,11 +417,12 @@ public: multi_buffer b; std::string const s = "Hello, world!"; ws.async_write(buffer(s), - [&](error_code ec) + [&](error_code ec, std::size_t n) { if(ec) BOOST_THROW_EXCEPTION( system_error{ec}); + BEAST_EXPECT(n == s.size()); BEAST_EXPECT(++count == 1); }); ws.async_read(b, @@ -474,7 +477,7 @@ public: ++count; }); ws.async_write(buffer(s), - [&](error_code ec) + [&](error_code ec, std::size_t) { if(ec != boost::asio::error::operation_aborted) BOOST_THROW_EXCEPTION( @@ -519,11 +522,12 @@ public: ++count; }); ws.async_write(buffer(s), - [&](error_code ec) + [&](error_code ec, std::size_t n) { if(ec) BOOST_THROW_EXCEPTION( system_error{ec}); + BEAST_EXPECT(n == s.size()); BEAST_EXPECT(++count == 1); }); ws.async_ping({}, @@ -578,7 +582,7 @@ public: ++count; }); ws.async_write(buffer(s), - [&](error_code ec) + [&](error_code ec, std::size_t) { if(ec != boost::asio::error::operation_aborted) BOOST_THROW_EXCEPTION( diff --git a/test/beast/websocket/ping.cpp b/test/beast/websocket/ping.cpp index f7b0fc9c..855a5996 100644 --- a/test/beast/websocket/ping.cpp +++ b/test/beast/websocket/ping.cpp @@ -104,12 +104,13 @@ public: ws.handshake("localhost", "/"); std::size_t count = 0; ws.async_write(sbuf("Hello, world"), - [&](error_code ec) + [&](error_code ec, std::size_t n) { ++count; if(ec) BOOST_THROW_EXCEPTION( system_error{ec}); + BEAST_EXPECT(n == 12); }); BEAST_EXPECT(ws.wr_block_); BEAST_EXPECT(count == 0); @@ -333,12 +334,13 @@ public: ws.handshake("localhost", "/"); std::size_t count = 0; ws.async_write(sbuf("*"), - [&](error_code ec) + [&](error_code ec, std::size_t n) { ++count; if(ec) BOOST_THROW_EXCEPTION( system_error{ec}); + BEAST_EXPECT(n == 1); }); BEAST_EXPECT(ws.wr_block_); ws.async_ping("", diff --git a/test/beast/websocket/read.cpp b/test/beast/websocket/read.cpp index 8b9fa517..96f3121e 100644 --- a/test/beast/websocket/read.cpp +++ b/test/beast/websocket/read.cpp @@ -744,11 +744,12 @@ public: }); BEAST_EXPECT(ws.rd_block_); ws.async_write(buffer(s), - [&](error_code ec) + [&](error_code ec, std::size_t n) { if(ec) BOOST_THROW_EXCEPTION( system_error{ec}); + BEAST_EXPECT(n == s.size()); ++count; }); BEAST_EXPECT(ws.wr_block_); diff --git a/test/beast/websocket/test.hpp b/test/beast/websocket/test.hpp index 9e258fdd..fb9f0794 100644 --- a/test/beast/websocket/test.hpp +++ b/test/beast/websocket/test.hpp @@ -636,29 +636,29 @@ public: template< class NextLayer, class ConstBufferSequence> - void + std::size_t write(stream& ws, ConstBufferSequence const& buffers) const { - ws.write(buffers); + return ws.write(buffers); } template< class NextLayer, class ConstBufferSequence> - void + std::size_t write_some(stream& ws, bool fin, ConstBufferSequence const& buffers) const { - ws.write_some(fin, buffers); + return ws.write_some(fin, buffers); } template< class NextLayer, class ConstBufferSequence> - void + std::size_t write_raw(stream& ws, ConstBufferSequence const& buffers) const { - boost::asio::write( + return boost::asio::write( ws.next_layer(), buffers); } }; @@ -896,39 +896,45 @@ public: template< class NextLayer, class ConstBufferSequence> - void + std::size_t write(stream& ws, ConstBufferSequence const& buffers) const { error_code ec; - ws.async_write(buffers, yield_[ec]); + auto const bytes_transferred = + ws.async_write(buffers, yield_[ec]); if(ec) throw system_error{ec}; + return bytes_transferred; } template< class NextLayer, class ConstBufferSequence> - void + std::size_t write_some(stream& ws, bool fin, ConstBufferSequence const& buffers) const { error_code ec; - ws.async_write_some(fin, buffers, yield_[ec]); + auto const bytes_transferred = + ws.async_write_some(fin, buffers, yield_[ec]); if(ec) throw system_error{ec}; + return bytes_transferred; } template< class NextLayer, class ConstBufferSequence> - void + std::size_t write_raw(stream& ws, ConstBufferSequence const& buffers) const { error_code ec; - boost::asio::async_write( - ws.next_layer(), buffers, yield_[ec]); + auto const bytes_transferred = + boost::asio::async_write( + ws.next_layer(), buffers, yield_[ec]); if(ec) throw system_error{ec}; + return bytes_transferred; } }; }; diff --git a/test/beast/websocket/write.cpp b/test/beast/websocket/write.cpp index bd22d41c..d45f9f84 100644 --- a/test/beast/websocket/write.cpp +++ b/test/beast/websocket/write.cpp @@ -271,12 +271,13 @@ public: BEAST_EXPECT(ws.wr_block_); BEAST_EXPECT(count == 0); ws.async_write(sbuf("*"), - [&](error_code ec) + [&](error_code ec, std::size_t n) { ++count; if(ec) BOOST_THROW_EXCEPTION( system_error{ec}); + BEAST_EXPECT(n == 1); }); BEAST_EXPECT(count == 0); ios.run(); @@ -303,7 +304,7 @@ public: BEAST_EXPECT(ws.wr_block_); BEAST_EXPECT(count == 0); ws.async_write(sbuf("*"), - [&](error_code ec) + [&](error_code ec, std::size_t) { ++count; if(ec != boost::asio::error::operation_aborted) @@ -344,12 +345,13 @@ public: } BEAST_EXPECT(count == 0); ws.async_write(sbuf("*"), - [&](error_code ec) + [&](error_code ec, std::size_t n) { ++count; if(ec) BOOST_THROW_EXCEPTION( system_error{ec}); + BEAST_EXPECT(n == 1); }); BEAST_EXPECT(count == 0); ios.run(); @@ -369,12 +371,13 @@ public: std::string const s(16384, '*'); ws.auto_fragment(false); ws.async_write(buffer(s), - [&](error_code ec) + [&](error_code ec, std::size_t n) { ++count; if(ec) BOOST_THROW_EXCEPTION( system_error{ec}); + BEAST_EXPECT(n == 16384); }); BEAST_EXPECT(ws.wr_block_); ws.async_ping("", @@ -402,12 +405,13 @@ public: std::string const s(16384, '*'); ws.auto_fragment(true); ws.async_write(buffer(s), - [&](error_code ec) + [&](error_code ec, std::size_t n) { ++count; if(ec) BOOST_THROW_EXCEPTION( system_error{ec}); + BEAST_EXPECT(n == 16384); }); BEAST_EXPECT(ws.wr_block_); ws.async_ping("", @@ -435,12 +439,13 @@ public: std::string const s(16384, '*'); ws.auto_fragment(false); ws.async_write(buffer(s), - [&](error_code ec) + [&](error_code ec, std::size_t n) { ++count; if(ec) BOOST_THROW_EXCEPTION( system_error{ec}); + BEAST_EXPECT(n == 16384); }); BEAST_EXPECT(ws.wr_block_); ws.async_ping("", @@ -467,12 +472,13 @@ public: std::string const s(16384, '*'); ws.auto_fragment(true); ws.async_write(buffer(s), - [&](error_code ec) + [&](error_code ec, std::size_t n) { ++count; if(ec) BOOST_THROW_EXCEPTION( system_error{ec}); + BEAST_EXPECT(n == 16384); }); BEAST_EXPECT(ws.wr_block_); ws.async_ping("", @@ -504,12 +510,13 @@ public: auto const& s = random_string(); ws.binary(true); ws.async_write(buffer(s), - [&](error_code ec) + [&](error_code ec, std::size_t n) { ++count; if(ec) BOOST_THROW_EXCEPTION( system_error{ec}); + BEAST_EXPECT(n == s.size()); }); BEAST_EXPECT(ws.wr_block_); ws.async_ping("", @@ -543,7 +550,7 @@ public: break; ws.async_write_some(false, boost::asio::null_buffers{}, - [&](error_code) + [&](error_code, std::size_t) { fail(); }); diff --git a/test/doc/websocket_snippets.cpp b/test/doc/websocket_snippets.cpp index d2779eb8..5bac49af 100644 --- a/test/doc/websocket_snippets.cpp +++ b/test/doc/websocket_snippets.cpp @@ -226,7 +226,7 @@ void echo(stream& ws, multi_buffer& buffer, boost::asio::yield_context yield) { ws.async_read(buffer, yield); - std::future fut = + std::future fut = ws.async_write(buffer.data(), boost::asio::use_future); } //]