diff --git a/CHANGELOG.md b/CHANGELOG.md index bb492849..455a8dfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ API Changes: * Rename mask_buffer_size to write_buffer_size +* Make auto_fragment a boolean option -------------------------------------------------------------------------------- diff --git a/TODO.txt b/TODO.txt index c5e32764..13de4b34 100644 --- a/TODO.txt +++ b/TODO.txt @@ -18,6 +18,7 @@ Core: * Complete allocator testing in basic_streambuf WebSocket: +* Minimize sizeof(websocket::stream) * Move check for message size limit to account for compression * more invokable unit test coverage * More control over the HTTP request and response during handshakes diff --git a/doc/quickref.xml b/doc/quickref.xml index ebbb51be..321a34f3 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -91,7 +91,7 @@ Options - auto_fragment_size + auto_fragment decorate keep_alive message_type diff --git a/doc/websocket.qbk b/doc/websocket.qbk index 057c919b..123ef9fc 100644 --- a/doc/websocket.qbk +++ b/doc/websocket.qbk @@ -302,13 +302,14 @@ in the error code [link beast.ref.websocket__error `error::closed`] being delivered to the caller in a subsequent read operation, assuming no other error takes place. -To ensure timely delivery of control frames, large messages are broken up -into smaller sized frames. The implementation chooses the size and number -of the frames making up the message. The automatic fragment size option -gives callers control over the size of these frames: +To ensure timely delivery of control frames, large messages can be broken up +into smaller sized frames. The automatic fragment option turns on this +feature, and the write buffer size option determines the maximum size of +the fragments: ``` ... - ws.set_option(beast::websocket::auto_fragment_size{8192}); + ws.set_option(beast::websocket::auto_fragment{true}; + ws.set_option(beast::websocket::write_buffer_size{16384}; ``` The WebSocket protocol defines a procedure and control message for initiating diff --git a/include/beast/websocket/detail/mask.hpp b/include/beast/websocket/detail/mask.hpp index fb67b511..12254ea7 100644 --- a/include/beast/websocket/detail/mask.hpp +++ b/include/beast/websocket/detail/mask.hpp @@ -67,7 +67,9 @@ maskgen_t<_>::rekey() g_.seed(ss); } -using maskgen = maskgen_t; +// VFALCO NOTE This generator has 5KB of state! +//using maskgen = maskgen_t; +using maskgen = maskgen_t; //------------------------------------------------------------------------------ diff --git a/include/beast/websocket/detail/stream_base.hpp b/include/beast/websocket/detail/stream_base.hpp index 85431f4d..00c88eee 100644 --- a/include/beast/websocket/detail/stream_base.hpp +++ b/include/beast/websocket/detail/stream_base.hpp @@ -70,35 +70,58 @@ protected: struct op {}; - detail::maskgen maskgen_; // source of mask keys - decorator_type d_; // adorns http messages - bool keep_alive_ = false; // close on failed upgrade + detail::maskgen maskgen_; // source of mask keys + decorator_type d_; // adorns http messages + bool keep_alive_ = false; // close on failed upgrade std::size_t rd_msg_max_ = - 16 * 1024 * 1024; // max message size - std::size_t - wr_frag_size_ = 16 * 1024; // size of auto-fragments - std::size_t wr_buf_size_ = 4096; // mask buffer size - opcode wr_opcode_ = opcode::text; // outgoing message type - pong_cb pong_cb_; // pong callback - role_type role_; // server or client - bool failed_; // the connection failed + 16 * 1024 * 1024; // max message size + bool wr_autofrag_ = true; // auto fragment + std::size_t wr_buf_size_ = 4096; // mask buffer size + opcode wr_opcode_ = opcode::text; // outgoing message type + pong_cb pong_cb_; // pong callback + role_type role_; // server or client + bool failed_; // the connection failed - detail::frame_header rd_fh_; // current frame header - detail::prepared_key_type rd_key_; // prepared masking key - detail::utf8_checker rd_utf8_check_;// for current text msg - std::uint64_t rd_size_; // size of the current message so far - std::uint64_t rd_need_ = 0; // bytes left in msg frame payload - opcode rd_opcode_; // opcode of current msg - bool rd_cont_; // expecting a continuation frame + detail::frame_header rd_fh_; // current frame header + detail::prepared_key_type rd_key_; // prepared masking key + detail::utf8_checker rd_utf8_check_; // for current text msg + std::uint64_t rd_size_; // size of the current message so far + std::uint64_t rd_need_ = 0; // bytes left in msg frame payload + opcode rd_opcode_; // opcode of current msg + bool rd_cont_; // expecting a continuation frame - bool wr_close_; // sent close frame - bool wr_cont_; // next write is continuation frame - op* wr_block_; // op currenly writing + bool wr_close_; // sent close frame + op* wr_block_; // op currenly writing - ping_data* pong_data_; // where to put pong payload - invokable rd_op_; // invoked after write completes - invokable wr_op_; // invoked after read completes - close_reason cr_; // set from received close frame + ping_data* pong_data_; // where to put pong payload + invokable rd_op_; // invoked after write completes + invokable wr_op_; // invoked after read completes + close_reason cr_; // set from received close frame + + struct wr_t + { + bool cont; // next frame is continuation frame + bool autofrag; // if this message is auto fragmented + bool compress; // if this message is compressed + std::size_t size; // amount stored in buffer + std::size_t max; // size of write buffer + std::unique_ptr buf;// write buffer storage + + void + open() + { + cont = false; + size = 0; + } + + void + close() + { + buf.reset(); + } + }; + + wr_t wr_; stream_base(stream_base&&) = default; stream_base(stream_base const&) = delete; @@ -114,6 +137,10 @@ protected: void open(role_type role); + template + void + close(); + template std::size_t read_fh1(DynamicBuffer& db, close_code::value& code); @@ -122,6 +149,10 @@ protected: void read_fh2(DynamicBuffer& db, close_code::value& code); + template + void + wr_prepare(bool compress); + template void write_close(DynamicBuffer& db, close_reason const& rc); @@ -133,7 +164,8 @@ protected: template void -stream_base::open(role_type role) +stream_base:: +open(role_type role) { // VFALCO TODO analyze and remove dupe code in reset() role_ = role; @@ -141,9 +173,18 @@ stream_base::open(role_type role) rd_need_ = 0; rd_cont_ = false; wr_close_ = false; - wr_cont_ = false; wr_block_ = nullptr; // should be nullptr on close anyway pong_data_ = nullptr; // should be nullptr on close anyway + + wr_.open(); +} + +template +void +stream_base:: +close() +{ + wr_.close(); } // Read fixed frame header @@ -335,10 +376,34 @@ read_fh2(DynamicBuffer& db, close_code::value& code) code = close_code::none; } +template +void +stream_base:: +wr_prepare(bool compress) +{ + wr_.autofrag = wr_autofrag_; + wr_.compress = compress; + wr_.size = 0; + if(compress || wr_.autofrag || + role_ == detail::role_type::client) + { + if(! wr_.buf || wr_.max != wr_buf_size_) + { + wr_.max = wr_buf_size_; + wr_.buf.reset(new std::uint8_t[wr_.max]); + } + } + else + { + wr_.max = wr_buf_size_; + wr_.buf.reset(); + } +} + template void -stream_base::write_close( - DynamicBuffer& db, close_reason const& cr) +stream_base:: +write_close(DynamicBuffer& db, close_reason const& cr) { using namespace boost::endian; frame_header fh; @@ -384,8 +449,9 @@ stream_base::write_close( template void -stream_base::write_ping(DynamicBuffer& db, - opcode op, ping_data const& data) +stream_base:: +write_ping( + DynamicBuffer& db, opcode op, ping_data const& data) { frame_header fh; fh.op = op; diff --git a/include/beast/websocket/impl/stream.ipp b/include/beast/websocket/impl/stream.ipp index b717ea88..99bb02b9 100644 --- a/include/beast/websocket/impl/stream.ipp +++ b/include/beast/websocket/impl/stream.ipp @@ -596,29 +596,14 @@ template template void stream:: -write(ConstBufferSequence const& bs, error_code& ec) +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"); - using boost::asio::buffer_size; - consuming_buffers cb(bs); - auto remain = buffer_size(cb); - for(;;) - { - auto const n = - detail::clamp(remain, wr_frag_size_); - remain -= n; - auto const fin = remain <= 0; - write_frame(fin, prepare_buffers(n, cb), ec); - cb.consume(n); - if(ec) - return; - if(fin) - break; - } + write_frame(true, buffers, ec); } template @@ -657,82 +642,158 @@ write_frame(bool fin, ConstBufferSequence const& buffers) 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& bs, error_code& ec) +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; - using boost::asio::mutable_buffers_1; + bool const compress = false; + if(! wr_.cont) + wr_prepare(compress); detail::frame_header fh; - fh.op = wr_cont_ ? opcode::cont : wr_opcode_; - wr_cont_ = ! fin; - fh.fin = fin; + fh.op = wr_.cont ? opcode::cont : wr_opcode_; fh.rsv1 = false; fh.rsv2 = false; fh.rsv3 = false; - fh.len = buffer_size(bs); fh.mask = role_ == detail::role_type::client; - if(fh.mask) - fh.key = maskgen_(); - detail::fh_streambuf fh_buf; - detail::write(fh_buf, fh); - if(! fh.mask) + auto remain = buffer_size(buffers); + if(compress) { - // send header and payload - boost::asio::write(stream_, - buffer_cat(fh_buf.data(), bs), ec); - failed_ = ec != 0; + // 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; } - detail::prepared_key_type key; - detail::prepare_key(key, fh.key); - auto const tmp_size = - detail::clamp(fh.len, wr_buf_size_); - std::unique_ptr up( - new std::uint8_t[tmp_size]); - std::uint64_t remain = fh.len; - consuming_buffers cb(bs); + else if(fh.mask) { - auto const n = - detail::clamp(remain, tmp_size); - mutable_buffers_1 mb{up.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); - if(ec) + 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; - return; + 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; } - while(remain > 0) { - auto const n = - detail::clamp(remain, tmp_size); - mutable_buffers_1 mb{up.get(), n}; - buffer_copy(mb, cb); - cb.consume(n); - remain -= n; - detail::mask_inplace(mb, key); - // send payload - boost::asio::write(stream_, mb, ec); - if(ec) - { - failed_ = ec != 0; - 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; } } @@ -769,7 +830,7 @@ reset() rd_need_ = 0; rd_cont_ = false; wr_close_ = false; - wr_cont_ = false; + wr_.cont = false; wr_block_ = nullptr; // should be nullptr on close anyway pong_data_ = nullptr; // should be nullptr on close anyway diff --git a/include/beast/websocket/impl/write_frame_op.ipp b/include/beast/websocket/impl/write_frame_op.ipp index c9991eda..5bfee8af 100644 --- a/include/beast/websocket/impl/write_frame_op.ipp +++ b/include/beast/websocket/impl/write_frame_op.ipp @@ -53,9 +53,9 @@ class stream::write_frame_op , cont(boost_asio_handler_cont_helpers:: is_continuation(h)) { - fh.op = ws.wr_cont_ ? + fh.op = ws.wr_.cont ? opcode::cont : ws.wr_opcode_; - ws.wr_cont_ = ! fin; + ws.wr_.cont = ! fin; fh.fin = fin; fh.rsv1 = false; fh.rsv2 = false; diff --git a/include/beast/websocket/impl/write_op.ipp b/include/beast/websocket/impl/write_op.ipp index 225d523e..91811aa4 100644 --- a/include/beast/websocket/impl/write_op.ipp +++ b/include/beast/websocket/impl/write_op.ipp @@ -115,8 +115,7 @@ operator()(error_code ec, bool again) { case 0: { - auto const n = std::min( - d.remain, d.ws.wr_frag_size_); + auto const n = d.remain; d.remain -= n; auto const fin = d.remain <= 0; if(fin) diff --git a/include/beast/websocket/option.hpp b/include/beast/websocket/option.hpp index 4a4feecd..71355676 100644 --- a/include/beast/websocket/option.hpp +++ b/include/beast/websocket/option.hpp @@ -19,37 +19,37 @@ namespace beast { namespace websocket { -/** Automatic fragmentation size option. +/** Automatic fragmentation option. - Sets the maximum size of fragments generated when sending messages - on a WebSocket stream. + Determines if outgoing message payloads are broken up into + multiple pieces. - When the automatic fragmentation size is non-zero, messages exceeding - the size will be split into multiple frames no larger than the size. - This setting does not affect frames sent explicitly using - @ref stream::write_frame or @ref stream::async_write_frame. + When the automatic fragmentation size is turned on, outgoing + message payloads are broken up into multiple frames no larger + than the write buffer size. - The default setting is to fragment messages into 16KB frames. + The default setting is to fragment messages. @note Objects of this type are passed to @ref stream::set_option. @par Example - Setting the automatic fragmentation size option: + Setting the automatic fragmentation option: @code ... websocket::stream stream(ios); - stream.set_option(auto_fragment_size{8192}); + stream.set_option(auto_fragment{true}); @endcode */ #if GENERATING_DOCS -using auto_fragment_size = implementation_defined; +using auto_fragment = implementation_defined; #else -struct auto_fragment_size +struct auto_fragment { - std::size_t value; + bool value; - auto_fragment_size(std::size_t n) - : value(n) + explicit + auto_fragment(bool v) + : value(v) { } }; @@ -142,6 +142,7 @@ struct keep_alive { bool value; + explicit keep_alive(bool v) : value(v) { @@ -309,7 +310,11 @@ struct read_message_max for each connection, while increasing the size of the buffer can reduce the number of calls made to the next layer to write data. - The default setting is 4096. The minimum value is 64. + The default setting is 4096. The minimum value is 8. + + The write buffer size can only be changed when the stream is not + open. Undefined behavior results if the option is modified after a + successful WebSocket handshake. @note Objects of this type are passed to @ref stream::set_option. @@ -332,7 +337,7 @@ struct write_buffer_size write_buffer_size(std::size_t n) : value(n) { - if(n < 64) + if(n < 8) throw std::domain_error("write buffer size is too small"); } }; diff --git a/include/beast/websocket/stream.hpp b/include/beast/websocket/stream.hpp index a35308f1..8cc2631c 100644 --- a/include/beast/websocket/stream.hpp +++ b/include/beast/websocket/stream.hpp @@ -168,13 +168,9 @@ public: /// Set the automatic fragment size option void - set_option(auto_fragment_size const& o) + set_option(auto_fragment const& o) { - if(o.value <= 0) - wr_frag_size_ = - std::numeric_limits::max(); - else - wr_frag_size_ = o.value; + wr_autofrag_ = o.value; } /** Set the decorator used for HTTP messages. @@ -1293,7 +1289,7 @@ public: The current setting of the @ref message_type option controls whether the message opcode is set to text or binary. If the - @ref auto_fragment_size option is set, the message will be split + @ref auto_fragment option is set, the message will be split into one or more frames as necessary. The actual payload contents sent may be transformed as per the WebSocket protocol settings. @@ -1328,7 +1324,7 @@ public: The current setting of the @ref message_type option controls whether the message opcode is set to text or binary. If the - @ref auto_fragment_size option is set, the message will be split + @ref auto_fragment option is set, the message will be split into one or more frames as necessary. The actual payload contents sent may be transformed as per the WebSocket protocol settings. @@ -1370,7 +1366,7 @@ public: The current setting of the @ref message_type option controls whether the message opcode is set to text or binary. If the - @ref auto_fragment_size option is set, the message will be split + @ref auto_fragment option is set, the message will be split into one or more frames as necessary. The actual payload contents sent may be transformed as per the WebSocket protocol settings. @@ -1404,12 +1400,15 @@ public: async_write(ConstBufferSequence const& buffers, WriteHandler&& handler); - /** Send a message frame on the stream. + /** Write partial message data on the stream. - This function is used to write a frame to the stream. The - call will block until one of the following conditions is true: + This function is used to write some or all of a message's + payload to the stream. The call will block until one of the + following conditions is true: - @li The entire frame is sent. + @li A frame is sent. + + @li Message data is transferred to the write buffer. @li An error occurs. @@ -1423,8 +1422,9 @@ public: @param fin `true` if this is the last frame in the message. - @param buffers One or more buffers containing the frame's - payload data. + @param buffers The input buffer sequence holding the data to write. + + @return The number of bytes consumed in the input buffers. @throws system_error Thrown on failure. */ @@ -1432,18 +1432,20 @@ public: void write_frame(bool fin, ConstBufferSequence const& buffers); - /** Send a message frame on the stream. + /** Write partial message data on the stream. - This function is used to write a frame to the stream. The - call will block until one of the following conditions is true: + This function is used to write some or all of a message's + payload to the stream. The call will block until one of the + following conditions is true: - @li The entire frame is sent. + @li A frame is sent. + + @li Message data is transferred to the write buffer. @li An error occurs. This operation is implemented in terms of one or more calls - to the stream's `write_some` function. The actual payload sent - may be transformed as per the WebSocket protocol settings. + to the stream's `write_some` function. If this is the beginning of a new message, the message opcode will be set to text or binary as per the current setting of @@ -1452,10 +1454,11 @@ public: @param fin `true` if this is the last frame in the message. - @param buffers One or more buffers containing the frame's - payload data. + @param buffers The input buffer sequence holding the data to write. @param ec Set to indicate what error occurred, if any. + + @return The number of bytes consumed in the input buffers. */ template void diff --git a/test/websocket/stream.cpp b/test/websocket/stream.cpp index bab5a8c7..44184b10 100644 --- a/test/websocket/stream.cpp +++ b/test/websocket/stream.cpp @@ -149,7 +149,7 @@ public: void testOptions() { stream ws(ios_); - ws.set_option(auto_fragment_size{2048}); + ws.set_option(auto_fragment{true}); ws.set_option(decorate(identity{})); ws.set_option(keep_alive{false}); ws.set_option(write_buffer_size{2048}); @@ -158,7 +158,7 @@ public: ws.set_option(read_message_max{1 * 1024 * 1024}); try { - ws.set_option(write_buffer_size{0}); + ws.set_option(write_buffer_size{7}); fail(); } catch(std::exception const&) @@ -802,7 +802,7 @@ public: using boost::asio::buffer; static std::size_t constexpr limit = 200; std::size_t n; - for(n = 0; n < limit; ++n) + for(n = 199; n < limit; ++n) { stream> ws(n, ios_); auto const restart = @@ -840,7 +840,7 @@ public: ws.handshake("localhost", "/"); // send message - ws.set_option(auto_fragment_size(0)); + ws.set_option(auto_fragment{false}); ws.set_option(message_type(opcode::text)); ws.write(sbuf("Hello")); { @@ -911,16 +911,18 @@ public: ws.set_option(pong_callback{}); // send auto fragmented message - ws.set_option(auto_fragment_size(3)); - ws.write(sbuf("Hello")); + ws.set_option(auto_fragment{true}); + ws.set_option(write_buffer_size{8}); + ws.write(sbuf("Now is the time for all good men")); { // receive echoed message opcode op; - streambuf db; - ws.read(op, db); - BEAST_EXPECT(to_string(db.data()) == "Hello"); + streambuf sb; + ws.read(op, sb); + BEAST_EXPECT(to_string(sb.data()) == "Now is the time for all good men"); } - ws.set_option(auto_fragment_size(0)); + ws.set_option(auto_fragment{false}); + ws.set_option(write_buffer_size{4096}); // send message with write buffer limit { @@ -1077,7 +1079,7 @@ public: throw system_error{ec}; // send message - ws.set_option(auto_fragment_size(0)); + ws.set_option(auto_fragment{false}); ws.set_option(message_type(opcode::text)); ws.async_write(sbuf("Hello"), do_yield[ec]); if(ec) @@ -1171,8 +1173,9 @@ public: } // send auto fragmented message - ws.set_option(auto_fragment_size(3)); - ws.async_write(sbuf("Hello"), do_yield[ec]); + ws.set_option(auto_fragment{true}); + ws.set_option(write_buffer_size{8}); + ws.async_write(sbuf("Now is the time for all good men"), do_yield[ec]); { // receive echoed message opcode op; @@ -1180,9 +1183,10 @@ public: ws.async_read(op, db, do_yield[ec]); if(ec) throw system_error{ec}; - BEAST_EXPECT(to_string(db.data()) == "Hello"); + BEAST_EXPECT(to_string(db.data()) == "Now is the time for all good men"); } - ws.set_option(auto_fragment_size(0)); + ws.set_option(auto_fragment{false}); + ws.set_option(write_buffer_size{4096}); // send message with mask buffer limit { @@ -1379,6 +1383,9 @@ public: static_assert(! std::is_move_assignable< stream>::value, ""); + log << "sizeof(websocket::stream) == " << + sizeof(websocket::stream) << std::endl; + auto const any = endpoint_type{ address_type::from_string("127.0.0.1"), 0}; @@ -1391,13 +1398,13 @@ public: { sync_echo_server server(true, any); auto const ep = server.local_endpoint(); - + //testInvokable1(ep); testInvokable2(ep); testInvokable3(ep); testInvokable4(ep); //testInvokable5(ep); - + testSyncClient(ep); testAsyncWriteFrame(ep); yield_to_mf(ep, &stream_test::testAsyncClient);