]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/beast/websocket/impl/write.ipp
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / boost / boost / beast / websocket / impl / write.ipp
CommitLineData
b32b8144
FG
1//
2// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
3//
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6//
7// Official repository: https://github.com/boostorg/beast
8//
9
10#ifndef BOOST_BEAST_WEBSOCKET_IMPL_WRITE_IPP
11#define BOOST_BEAST_WEBSOCKET_IMPL_WRITE_IPP
12
13#include <boost/beast/core/bind_handler.hpp>
14#include <boost/beast/core/buffers_cat.hpp>
15#include <boost/beast/core/buffers_prefix.hpp>
16#include <boost/beast/core/buffers_suffix.hpp>
b32b8144
FG
17#include <boost/beast/core/flat_static_buffer.hpp>
18#include <boost/beast/core/type_traits.hpp>
19#include <boost/beast/core/detail/clamp.hpp>
20#include <boost/beast/core/detail/config.hpp>
21#include <boost/beast/websocket/detail/frame.hpp>
22#include <boost/asio/associated_allocator.hpp>
23#include <boost/asio/associated_executor.hpp>
24#include <boost/asio/coroutine.hpp>
25#include <boost/asio/handler_continuation_hook.hpp>
11fdf7f2 26#include <boost/asio/handler_invoke_hook.hpp>
b32b8144
FG
27#include <boost/assert.hpp>
28#include <boost/config.hpp>
29#include <boost/throw_exception.hpp>
30#include <algorithm>
31#include <memory>
32
33namespace boost {
34namespace beast {
35namespace websocket {
36
11fdf7f2
TL
37namespace detail {
38
39// Compress a buffer sequence
40// Returns: `true` if more calls are needed
41//
42template<>
43template<class ConstBufferSequence>
44bool
45stream_base<true>::
46deflate(
47 boost::asio::mutable_buffer& out,
48 buffers_suffix<ConstBufferSequence>& cb,
49 bool fin,
50 std::size_t& total_in,
51 error_code& ec)
52{
53 using boost::asio::buffer;
54 BOOST_ASSERT(out.size() >= 6);
55 auto& zo = this->pmd_->zo;
56 zlib::z_params zs;
57 zs.avail_in = 0;
58 zs.next_in = nullptr;
59 zs.avail_out = out.size();
60 zs.next_out = out.data();
61 for(auto in : beast::detail::buffers_range(cb))
62 {
63 zs.avail_in = in.size();
64 if(zs.avail_in == 0)
65 continue;
66 zs.next_in = in.data();
67 zo.write(zs, zlib::Flush::none, ec);
68 if(ec)
69 {
70 if(ec != zlib::error::need_buffers)
71 return false;
72 BOOST_ASSERT(zs.avail_out == 0);
73 BOOST_ASSERT(zs.total_out == out.size());
74 ec.assign(0, ec.category());
75 break;
76 }
77 if(zs.avail_out == 0)
78 {
79 BOOST_ASSERT(zs.total_out == out.size());
80 break;
81 }
82 BOOST_ASSERT(zs.avail_in == 0);
83 }
84 total_in = zs.total_in;
85 cb.consume(zs.total_in);
86 if(zs.avail_out > 0 && fin)
87 {
88 auto const remain = boost::asio::buffer_size(cb);
89 if(remain == 0)
90 {
91 // Inspired by Mark Adler
92 // https://github.com/madler/zlib/issues/149
93 //
94 // VFALCO We could do this flush twice depending
95 // on how much space is in the output.
96 zo.write(zs, zlib::Flush::block, ec);
97 BOOST_ASSERT(! ec || ec == zlib::error::need_buffers);
98 if(ec == zlib::error::need_buffers)
99 ec.assign(0, ec.category());
100 if(ec)
101 return false;
102 if(zs.avail_out >= 6)
103 {
104 zo.write(zs, zlib::Flush::full, ec);
105 BOOST_ASSERT(! ec);
106 // remove flush marker
107 zs.total_out -= 4;
108 out = buffer(out.data(), zs.total_out);
109 return false;
110 }
111 }
112 }
113 ec.assign(0, ec.category());
114 out = buffer(out.data(), zs.total_out);
115 return true;
116}
117
118template<>
119inline
120void
121stream_base<true>::
122do_context_takeover_write(role_type role)
123{
124 if((role == role_type::client &&
125 this->pmd_config_.client_no_context_takeover) ||
126 (role == role_type::server &&
127 this->pmd_config_.server_no_context_takeover))
128 {
129 this->pmd_->zo.reset();
130 }
131}
132
133} // detail
134
135//------------------------------------------------------------------------------
136
137template<class NextLayer, bool deflateSupported>
b32b8144 138template<class Buffers, class Handler>
11fdf7f2 139class stream<NextLayer, deflateSupported>::write_some_op
b32b8144
FG
140 : public boost::asio::coroutine
141{
142 Handler h_;
11fdf7f2 143 stream<NextLayer, deflateSupported>& ws_;
b32b8144
FG
144 buffers_suffix<Buffers> cb_;
145 detail::frame_header fh_;
146 detail::prepared_key key_;
147 std::size_t bytes_transferred_ = 0;
148 std::size_t remain_;
149 std::size_t in_;
b32b8144
FG
150 int how_;
151 bool fin_;
152 bool more_;
153 bool cont_ = false;
154
155public:
11fdf7f2
TL
156 static constexpr int id = 2; // for soft_mutex
157
b32b8144 158 write_some_op(write_some_op&&) = default;
11fdf7f2 159 write_some_op(write_some_op const&) = delete;
b32b8144
FG
160
161 template<class DeducedHandler>
162 write_some_op(
163 DeducedHandler&& h,
11fdf7f2 164 stream<NextLayer, deflateSupported>& ws,
b32b8144
FG
165 bool fin,
166 Buffers const& bs)
167 : h_(std::forward<DeducedHandler>(h))
168 , ws_(ws)
169 , cb_(bs)
b32b8144
FG
170 , fin_(fin)
171 {
172 }
173
174 using allocator_type =
175 boost::asio::associated_allocator_t<Handler>;
176
177 allocator_type
178 get_allocator() const noexcept
179 {
11fdf7f2 180 return (boost::asio::get_associated_allocator)(h_);
b32b8144
FG
181 }
182
183 using executor_type = boost::asio::associated_executor_t<
11fdf7f2 184 Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
b32b8144
FG
185
186 executor_type
187 get_executor() const noexcept
188 {
11fdf7f2 189 return (boost::asio::get_associated_executor)(
b32b8144
FG
190 h_, ws_.get_executor());
191 }
192
193 Handler&
194 handler()
195 {
196 return h_;
197 }
198
199 void operator()(
200 error_code ec = {},
201 std::size_t bytes_transferred = 0,
202 bool cont = true);
203
204 friend
205 bool asio_handler_is_continuation(write_some_op* op)
206 {
207 using boost::asio::asio_handler_is_continuation;
208 return op->cont_ || asio_handler_is_continuation(
209 std::addressof(op->h_));
210 }
11fdf7f2
TL
211
212 template<class Function>
213 friend
214 void asio_handler_invoke(Function&& f, write_some_op* op)
215 {
216 using boost::asio::asio_handler_invoke;
217 asio_handler_invoke(
218 f, std::addressof(op->h_));
219 }
b32b8144
FG
220};
221
11fdf7f2 222template<class NextLayer, bool deflateSupported>
b32b8144
FG
223template<class Buffers, class Handler>
224void
11fdf7f2 225stream<NextLayer, deflateSupported>::
b32b8144
FG
226write_some_op<Buffers, Handler>::
227operator()(
228 error_code ec,
229 std::size_t bytes_transferred,
230 bool cont)
231{
232 using beast::detail::clamp;
233 using boost::asio::buffer;
234 using boost::asio::buffer_copy;
235 using boost::asio::buffer_size;
236 using boost::asio::mutable_buffer;
237 enum
238 {
239 do_nomask_nofrag,
240 do_nomask_frag,
241 do_mask_nofrag,
242 do_mask_frag,
243 do_deflate
244 };
245 std::size_t n;
246 boost::asio::mutable_buffer b;
247 cont_ = cont;
248 BOOST_ASIO_CORO_REENTER(*this)
249 {
250 // Set up the outgoing frame header
251 if(! ws_.wr_cont_)
252 {
253 ws_.begin_msg();
254 fh_.rsv1 = ws_.wr_compress_;
255 }
256 else
257 {
258 fh_.rsv1 = false;
259 }
260 fh_.rsv2 = false;
261 fh_.rsv3 = false;
262 fh_.op = ws_.wr_cont_ ?
263 detail::opcode::cont : ws_.wr_opcode_;
264 fh_.mask =
265 ws_.role_ == role_type::client;
266
267 // Choose a write algorithm
268 if(ws_.wr_compress_)
269 {
270 how_ = do_deflate;
271 }
272 else if(! fh_.mask)
273 {
274 if(! ws_.wr_frag_)
275 {
276 how_ = do_nomask_nofrag;
277 }
278 else
279 {
280 BOOST_ASSERT(ws_.wr_buf_size_ != 0);
281 remain_ = buffer_size(cb_);
282 if(remain_ > ws_.wr_buf_size_)
283 how_ = do_nomask_frag;
284 else
285 how_ = do_nomask_nofrag;
286 }
287 }
288 else
289 {
290 if(! ws_.wr_frag_)
291 {
292 how_ = do_mask_nofrag;
293 }
294 else
295 {
296 BOOST_ASSERT(ws_.wr_buf_size_ != 0);
297 remain_ = buffer_size(cb_);
298 if(remain_ > ws_.wr_buf_size_)
299 how_ = do_mask_frag;
300 else
301 how_ = do_mask_nofrag;
302 }
303 }
304
305 // Maybe suspend
11fdf7f2 306 if(ws_.wr_block_.try_lock(this))
b32b8144 307 {
b32b8144
FG
308 // Make sure the stream is open
309 if(! ws_.check_open(ec))
310 goto upcall;
311 }
312 else
313 {
314 do_suspend:
315 // Suspend
b32b8144 316 BOOST_ASIO_CORO_YIELD
11fdf7f2 317 ws_.paused_wr_.emplace(std::move(*this));
b32b8144
FG
318
319 // Acquire the write block
11fdf7f2 320 ws_.wr_block_.lock(this);
b32b8144
FG
321
322 // Resume
323 BOOST_ASIO_CORO_YIELD
324 boost::asio::post(
325 ws_.get_executor(), std::move(*this));
11fdf7f2 326 BOOST_ASSERT(ws_.wr_block_.is_locked(this));
b32b8144
FG
327
328 // Make sure the stream is open
329 if(! ws_.check_open(ec))
330 goto upcall;
331 }
332
333 //------------------------------------------------------------------
334
335 if(how_ == do_nomask_nofrag)
336 {
337 fh_.fin = fin_;
338 fh_.len = buffer_size(cb_);
339 ws_.wr_fb_.reset();
340 detail::write<flat_static_buffer_base>(
341 ws_.wr_fb_, fh_);
342 ws_.wr_cont_ = ! fin_;
343 // Send frame
344 BOOST_ASIO_CORO_YIELD
345 boost::asio::async_write(ws_.stream_,
346 buffers_cat(ws_.wr_fb_.data(), cb_),
347 std::move(*this));
348 if(! ws_.check_ok(ec))
349 goto upcall;
350 bytes_transferred_ += clamp(fh_.len);
351 goto upcall;
352 }
353
354 //------------------------------------------------------------------
355
356 else if(how_ == do_nomask_frag)
357 {
358 for(;;)
359 {
360 n = clamp(remain_, ws_.wr_buf_size_);
361 fh_.len = n;
362 remain_ -= n;
363 fh_.fin = fin_ ? remain_ == 0 : false;
364 ws_.wr_fb_.reset();
365 detail::write<flat_static_buffer_base>(
366 ws_.wr_fb_, fh_);
367 ws_.wr_cont_ = ! fin_;
368 // Send frame
369 BOOST_ASIO_CORO_YIELD
370 boost::asio::async_write(
371 ws_.stream_, buffers_cat(
372 ws_.wr_fb_.data(), buffers_prefix(
373 clamp(fh_.len), cb_)),
374 std::move(*this));
375 if(! ws_.check_ok(ec))
376 goto upcall;
377 n = clamp(fh_.len); // because yield
378 bytes_transferred_ += n;
379 if(remain_ == 0)
380 break;
381 cb_.consume(n);
382 fh_.op = detail::opcode::cont;
383 // Allow outgoing control frames to
384 // be sent in between message frames
11fdf7f2 385 ws_.wr_block_.unlock(this);
b32b8144
FG
386 if( ws_.paused_close_.maybe_invoke() ||
387 ws_.paused_rd_.maybe_invoke() ||
388 ws_.paused_ping_.maybe_invoke())
389 {
11fdf7f2 390 BOOST_ASSERT(ws_.wr_block_.is_locked());
b32b8144
FG
391 goto do_suspend;
392 }
11fdf7f2 393 ws_.wr_block_.lock(this);
b32b8144
FG
394 }
395 goto upcall;
396 }
397
398 //------------------------------------------------------------------
399
400 else if(how_ == do_mask_nofrag)
401 {
402 remain_ = buffer_size(cb_);
403 fh_.fin = fin_;
404 fh_.len = remain_;
405 fh_.key = ws_.wr_gen_();
406 detail::prepare_key(key_, fh_.key);
407 ws_.wr_fb_.reset();
408 detail::write<flat_static_buffer_base>(
409 ws_.wr_fb_, fh_);
410 n = clamp(remain_, ws_.wr_buf_size_);
411 buffer_copy(buffer(
412 ws_.wr_buf_.get(), n), cb_);
413 detail::mask_inplace(buffer(
414 ws_.wr_buf_.get(), n), key_);
415 remain_ -= n;
416 ws_.wr_cont_ = ! fin_;
417 // Send frame header and partial payload
418 BOOST_ASIO_CORO_YIELD
419 boost::asio::async_write(
420 ws_.stream_, buffers_cat(ws_.wr_fb_.data(),
421 buffer(ws_.wr_buf_.get(), n)),
422 std::move(*this));
423 if(! ws_.check_ok(ec))
424 goto upcall;
425 bytes_transferred_ +=
426 bytes_transferred - ws_.wr_fb_.size();
427 while(remain_ > 0)
428 {
429 cb_.consume(ws_.wr_buf_size_);
430 n = clamp(remain_, ws_.wr_buf_size_);
431 buffer_copy(buffer(
432 ws_.wr_buf_.get(), n), cb_);
433 detail::mask_inplace(buffer(
434 ws_.wr_buf_.get(), n), key_);
435 remain_ -= n;
436 // Send partial payload
437 BOOST_ASIO_CORO_YIELD
438 boost::asio::async_write(ws_.stream_,
439 buffer(ws_.wr_buf_.get(), n),
440 std::move(*this));
441 if(! ws_.check_ok(ec))
442 goto upcall;
443 bytes_transferred_ += bytes_transferred;
444 }
445 goto upcall;
446 }
447
448 //------------------------------------------------------------------
449
450 else if(how_ == do_mask_frag)
451 {
452 for(;;)
453 {
454 n = clamp(remain_, ws_.wr_buf_size_);
455 remain_ -= n;
456 fh_.len = n;
457 fh_.key = ws_.wr_gen_();
458 fh_.fin = fin_ ? remain_ == 0 : false;
459 detail::prepare_key(key_, fh_.key);
460 buffer_copy(buffer(
461 ws_.wr_buf_.get(), n), cb_);
462 detail::mask_inplace(buffer(
463 ws_.wr_buf_.get(), n), key_);
464 ws_.wr_fb_.reset();
465 detail::write<flat_static_buffer_base>(
466 ws_.wr_fb_, fh_);
467 ws_.wr_cont_ = ! fin_;
468 // Send frame
469 BOOST_ASIO_CORO_YIELD
470 boost::asio::async_write(ws_.stream_,
471 buffers_cat(ws_.wr_fb_.data(),
472 buffer(ws_.wr_buf_.get(), n)),
473 std::move(*this));
474 if(! ws_.check_ok(ec))
475 goto upcall;
476 n = bytes_transferred - ws_.wr_fb_.size();
477 bytes_transferred_ += n;
478 if(remain_ == 0)
479 break;
480 cb_.consume(n);
481 fh_.op = detail::opcode::cont;
482 // Allow outgoing control frames to
483 // be sent in between message frames:
11fdf7f2 484 ws_.wr_block_.unlock(this);
b32b8144
FG
485 if( ws_.paused_close_.maybe_invoke() ||
486 ws_.paused_rd_.maybe_invoke() ||
487 ws_.paused_ping_.maybe_invoke())
488 {
11fdf7f2 489 BOOST_ASSERT(ws_.wr_block_.is_locked());
b32b8144
FG
490 goto do_suspend;
491 }
11fdf7f2 492 ws_.wr_block_.lock(this);
b32b8144
FG
493 }
494 goto upcall;
495 }
496
497 //------------------------------------------------------------------
498
499 else if(how_ == do_deflate)
500 {
501 for(;;)
502 {
503 b = buffer(ws_.wr_buf_.get(),
504 ws_.wr_buf_size_);
11fdf7f2 505 more_ = ws_.deflate(b, cb_, fin_, in_, ec);
b32b8144
FG
506 if(! ws_.check_ok(ec))
507 goto upcall;
508 n = buffer_size(b);
509 if(n == 0)
510 {
511 // The input was consumed, but there
512 // is no output due to compression
513 // latency.
514 BOOST_ASSERT(! fin_);
515 BOOST_ASSERT(buffer_size(cb_) == 0);
516 goto upcall;
517 }
518 if(fh_.mask)
519 {
520 fh_.key = ws_.wr_gen_();
521 detail::prepared_key key;
522 detail::prepare_key(key, fh_.key);
523 detail::mask_inplace(b, key);
524 }
525 fh_.fin = ! more_;
526 fh_.len = n;
527 ws_.wr_fb_.reset();
528 detail::write<
529 flat_static_buffer_base>(ws_.wr_fb_, fh_);
530 ws_.wr_cont_ = ! fin_;
531 // Send frame
532 BOOST_ASIO_CORO_YIELD
533 boost::asio::async_write(ws_.stream_,
534 buffers_cat(ws_.wr_fb_.data(), b),
535 std::move(*this));
536 if(! ws_.check_ok(ec))
537 goto upcall;
538 bytes_transferred_ += in_;
539 if(more_)
540 {
541 fh_.op = detail::opcode::cont;
542 fh_.rsv1 = false;
543 // Allow outgoing control frames to
544 // be sent in between message frames:
11fdf7f2 545 ws_.wr_block_.unlock(this);
b32b8144
FG
546 if( ws_.paused_close_.maybe_invoke() ||
547 ws_.paused_rd_.maybe_invoke() ||
548 ws_.paused_ping_.maybe_invoke())
549 {
11fdf7f2 550 BOOST_ASSERT(ws_.wr_block_.is_locked());
b32b8144
FG
551 goto do_suspend;
552 }
11fdf7f2 553 ws_.wr_block_.lock(this);
b32b8144
FG
554 }
555 else
556 {
11fdf7f2
TL
557 if(fh_.fin)
558 ws_.do_context_takeover_write(ws_.role_);
b32b8144
FG
559 goto upcall;
560 }
561 }
562 }
563
564 //--------------------------------------------------------------------------
565
566 upcall:
11fdf7f2 567 ws_.wr_block_.unlock(this);
b32b8144
FG
568 ws_.paused_close_.maybe_invoke() ||
569 ws_.paused_rd_.maybe_invoke() ||
570 ws_.paused_ping_.maybe_invoke();
571 if(! cont_)
572 return boost::asio::post(
573 ws_.stream_.get_executor(),
11fdf7f2 574 bind_handler(std::move(h_), ec, bytes_transferred_));
b32b8144
FG
575 h_(ec, bytes_transferred_);
576 }
577}
578
579//------------------------------------------------------------------------------
580
11fdf7f2 581template<class NextLayer, bool deflateSupported>
b32b8144
FG
582template<class ConstBufferSequence>
583std::size_t
11fdf7f2 584stream<NextLayer, deflateSupported>::
b32b8144
FG
585write_some(bool fin, ConstBufferSequence const& buffers)
586{
587 static_assert(is_sync_stream<next_layer_type>::value,
588 "SyncStream requirements not met");
589 static_assert(boost::asio::is_const_buffer_sequence<
590 ConstBufferSequence>::value,
591 "ConstBufferSequence requirements not met");
592 error_code ec;
593 auto const bytes_transferred =
594 write_some(fin, buffers, ec);
595 if(ec)
596 BOOST_THROW_EXCEPTION(system_error{ec});
597 return bytes_transferred;
598}
599
11fdf7f2 600template<class NextLayer, bool deflateSupported>
b32b8144
FG
601template<class ConstBufferSequence>
602std::size_t
11fdf7f2 603stream<NextLayer, deflateSupported>::
b32b8144
FG
604write_some(bool fin,
605 ConstBufferSequence const& buffers, error_code& ec)
606{
607 static_assert(is_sync_stream<next_layer_type>::value,
608 "SyncStream requirements not met");
609 static_assert(boost::asio::is_const_buffer_sequence<
610 ConstBufferSequence>::value,
611 "ConstBufferSequence requirements not met");
612 using beast::detail::clamp;
613 using boost::asio::buffer;
614 using boost::asio::buffer_copy;
615 using boost::asio::buffer_size;
616 std::size_t bytes_transferred = 0;
617 ec.assign(0, ec.category());
618 // Make sure the stream is open
619 if(! check_open(ec))
620 return bytes_transferred;
621 detail::frame_header fh;
622 if(! wr_cont_)
623 {
624 begin_msg();
625 fh.rsv1 = wr_compress_;
626 }
627 else
628 {
629 fh.rsv1 = false;
630 }
631 fh.rsv2 = false;
632 fh.rsv3 = false;
633 fh.op = wr_cont_ ?
634 detail::opcode::cont : wr_opcode_;
635 fh.mask = role_ == role_type::client;
636 auto remain = buffer_size(buffers);
637 if(wr_compress_)
638 {
639 buffers_suffix<
640 ConstBufferSequence> cb{buffers};
641 for(;;)
642 {
643 auto b = buffer(
644 wr_buf_.get(), wr_buf_size_);
11fdf7f2
TL
645 auto const more = this->deflate(
646 b, cb, fin, bytes_transferred, ec);
b32b8144
FG
647 if(! check_ok(ec))
648 return bytes_transferred;
649 auto const n = buffer_size(b);
650 if(n == 0)
651 {
652 // The input was consumed, but there
653 // is no output due to compression
654 // latency.
655 BOOST_ASSERT(! fin);
656 BOOST_ASSERT(buffer_size(cb) == 0);
657 fh.fin = false;
658 break;
659 }
660 if(fh.mask)
661 {
662 fh.key = wr_gen_();
663 detail::prepared_key key;
664 detail::prepare_key(key, fh.key);
665 detail::mask_inplace(b, key);
666 }
667 fh.fin = ! more;
668 fh.len = n;
669 detail::fh_buffer fh_buf;
670 detail::write<
671 flat_static_buffer_base>(fh_buf, fh);
672 wr_cont_ = ! fin;
673 boost::asio::write(stream_,
674 buffers_cat(fh_buf.data(), b), ec);
675 if(! check_ok(ec))
676 return bytes_transferred;
677 if(! more)
678 break;
679 fh.op = detail::opcode::cont;
680 fh.rsv1 = false;
681 }
11fdf7f2
TL
682 if(fh.fin)
683 this->do_context_takeover_write(role_);
b32b8144
FG
684 }
685 else if(! fh.mask)
686 {
687 if(! wr_frag_)
688 {
689 // no mask, no autofrag
690 fh.fin = fin;
691 fh.len = remain;
692 detail::fh_buffer fh_buf;
693 detail::write<
694 flat_static_buffer_base>(fh_buf, fh);
695 wr_cont_ = ! fin;
696 boost::asio::write(stream_,
697 buffers_cat(fh_buf.data(), buffers), ec);
698 if(! check_ok(ec))
699 return bytes_transferred;
700 bytes_transferred += remain;
701 }
702 else
703 {
704 // no mask, autofrag
705 BOOST_ASSERT(wr_buf_size_ != 0);
706 buffers_suffix<
707 ConstBufferSequence> cb{buffers};
708 for(;;)
709 {
710 auto const n = clamp(remain, wr_buf_size_);
711 remain -= n;
712 fh.len = n;
713 fh.fin = fin ? remain == 0 : false;
714 detail::fh_buffer fh_buf;
715 detail::write<
716 flat_static_buffer_base>(fh_buf, fh);
717 wr_cont_ = ! fin;
718 boost::asio::write(stream_,
719 buffers_cat(fh_buf.data(),
720 buffers_prefix(n, cb)), ec);
721 if(! check_ok(ec))
722 return bytes_transferred;
723 bytes_transferred += n;
724 if(remain == 0)
725 break;
726 fh.op = detail::opcode::cont;
727 cb.consume(n);
728 }
729 }
730 }
731 else if(! wr_frag_)
732 {
733 // mask, no autofrag
734 fh.fin = fin;
735 fh.len = remain;
736 fh.key = wr_gen_();
737 detail::prepared_key key;
738 detail::prepare_key(key, fh.key);
739 detail::fh_buffer fh_buf;
740 detail::write<
741 flat_static_buffer_base>(fh_buf, fh);
742 buffers_suffix<
743 ConstBufferSequence> cb{buffers};
744 {
745 auto const n = clamp(remain, wr_buf_size_);
746 auto const b = buffer(wr_buf_.get(), n);
747 buffer_copy(b, cb);
748 cb.consume(n);
749 remain -= n;
750 detail::mask_inplace(b, key);
751 wr_cont_ = ! fin;
752 boost::asio::write(stream_,
753 buffers_cat(fh_buf.data(), b), ec);
754 if(! check_ok(ec))
755 return bytes_transferred;
756 bytes_transferred += n;
757 }
758 while(remain > 0)
759 {
760 auto const n = clamp(remain, wr_buf_size_);
761 auto const b = buffer(wr_buf_.get(), n);
762 buffer_copy(b, cb);
763 cb.consume(n);
764 remain -= n;
765 detail::mask_inplace(b, key);
766 boost::asio::write(stream_, b, ec);
767 if(! check_ok(ec))
768 return bytes_transferred;
769 bytes_transferred += n;
770 }
771 }
772 else
773 {
774 // mask, autofrag
775 BOOST_ASSERT(wr_buf_size_ != 0);
776 buffers_suffix<
777 ConstBufferSequence> cb{buffers};
778 for(;;)
779 {
780 fh.key = wr_gen_();
781 detail::prepared_key key;
782 detail::prepare_key(key, fh.key);
783 auto const n = clamp(remain, wr_buf_size_);
784 auto const b = buffer(wr_buf_.get(), n);
785 buffer_copy(b, cb);
786 detail::mask_inplace(b, key);
787 fh.len = n;
788 remain -= n;
789 fh.fin = fin ? remain == 0 : false;
790 wr_cont_ = ! fh.fin;
791 detail::fh_buffer fh_buf;
792 detail::write<
793 flat_static_buffer_base>(fh_buf, fh);
794 boost::asio::write(stream_,
795 buffers_cat(fh_buf.data(), b), ec);
796 if(! check_ok(ec))
797 return bytes_transferred;
798 bytes_transferred += n;
799 if(remain == 0)
800 break;
801 fh.op = detail::opcode::cont;
802 cb.consume(n);
803 }
804 }
805 return bytes_transferred;
806}
807
11fdf7f2 808template<class NextLayer, bool deflateSupported>
b32b8144
FG
809template<class ConstBufferSequence, class WriteHandler>
810BOOST_ASIO_INITFN_RESULT_TYPE(
811 WriteHandler, void(error_code, std::size_t))
11fdf7f2 812stream<NextLayer, deflateSupported>::
b32b8144
FG
813async_write_some(bool fin,
814 ConstBufferSequence const& bs, WriteHandler&& handler)
815{
816 static_assert(is_async_stream<next_layer_type>::value,
817 "AsyncStream requirements not met");
818 static_assert(boost::asio::is_const_buffer_sequence<
819 ConstBufferSequence>::value,
820 "ConstBufferSequence requirements not met");
11fdf7f2
TL
821 BOOST_BEAST_HANDLER_INIT(
822 WriteHandler, void(error_code, std::size_t));
b32b8144
FG
823 write_some_op<ConstBufferSequence, BOOST_ASIO_HANDLER_TYPE(
824 WriteHandler, void(error_code, std::size_t))>{
11fdf7f2 825 std::move(init.completion_handler), *this, fin, bs}(
b32b8144
FG
826 {}, 0, false);
827 return init.result.get();
828}
829
830//------------------------------------------------------------------------------
831
11fdf7f2 832template<class NextLayer, bool deflateSupported>
b32b8144
FG
833template<class ConstBufferSequence>
834std::size_t
11fdf7f2 835stream<NextLayer, deflateSupported>::
b32b8144
FG
836write(ConstBufferSequence const& buffers)
837{
838 static_assert(is_sync_stream<next_layer_type>::value,
839 "SyncStream requirements not met");
840 static_assert(boost::asio::is_const_buffer_sequence<
841 ConstBufferSequence>::value,
842 "ConstBufferSequence requirements not met");
843 error_code ec;
844 auto const bytes_transferred = write(buffers, ec);
845 if(ec)
846 BOOST_THROW_EXCEPTION(system_error{ec});
847 return bytes_transferred;
848}
849
11fdf7f2 850template<class NextLayer, bool deflateSupported>
b32b8144
FG
851template<class ConstBufferSequence>
852std::size_t
11fdf7f2 853stream<NextLayer, deflateSupported>::
b32b8144
FG
854write(ConstBufferSequence const& buffers, error_code& ec)
855{
856 static_assert(is_sync_stream<next_layer_type>::value,
857 "SyncStream requirements not met");
858 static_assert(boost::asio::is_const_buffer_sequence<
859 ConstBufferSequence>::value,
860 "ConstBufferSequence requirements not met");
861 return write_some(true, buffers, ec);
862}
863
11fdf7f2 864template<class NextLayer, bool deflateSupported>
b32b8144
FG
865template<class ConstBufferSequence, class WriteHandler>
866BOOST_ASIO_INITFN_RESULT_TYPE(
867 WriteHandler, void(error_code, std::size_t))
11fdf7f2 868stream<NextLayer, deflateSupported>::
b32b8144
FG
869async_write(
870 ConstBufferSequence const& bs, WriteHandler&& handler)
871{
872 static_assert(is_async_stream<next_layer_type>::value,
873 "AsyncStream requirements not met");
874 static_assert(boost::asio::is_const_buffer_sequence<
875 ConstBufferSequence>::value,
876 "ConstBufferSequence requirements not met");
11fdf7f2
TL
877 BOOST_BEAST_HANDLER_INIT(
878 WriteHandler, void(error_code, std::size_t));
b32b8144
FG
879 write_some_op<ConstBufferSequence, BOOST_ASIO_HANDLER_TYPE(
880 WriteHandler, void(error_code, std::size_t))>{
11fdf7f2 881 std::move(init.completion_handler), *this, true, bs}(
b32b8144
FG
882 {}, 0, false);
883 return init.result.get();
884}
885
886} // websocket
887} // beast
888} // boost
889
890#endif