]> git.proxmox.com Git - ceph.git/blob - ceph/src/Beast/include/beast/http/impl/write.ipp
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / Beast / include / beast / http / impl / write.ipp
1 //
2 // Copyright (c) 2013-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
8 #ifndef BEAST_HTTP_IMPL_WRITE_IPP
9 #define BEAST_HTTP_IMPL_WRITE_IPP
10
11 #include <beast/http/concepts.hpp>
12 #include <beast/http/chunk_encode.hpp>
13 #include <beast/core/buffer_cat.hpp>
14 #include <beast/core/bind_handler.hpp>
15 #include <beast/core/buffer_concepts.hpp>
16 #include <beast/core/handler_helpers.hpp>
17 #include <beast/core/handler_ptr.hpp>
18 #include <beast/core/stream_concepts.hpp>
19 #include <beast/core/streambuf.hpp>
20 #include <beast/core/write_dynabuf.hpp>
21 #include <beast/core/detail/sync_ostream.hpp>
22 #include <boost/asio/write.hpp>
23 #include <condition_variable>
24 #include <mutex>
25 #include <ostream>
26 #include <sstream>
27 #include <type_traits>
28
29 namespace beast {
30 namespace http {
31
32 namespace detail {
33
34 template<class DynamicBuffer, class Fields>
35 void
36 write_start_line(DynamicBuffer& dynabuf,
37 header<true, Fields> const& msg)
38 {
39 BOOST_ASSERT(msg.version == 10 || msg.version == 11);
40 write(dynabuf, msg.method);
41 write(dynabuf, " ");
42 write(dynabuf, msg.url);
43 switch(msg.version)
44 {
45 case 10:
46 write(dynabuf, " HTTP/1.0\r\n");
47 break;
48 case 11:
49 write(dynabuf, " HTTP/1.1\r\n");
50 break;
51 }
52 }
53
54 template<class DynamicBuffer, class Fields>
55 void
56 write_start_line(DynamicBuffer& dynabuf,
57 header<false, Fields> const& msg)
58 {
59 BOOST_ASSERT(msg.version == 10 || msg.version == 11);
60 switch(msg.version)
61 {
62 case 10:
63 write(dynabuf, "HTTP/1.0 ");
64 break;
65 case 11:
66 write(dynabuf, "HTTP/1.1 ");
67 break;
68 }
69 write(dynabuf, msg.status);
70 write(dynabuf, " ");
71 write(dynabuf, msg.reason);
72 write(dynabuf, "\r\n");
73 }
74
75 template<class DynamicBuffer, class FieldSequence>
76 void
77 write_fields(DynamicBuffer& dynabuf, FieldSequence const& fields)
78 {
79 static_assert(is_DynamicBuffer<DynamicBuffer>::value,
80 "DynamicBuffer requirements not met");
81 //static_assert(is_FieldSequence<FieldSequence>::value,
82 // "FieldSequence requirements not met");
83 for(auto const& field : fields)
84 {
85 write(dynabuf, field.name());
86 write(dynabuf, ": ");
87 write(dynabuf, field.value());
88 write(dynabuf, "\r\n");
89 }
90 }
91
92 } // detail
93
94 //------------------------------------------------------------------------------
95
96 namespace detail {
97
98 template<class Stream, class Handler>
99 class write_streambuf_op
100 {
101 struct data
102 {
103 bool cont;
104 Stream& s;
105 streambuf sb;
106 int state = 0;
107
108 data(Handler& handler, Stream& s_,
109 streambuf&& sb_)
110 : cont(beast_asio_helpers::
111 is_continuation(handler))
112 , s(s_)
113 , sb(std::move(sb_))
114 {
115 }
116 };
117
118 handler_ptr<data, Handler> d_;
119
120 public:
121 write_streambuf_op(write_streambuf_op&&) = default;
122 write_streambuf_op(write_streambuf_op const&) = default;
123
124 template<class DeducedHandler, class... Args>
125 write_streambuf_op(DeducedHandler&& h, Stream& s,
126 Args&&... args)
127 : d_(std::forward<DeducedHandler>(h),
128 s, std::forward<Args>(args)...)
129 {
130 (*this)(error_code{}, 0, false);
131 }
132
133 void
134 operator()(error_code ec,
135 std::size_t bytes_transferred, bool again = true);
136
137 friend
138 void* asio_handler_allocate(
139 std::size_t size, write_streambuf_op* op)
140 {
141 return beast_asio_helpers::
142 allocate(size, op->d_.handler());
143 }
144
145 friend
146 void asio_handler_deallocate(
147 void* p, std::size_t size, write_streambuf_op* op)
148 {
149 return beast_asio_helpers::
150 deallocate(p, size, op->d_.handler());
151 }
152
153 friend
154 bool asio_handler_is_continuation(write_streambuf_op* op)
155 {
156 return op->d_->cont;
157 }
158
159 template<class Function>
160 friend
161 void asio_handler_invoke(Function&& f, write_streambuf_op* op)
162 {
163 return beast_asio_helpers::
164 invoke(f, op->d_.handler());
165 }
166 };
167
168 template<class Stream, class Handler>
169 void
170 write_streambuf_op<Stream, Handler>::
171 operator()(error_code ec, std::size_t, bool again)
172 {
173 auto& d = *d_;
174 d.cont = d.cont || again;
175 while(! ec && d.state != 99)
176 {
177 switch(d.state)
178 {
179 case 0:
180 {
181 d.state = 99;
182 boost::asio::async_write(d.s,
183 d.sb.data(), std::move(*this));
184 return;
185 }
186 }
187 }
188 d_.invoke(ec);
189 }
190
191 } // detail
192
193 template<class SyncWriteStream,
194 bool isRequest, class Fields>
195 void
196 write(SyncWriteStream& stream,
197 header<isRequest, Fields> const& msg)
198 {
199 static_assert(is_SyncWriteStream<SyncWriteStream>::value,
200 "SyncWriteStream requirements not met");
201 error_code ec;
202 write(stream, msg, ec);
203 if(ec)
204 throw system_error{ec};
205 }
206
207 template<class SyncWriteStream,
208 bool isRequest, class Fields>
209 void
210 write(SyncWriteStream& stream,
211 header<isRequest, Fields> const& msg,
212 error_code& ec)
213 {
214 static_assert(is_SyncWriteStream<SyncWriteStream>::value,
215 "SyncWriteStream requirements not met");
216 streambuf sb;
217 detail::write_start_line(sb, msg);
218 detail::write_fields(sb, msg.fields);
219 beast::write(sb, "\r\n");
220 boost::asio::write(stream, sb.data(), ec);
221 }
222
223 template<class AsyncWriteStream,
224 bool isRequest, class Fields,
225 class WriteHandler>
226 typename async_completion<
227 WriteHandler, void(error_code)>::result_type
228 async_write(AsyncWriteStream& stream,
229 header<isRequest, Fields> const& msg,
230 WriteHandler&& handler)
231 {
232 static_assert(is_AsyncWriteStream<AsyncWriteStream>::value,
233 "AsyncWriteStream requirements not met");
234 beast::async_completion<WriteHandler,
235 void(error_code)> completion{handler};
236 streambuf sb;
237 detail::write_start_line(sb, msg);
238 detail::write_fields(sb, msg.fields);
239 beast::write(sb, "\r\n");
240 detail::write_streambuf_op<AsyncWriteStream,
241 decltype(completion.handler)>{
242 completion.handler, stream, std::move(sb)};
243 return completion.result.get();
244 }
245
246 //------------------------------------------------------------------------------
247
248 namespace detail {
249
250 template<bool isRequest, class Body, class Fields>
251 struct write_preparation
252 {
253 message<isRequest, Body, Fields> const& msg;
254 typename Body::writer w;
255 streambuf sb;
256 bool chunked;
257 bool close;
258
259 explicit
260 write_preparation(
261 message<isRequest, Body, Fields> const& msg_)
262 : msg(msg_)
263 , w(msg)
264 , chunked(token_list{
265 msg.fields["Transfer-Encoding"]}.exists("chunked"))
266 , close(token_list{
267 msg.fields["Connection"]}.exists("close") ||
268 (msg.version < 11 && ! msg.fields.exists(
269 "Content-Length")))
270 {
271 }
272
273 void
274 init(error_code& ec)
275 {
276 w.init(ec);
277 if(ec)
278 return;
279
280 write_start_line(sb, msg);
281 write_fields(sb, msg.fields);
282 beast::write(sb, "\r\n");
283 }
284 };
285
286 template<class Stream, class Handler,
287 bool isRequest, class Body, class Fields>
288 class write_op
289 {
290 struct data
291 {
292 bool cont;
293 Stream& s;
294 // VFALCO How do we use handler_alloc in write_preparation?
295 write_preparation<
296 isRequest, Body, Fields> wp;
297 int state = 0;
298
299 data(Handler& handler, Stream& s_,
300 message<isRequest, Body, Fields> const& m_)
301 : cont(beast_asio_helpers::
302 is_continuation(handler))
303 , s(s_)
304 , wp(m_)
305 {
306 }
307 };
308
309 class writef0_lambda
310 {
311 write_op& self_;
312
313 public:
314 explicit
315 writef0_lambda(write_op& self)
316 : self_(self)
317 {
318 }
319
320 template<class ConstBufferSequence>
321 void operator()(ConstBufferSequence const& buffers) const
322 {
323 auto& d = *self_.d_;
324 // write header and body
325 if(d.wp.chunked)
326 boost::asio::async_write(d.s,
327 buffer_cat(d.wp.sb.data(),
328 chunk_encode(false, buffers)),
329 std::move(self_));
330 else
331 boost::asio::async_write(d.s,
332 buffer_cat(d.wp.sb.data(),
333 buffers), std::move(self_));
334 }
335 };
336
337 class writef_lambda
338 {
339 write_op& self_;
340
341 public:
342 explicit
343 writef_lambda(write_op& self)
344 : self_(self)
345 {
346 }
347
348 template<class ConstBufferSequence>
349 void operator()(ConstBufferSequence const& buffers) const
350 {
351 auto& d = *self_.d_;
352 // write body
353 if(d.wp.chunked)
354 boost::asio::async_write(d.s,
355 chunk_encode(false, buffers),
356 std::move(self_));
357 else
358 boost::asio::async_write(d.s,
359 buffers, std::move(self_));
360 }
361 };
362
363 handler_ptr<data, Handler> d_;
364
365 public:
366 write_op(write_op&&) = default;
367 write_op(write_op const&) = default;
368
369 template<class DeducedHandler, class... Args>
370 write_op(DeducedHandler&& h, Stream& s, Args&&... args)
371 : d_(std::forward<DeducedHandler>(h),
372 s, std::forward<Args>(args)...)
373 {
374 (*this)(error_code{}, 0, false);
375 }
376
377 void
378 operator()(error_code ec,
379 std::size_t bytes_transferred, bool again = true);
380
381 friend
382 void* asio_handler_allocate(
383 std::size_t size, write_op* op)
384 {
385 return beast_asio_helpers::
386 allocate(size, op->d_.handler());
387 }
388
389 friend
390 void asio_handler_deallocate(
391 void* p, std::size_t size, write_op* op)
392 {
393 return beast_asio_helpers::
394 deallocate(p, size, op->d_.handler());
395 }
396
397 friend
398 bool asio_handler_is_continuation(write_op* op)
399 {
400 return op->d_->cont;
401 }
402
403 template<class Function>
404 friend
405 void asio_handler_invoke(Function&& f, write_op* op)
406 {
407 return beast_asio_helpers::
408 invoke(f, op->d_.handler());
409 }
410 };
411
412 template<class Stream, class Handler,
413 bool isRequest, class Body, class Fields>
414 void
415 write_op<Stream, Handler, isRequest, Body, Fields>::
416 operator()(error_code ec, std::size_t, bool again)
417 {
418 auto& d = *d_;
419 d.cont = d.cont || again;
420 while(! ec && d.state != 99)
421 {
422 switch(d.state)
423 {
424 case 0:
425 {
426 d.wp.init(ec);
427 if(ec)
428 {
429 // call handler
430 d.state = 99;
431 d.s.get_io_service().post(bind_handler(
432 std::move(*this), ec, 0, false));
433 return;
434 }
435 d.state = 1;
436 break;
437 }
438
439 case 1:
440 {
441 auto const result =
442 d.wp.w.write(ec,
443 writef0_lambda{*this});
444 if(ec)
445 {
446 // call handler
447 d.state = 99;
448 d.s.get_io_service().post(bind_handler(
449 std::move(*this), ec, false));
450 return;
451 }
452 if(result)
453 d.state = d.wp.chunked ? 4 : 5;
454 else
455 d.state = 2;
456 return;
457 }
458
459 // sent header and body
460 case 2:
461 d.wp.sb.consume(d.wp.sb.size());
462 d.state = 3;
463 break;
464
465 case 3:
466 {
467 auto const result =
468 d.wp.w.write(ec,
469 writef_lambda{*this});
470 if(ec)
471 {
472 // call handler
473 d.state = 99;
474 break;
475 }
476 if(result)
477 d.state = d.wp.chunked ? 4 : 5;
478 else
479 d.state = 2;
480 return;
481 }
482
483 case 4:
484 // VFALCO Unfortunately the current interface to the
485 // Writer concept prevents us from coalescing the
486 // final body chunk with the final chunk delimiter.
487 //
488 // write final chunk
489 d.state = 5;
490 boost::asio::async_write(d.s,
491 chunk_encode_final(), std::move(*this));
492 return;
493
494 case 5:
495 if(d.wp.close)
496 {
497 // VFALCO TODO Decide on an error code
498 ec = boost::asio::error::eof;
499 }
500 d.state = 99;
501 break;
502 }
503 }
504 d_.invoke(ec);
505 }
506
507 template<class SyncWriteStream, class DynamicBuffer>
508 class writef0_lambda
509 {
510 DynamicBuffer const& sb_;
511 SyncWriteStream& stream_;
512 bool chunked_;
513 error_code& ec_;
514
515 public:
516 writef0_lambda(SyncWriteStream& stream,
517 DynamicBuffer const& sb, bool chunked, error_code& ec)
518 : sb_(sb)
519 , stream_(stream)
520 , chunked_(chunked)
521 , ec_(ec)
522 {
523 }
524
525 template<class ConstBufferSequence>
526 void operator()(ConstBufferSequence const& buffers) const
527 {
528 // write header and body
529 if(chunked_)
530 boost::asio::write(stream_, buffer_cat(
531 sb_.data(), chunk_encode(false, buffers)), ec_);
532 else
533 boost::asio::write(stream_, buffer_cat(
534 sb_.data(), buffers), ec_);
535 }
536 };
537
538 template<class SyncWriteStream>
539 class writef_lambda
540 {
541 SyncWriteStream& stream_;
542 bool chunked_;
543 error_code& ec_;
544
545 public:
546 writef_lambda(SyncWriteStream& stream,
547 bool chunked, error_code& ec)
548 : stream_(stream)
549 , chunked_(chunked)
550 , ec_(ec)
551 {
552 }
553
554 template<class ConstBufferSequence>
555 void operator()(ConstBufferSequence const& buffers) const
556 {
557 // write body
558 if(chunked_)
559 boost::asio::write(stream_,
560 chunk_encode(false, buffers), ec_);
561 else
562 boost::asio::write(stream_, buffers, ec_);
563 }
564 };
565
566 } // detail
567
568 template<class SyncWriteStream,
569 bool isRequest, class Body, class Fields>
570 void
571 write(SyncWriteStream& stream,
572 message<isRequest, Body, Fields> const& msg)
573 {
574 static_assert(is_SyncWriteStream<SyncWriteStream>::value,
575 "SyncWriteStream requirements not met");
576 static_assert(is_Body<Body>::value,
577 "Body requirements not met");
578 static_assert(has_writer<Body>::value,
579 "Body has no writer");
580 static_assert(is_Writer<typename Body::writer,
581 message<isRequest, Body, Fields>>::value,
582 "Writer requirements not met");
583 error_code ec;
584 write(stream, msg, ec);
585 if(ec)
586 throw system_error{ec};
587 }
588
589 template<class SyncWriteStream,
590 bool isRequest, class Body, class Fields>
591 void
592 write(SyncWriteStream& stream,
593 message<isRequest, Body, Fields> const& msg,
594 error_code& ec)
595 {
596 static_assert(is_SyncWriteStream<SyncWriteStream>::value,
597 "SyncWriteStream requirements not met");
598 static_assert(is_Body<Body>::value,
599 "Body requirements not met");
600 static_assert(has_writer<Body>::value,
601 "Body has no writer");
602 static_assert(is_Writer<typename Body::writer,
603 message<isRequest, Body, Fields>>::value,
604 "Writer requirements not met");
605 detail::write_preparation<isRequest, Body, Fields> wp(msg);
606 wp.init(ec);
607 if(ec)
608 return;
609 auto result = wp.w.write(
610 ec, detail::writef0_lambda<
611 SyncWriteStream, decltype(wp.sb)>{
612 stream, wp.sb, wp.chunked, ec});
613 if(ec)
614 return;
615 wp.sb.consume(wp.sb.size());
616 if(! result)
617 {
618 detail::writef_lambda<SyncWriteStream> wf{
619 stream, wp.chunked, ec};
620 for(;;)
621 {
622 result = wp.w.write(ec, wf);
623 if(ec)
624 return;
625 if(result)
626 break;
627 }
628 }
629 if(wp.chunked)
630 {
631 // VFALCO Unfortunately the current interface to the
632 // Writer concept prevents us from using coalescing the
633 // final body chunk with the final chunk delimiter.
634 //
635 // write final chunk
636 boost::asio::write(stream, chunk_encode_final(), ec);
637 if(ec)
638 return;
639 }
640 if(wp.close)
641 {
642 // VFALCO TODO Decide on an error code
643 ec = boost::asio::error::eof;
644 }
645 }
646
647 template<class AsyncWriteStream,
648 bool isRequest, class Body, class Fields,
649 class WriteHandler>
650 typename async_completion<
651 WriteHandler, void(error_code)>::result_type
652 async_write(AsyncWriteStream& stream,
653 message<isRequest, Body, Fields> const& msg,
654 WriteHandler&& handler)
655 {
656 static_assert(is_AsyncWriteStream<AsyncWriteStream>::value,
657 "AsyncWriteStream requirements not met");
658 static_assert(is_Body<Body>::value,
659 "Body requirements not met");
660 static_assert(has_writer<Body>::value,
661 "Body has no writer");
662 static_assert(is_Writer<typename Body::writer,
663 message<isRequest, Body, Fields>>::value,
664 "Writer requirements not met");
665 beast::async_completion<WriteHandler,
666 void(error_code)> completion{handler};
667 detail::write_op<AsyncWriteStream, decltype(completion.handler),
668 isRequest, Body, Fields>{completion.handler, stream, msg};
669 return completion.result.get();
670 }
671
672 //------------------------------------------------------------------------------
673
674 template<bool isRequest, class Fields>
675 std::ostream&
676 operator<<(std::ostream& os,
677 header<isRequest, Fields> const& msg)
678 {
679 beast::detail::sync_ostream oss{os};
680 error_code ec;
681 write(oss, msg, ec);
682 if(ec)
683 throw system_error{ec};
684 return os;
685 }
686
687 template<bool isRequest, class Body, class Fields>
688 std::ostream&
689 operator<<(std::ostream& os,
690 message<isRequest, Body, Fields> const& msg)
691 {
692 static_assert(is_Body<Body>::value,
693 "Body requirements not met");
694 static_assert(has_writer<Body>::value,
695 "Body has no writer");
696 static_assert(is_Writer<typename Body::writer,
697 message<isRequest, Body, Fields>>::value,
698 "Writer requirements not met");
699 beast::detail::sync_ostream oss{os};
700 error_code ec;
701 write(oss, msg, ec);
702 if(ec && ec != boost::asio::error::eof)
703 throw system_error{ec};
704 return os;
705 }
706
707 } // http
708 } // beast
709
710 #endif