2 // Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
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)
8 #ifndef BEAST_HTTP_IMPL_WRITE_IPP
9 #define BEAST_HTTP_IMPL_WRITE_IPP
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>
27 #include <type_traits>
34 template<class DynamicBuffer, class Fields>
36 write_start_line(DynamicBuffer& dynabuf,
37 header<true, Fields> const& msg)
39 BOOST_ASSERT(msg.version == 10 || msg.version == 11);
40 write(dynabuf, msg.method);
42 write(dynabuf, msg.url);
46 write(dynabuf, " HTTP/1.0\r\n");
49 write(dynabuf, " HTTP/1.1\r\n");
54 template<class DynamicBuffer, class Fields>
56 write_start_line(DynamicBuffer& dynabuf,
57 header<false, Fields> const& msg)
59 BOOST_ASSERT(msg.version == 10 || msg.version == 11);
63 write(dynabuf, "HTTP/1.0 ");
66 write(dynabuf, "HTTP/1.1 ");
69 write(dynabuf, msg.status);
71 write(dynabuf, msg.reason);
72 write(dynabuf, "\r\n");
75 template<class DynamicBuffer, class FieldSequence>
77 write_fields(DynamicBuffer& dynabuf, FieldSequence const& fields)
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)
85 write(dynabuf, field.name());
87 write(dynabuf, field.value());
88 write(dynabuf, "\r\n");
94 //------------------------------------------------------------------------------
98 template<class Stream, class Handler>
99 class write_streambuf_op
108 data(Handler& handler, Stream& s_,
110 : cont(beast_asio_helpers::
111 is_continuation(handler))
118 handler_ptr<data, Handler> d_;
121 write_streambuf_op(write_streambuf_op&&) = default;
122 write_streambuf_op(write_streambuf_op const&) = default;
124 template<class DeducedHandler, class... Args>
125 write_streambuf_op(DeducedHandler&& h, Stream& s,
127 : d_(std::forward<DeducedHandler>(h),
128 s, std::forward<Args>(args)...)
130 (*this)(error_code{}, 0, false);
134 operator()(error_code ec,
135 std::size_t bytes_transferred, bool again = true);
138 void* asio_handler_allocate(
139 std::size_t size, write_streambuf_op* op)
141 return beast_asio_helpers::
142 allocate(size, op->d_.handler());
146 void asio_handler_deallocate(
147 void* p, std::size_t size, write_streambuf_op* op)
149 return beast_asio_helpers::
150 deallocate(p, size, op->d_.handler());
154 bool asio_handler_is_continuation(write_streambuf_op* op)
159 template<class Function>
161 void asio_handler_invoke(Function&& f, write_streambuf_op* op)
163 return beast_asio_helpers::
164 invoke(f, op->d_.handler());
168 template<class Stream, class Handler>
170 write_streambuf_op<Stream, Handler>::
171 operator()(error_code ec, std::size_t, bool again)
174 d.cont = d.cont || again;
175 while(! ec && d.state != 99)
182 boost::asio::async_write(d.s,
183 d.sb.data(), std::move(*this));
193 template<class SyncWriteStream,
194 bool isRequest, class Fields>
196 write(SyncWriteStream& stream,
197 header<isRequest, Fields> const& msg)
199 static_assert(is_SyncWriteStream<SyncWriteStream>::value,
200 "SyncWriteStream requirements not met");
202 write(stream, msg, ec);
204 throw system_error{ec};
207 template<class SyncWriteStream,
208 bool isRequest, class Fields>
210 write(SyncWriteStream& stream,
211 header<isRequest, Fields> const& msg,
214 static_assert(is_SyncWriteStream<SyncWriteStream>::value,
215 "SyncWriteStream requirements not met");
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);
223 template<class AsyncWriteStream,
224 bool isRequest, class Fields,
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)
232 static_assert(is_AsyncWriteStream<AsyncWriteStream>::value,
233 "AsyncWriteStream requirements not met");
234 beast::async_completion<WriteHandler,
235 void(error_code)> completion{handler};
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();
246 //------------------------------------------------------------------------------
250 template<bool isRequest, class Body, class Fields>
251 struct write_preparation
253 message<isRequest, Body, Fields> const& msg;
254 typename Body::writer w;
261 message<isRequest, Body, Fields> const& msg_)
264 , chunked(token_list{
265 msg.fields["Transfer-Encoding"]}.exists("chunked"))
267 msg.fields["Connection"]}.exists("close") ||
268 (msg.version < 11 && ! msg.fields.exists(
280 write_start_line(sb, msg);
281 write_fields(sb, msg.fields);
282 beast::write(sb, "\r\n");
286 template<class Stream, class Handler,
287 bool isRequest, class Body, class Fields>
294 // VFALCO How do we use handler_alloc in write_preparation?
296 isRequest, Body, Fields> wp;
299 data(Handler& handler, Stream& s_,
300 message<isRequest, Body, Fields> const& m_)
301 : cont(beast_asio_helpers::
302 is_continuation(handler))
315 writef0_lambda(write_op& self)
320 template<class ConstBufferSequence>
321 void operator()(ConstBufferSequence const& buffers) const
324 // write header and body
326 boost::asio::async_write(d.s,
327 buffer_cat(d.wp.sb.data(),
328 chunk_encode(false, buffers)),
331 boost::asio::async_write(d.s,
332 buffer_cat(d.wp.sb.data(),
333 buffers), std::move(self_));
343 writef_lambda(write_op& self)
348 template<class ConstBufferSequence>
349 void operator()(ConstBufferSequence const& buffers) const
354 boost::asio::async_write(d.s,
355 chunk_encode(false, buffers),
358 boost::asio::async_write(d.s,
359 buffers, std::move(self_));
363 handler_ptr<data, Handler> d_;
366 write_op(write_op&&) = default;
367 write_op(write_op const&) = default;
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)...)
374 (*this)(error_code{}, 0, false);
378 operator()(error_code ec,
379 std::size_t bytes_transferred, bool again = true);
382 void* asio_handler_allocate(
383 std::size_t size, write_op* op)
385 return beast_asio_helpers::
386 allocate(size, op->d_.handler());
390 void asio_handler_deallocate(
391 void* p, std::size_t size, write_op* op)
393 return beast_asio_helpers::
394 deallocate(p, size, op->d_.handler());
398 bool asio_handler_is_continuation(write_op* op)
403 template<class Function>
405 void asio_handler_invoke(Function&& f, write_op* op)
407 return beast_asio_helpers::
408 invoke(f, op->d_.handler());
412 template<class Stream, class Handler,
413 bool isRequest, class Body, class Fields>
415 write_op<Stream, Handler, isRequest, Body, Fields>::
416 operator()(error_code ec, std::size_t, bool again)
419 d.cont = d.cont || again;
420 while(! ec && d.state != 99)
431 d.s.get_io_service().post(bind_handler(
432 std::move(*this), ec, 0, false));
443 writef0_lambda{*this});
448 d.s.get_io_service().post(bind_handler(
449 std::move(*this), ec, false));
453 d.state = d.wp.chunked ? 4 : 5;
459 // sent header and body
461 d.wp.sb.consume(d.wp.sb.size());
469 writef_lambda{*this});
477 d.state = d.wp.chunked ? 4 : 5;
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.
490 boost::asio::async_write(d.s,
491 chunk_encode_final(), std::move(*this));
497 // VFALCO TODO Decide on an error code
498 ec = boost::asio::error::eof;
507 template<class SyncWriteStream, class DynamicBuffer>
510 DynamicBuffer const& sb_;
511 SyncWriteStream& stream_;
516 writef0_lambda(SyncWriteStream& stream,
517 DynamicBuffer const& sb, bool chunked, error_code& ec)
525 template<class ConstBufferSequence>
526 void operator()(ConstBufferSequence const& buffers) const
528 // write header and body
530 boost::asio::write(stream_, buffer_cat(
531 sb_.data(), chunk_encode(false, buffers)), ec_);
533 boost::asio::write(stream_, buffer_cat(
534 sb_.data(), buffers), ec_);
538 template<class SyncWriteStream>
541 SyncWriteStream& stream_;
546 writef_lambda(SyncWriteStream& stream,
547 bool chunked, error_code& ec)
554 template<class ConstBufferSequence>
555 void operator()(ConstBufferSequence const& buffers) const
559 boost::asio::write(stream_,
560 chunk_encode(false, buffers), ec_);
562 boost::asio::write(stream_, buffers, ec_);
568 template<class SyncWriteStream,
569 bool isRequest, class Body, class Fields>
571 write(SyncWriteStream& stream,
572 message<isRequest, Body, Fields> const& msg)
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");
584 write(stream, msg, ec);
586 throw system_error{ec};
589 template<class SyncWriteStream,
590 bool isRequest, class Body, class Fields>
592 write(SyncWriteStream& stream,
593 message<isRequest, Body, Fields> const& msg,
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);
609 auto result = wp.w.write(
610 ec, detail::writef0_lambda<
611 SyncWriteStream, decltype(wp.sb)>{
612 stream, wp.sb, wp.chunked, ec});
615 wp.sb.consume(wp.sb.size());
618 detail::writef_lambda<SyncWriteStream> wf{
619 stream, wp.chunked, ec};
622 result = wp.w.write(ec, wf);
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.
636 boost::asio::write(stream, chunk_encode_final(), ec);
642 // VFALCO TODO Decide on an error code
643 ec = boost::asio::error::eof;
647 template<class AsyncWriteStream,
648 bool isRequest, class Body, class Fields,
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)
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();
672 //------------------------------------------------------------------------------
674 template<bool isRequest, class Fields>
676 operator<<(std::ostream& os,
677 header<isRequest, Fields> const& msg)
679 beast::detail::sync_ostream oss{os};
683 throw system_error{ec};
687 template<bool isRequest, class Body, class Fields>
689 operator<<(std::ostream& os,
690 message<isRequest, Body, Fields> const& msg)
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};
702 if(ec && ec != boost::asio::error::eof)
703 throw system_error{ec};