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