include/boost/burl/message_writer.hpp

100.0% Lines (30/30) 84.4% List of functions (38/45) 75.0% Branches (6/8)
message_writer.hpp
f(x) Functions (45)
Function Calls Lines Branches Blocks
boost::burl::message_writer<boost::burl::detail::pooled_connection>::message_writer(boost::burl::detail::pooled_connection*, boost::burl::serializer*) :84 35x 100.0% 50.0% 60.0% boost::burl::message_writer<boost::burl::message_writer_test::flaky_write_stream>::message_writer(boost::burl::message_writer_test::flaky_write_stream*, boost::burl::serializer*) :84 2x 100.0% 50.0% 60.0% boost::burl::message_writer<boost::capy::test::stream>::message_writer(boost::capy::test::stream*, boost::burl::serializer*) :84 8x 100.0% 50.0% 60.0% boost::burl::message_writer<boost::capy::test::write_stream>::message_writer(boost::capy::test::write_stream*, boost::burl::serializer*) :84 12x 100.0% 50.0% 60.0% boost::burl::message_writer<boost::burl::detail::pooled_connection>::prepare(std::span<boost::capy::mutable_buffer, 18446744073709551615ul>) :99 0 0.0% – 0.0% boost::burl::message_writer<boost::capy::test::write_stream>::prepare(std::span<boost::capy::mutable_buffer, 18446744073709551615ul>) :99 3x 100.0% – 100.0% boost::burl::message_writer<boost::burl::detail::pooled_connection>::commit(unsigned long) :115 0 0.0% – 0.0% boost::burl::message_writer<boost::capy::test::write_stream>::commit(unsigned long) :115 2x 100.0% – 100.0% boost::burl::message_writer<boost::burl::detail::pooled_connection>::commit_eof(unsigned long) :127 0 0.0% – 0.0% boost::burl::message_writer<boost::capy::test::write_stream>::commit_eof(unsigned long) :127 1x 100.0% – 100.0% boost::burl::message_writer<boost::burl::detail::pooled_connection>::write_eof() :137 33x 100.0% – 100.0% boost::burl::message_writer<boost::burl::message_writer_test::flaky_write_stream>::write_eof() :137 1x 100.0% – 100.0% boost::burl::message_writer<boost::capy::test::stream>::write_eof() :137 4x 100.0% – 100.0% boost::burl::message_writer<boost::capy::test::write_stream>::write_eof() :137 4x 100.0% – 100.0% boost::burl::message_writer<boost::capy::test::write_stream>::write_header() :161 2x 100.0% – 100.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::burl::detail::pooled_connection>::write_some<std::span<boost::capy::const_buffer const, 18446744073709551615ul> >(std::span<boost::capy::const_buffer const, 18446744073709551615ul>) :180 0 0.0% – 0.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::capy::test::write_stream>::write_some<boost::capy::const_buffer>(boost::capy::const_buffer) :180 1x 100.0% – 100.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::capy::test::write_stream>::write_some<std::span<boost::capy::const_buffer const, 18446744073709551615ul> >(std::span<boost::capy::const_buffer const, 18446744073709551615ul>) :180 1x 100.0% – 100.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::burl::detail::pooled_connection>::write<std::span<boost::capy::const_buffer const, 18446744073709551615ul> >(std::span<boost::capy::const_buffer const, 18446744073709551615ul>) :197 0 0.0% – 0.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::burl::message_writer_test::flaky_write_stream>::write<boost::capy::const_buffer>(boost::capy::const_buffer) :197 2x 100.0% – 100.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::capy::test::stream>::write<boost::capy::const_buffer>(boost::capy::const_buffer) :197 8x 100.0% – 100.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::capy::test::write_stream>::write<boost::capy::const_buffer>(boost::capy::const_buffer) :197 2x 100.0% – 100.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::capy::test::write_stream>::write<std::span<boost::capy::const_buffer const, 18446744073709551615ul> >(std::span<boost::capy::const_buffer const, 18446744073709551615ul>) :197 1x 100.0% – 100.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::burl::detail::pooled_connection>::write_eof<std::span<boost::capy::const_buffer const, 18446744073709551615ul> >(std::span<boost::capy::const_buffer const, 18446744073709551615ul>) :217 2x 100.0% – 100.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::burl::message_writer_test::flaky_write_stream>::write_eof<boost::capy::const_buffer>(boost::capy::const_buffer) :217 17x 100.0% – 100.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::capy::test::write_stream>::write_eof<boost::capy::const_buffer>(boost::capy::const_buffer) :217 3x 100.0% – 100.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::capy::test::write_stream>::write_eof<std::array<boost::capy::const_buffer, 24ul> >(std::array<boost::capy::const_buffer, 24ul>) :217 2x 100.0% – 100.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::capy::test::write_stream>::write_eof<std::array<boost::capy::const_buffer, 2ul> >(std::array<boost::capy::const_buffer, 2ul>) :217 1x 100.0% – 100.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::capy::test::write_stream>::write_eof<std::span<boost::capy::const_buffer const, 18446744073709551615ul> >(std::span<boost::capy::const_buffer const, 18446744073709551615ul>) :217 1x 100.0% – 100.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::burl::detail::pooled_connection>::drive_<boost::capy::const_buffer>(boost::burl::detail::pooled_connection&, boost::burl::serializer&, boost::capy::const_buffer, bool) :252 33x 100.0% 100.0% 44.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::burl::detail::pooled_connection>::drive_<std::span<boost::capy::const_buffer const, 18446744073709551615ul> >(boost::burl::detail::pooled_connection&, boost::burl::serializer&, std::span<boost::capy::const_buffer const, 18446744073709551615ul>, bool) :252 2x 100.0% 100.0% 44.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::burl::message_writer_test::flaky_write_stream>::drive_<boost::capy::const_buffer>(boost::burl::message_writer_test::flaky_write_stream&, boost::burl::serializer&, boost::capy::const_buffer, bool) :252 20x 100.0% 100.0% 44.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::capy::test::stream>::drive_<boost::capy::const_buffer>(boost::capy::test::stream&, boost::burl::serializer&, boost::capy::const_buffer, bool) :252 12x 100.0% 100.0% 44.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::capy::test::write_stream>::drive_<boost::capy::const_buffer>(boost::capy::test::write_stream&, boost::burl::serializer&, boost::capy::const_buffer, bool) :252 14x 100.0% 100.0% 44.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::capy::test::write_stream>::drive_<std::array<boost::capy::const_buffer, 24ul> >(boost::capy::test::write_stream&, boost::burl::serializer&, std::array<boost::capy::const_buffer, 24ul>, bool) :252 2x 100.0% 100.0% 44.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::capy::test::write_stream>::drive_<std::array<boost::capy::const_buffer, 2ul> >(boost::capy::test::write_stream&, boost::burl::serializer&, std::array<boost::capy::const_buffer, 2ul>, bool) :252 1x 100.0% 100.0% 44.0% boost::capy::task<std::tuple<std::error_code, unsigned long> > boost::burl::message_writer<boost::capy::test::write_stream>::drive_<std::span<boost::capy::const_buffer const, 18446744073709551615ul> >(boost::capy::test::write_stream&, boost::burl::serializer&, std::span<boost::capy::const_buffer const, 18446744073709551615ul>, bool) :252 3x 100.0% 100.0% 44.0% boost::burl::message_writer<boost::burl::detail::pooled_connection>::drain_(boost::burl::detail::pooled_connection&, boost::burl::serializer&) :278 0 0.0% 0.0% 0.0% boost::burl::message_writer<boost::capy::test::write_stream>::drain_(boost::capy::test::write_stream&, boost::burl::serializer&) :278 3x 100.0% 100.0% 44.0% boost::burl::message_writer<boost::burl::detail::pooled_connection>::commit_(boost::burl::detail::pooled_connection&, boost::burl::serializer&, unsigned long) :289 0 0.0% 0.0% 0.0% boost::burl::message_writer<boost::capy::test::write_stream>::commit_(boost::capy::test::write_stream&, boost::burl::serializer&, unsigned long) :289 2x 100.0% 100.0% 44.0% boost::burl::message_writer<boost::burl::detail::pooled_connection>::commit_eof_(boost::burl::detail::pooled_connection&, boost::burl::serializer&, unsigned long) :303 33x 100.0% 100.0% 44.0% boost::burl::message_writer<boost::burl::message_writer_test::flaky_write_stream>::commit_eof_(boost::burl::message_writer_test::flaky_write_stream&, boost::burl::serializer&, unsigned long) :303 1x 100.0% 100.0% 44.0% boost::burl::message_writer<boost::capy::test::stream>::commit_eof_(boost::capy::test::stream&, boost::burl::serializer&, unsigned long) :303 4x 100.0% 100.0% 44.0% boost::burl::message_writer<boost::capy::test::write_stream>::commit_eof_(boost::capy::test::write_stream&, boost::burl::serializer&, unsigned long) :303 5x 100.0% 100.0% 44.0%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Mohammad Nejati
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/cppalliance/burl
8 //
9
10 #ifndef BOOST_BURL_MESSAGE_WRITER_HPP
11 #define BOOST_BURL_MESSAGE_WRITER_HPP
12
13 #include <boost/burl/serializer.hpp>
14
15 #include <boost/assert.hpp>
16 #include <boost/capy/buffers/consuming_buffers.hpp>
17 #include <boost/capy/concept/write_stream.hpp>
18 #include <boost/capy/io_task.hpp>
19
20 #include <cstddef>
21 #include <span>
22 #include <utility>
23
24 namespace boost
25 {
26 namespace burl
27 {
28
29 /** Drives a @ref serializer over a stream.
30
31 A writer binds a stream to a serializer. It
32 holds only pointers to both, which must outlive
33 it.
34
35 @par Example
36 @code
37 serializer sr( cfg );
38 message_writer writer( &sock, &sr );
39
40 sr.start( &head );
41
42 auto [ec, n] = co_await writer.write_eof(
43 capy::make_buffer( body ));
44 @endcode
45
46 Every operation writes the header first if it
47 has not gone out yet. Small writes coalesce in
48 the serializer's staging buffer; large ones are
49 spliced into the same gathered write as the
50 framing, without copying.
51
52 If an operation is cancelled mid-write, the
53 serializer's accounting stays true to the wire:
54 the completion counts already reported cover
55 exactly the consumed octets, and re-issuing a
56 write of the unconsumed remainder resumes the
57 message.
58
59 This type satisfies @ref capy::WriteStream,
60 @ref http::WriteSink, and @ref http::BufferSink,
61 all over the octets of the message body.
62
63 @tparam S A type satisfying @ref capy::WriteStream.
64
65 @see @ref serializer.
66 */
67 template<capy::WriteStream S>
68 class message_writer
69 {
70 S* s_;
71 serializer* sr_;
72
73 public:
74 /** Constructor.
75
76 @par Preconditions
77 Neither pointer is null, and both objects
78 outlive the writer.
79
80 @param stream The stream to write to.
81
82 @param sr The serializer to drive.
83 */
84 57x message_writer(S* stream, serializer* sr) noexcept
85 57x : s_(stream)
86 57x , sr_(sr)
87 {
88
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
57x BOOST_ASSERT(s_ != nullptr);
89
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
57x BOOST_ASSERT(sr_ != nullptr);
90 57x }
91
92 /** Return writable staging memory.
93
94 @param dest The descriptors to fill.
95
96 @see @ref serializer::prepare.
97 */
98 std::span<capy::mutable_buffer>
99 3x prepare(std::span<capy::mutable_buffer> dest)
100 {
101 3x return sr_->prepare(dest);
102 }
103
104 /** Asynchronously commit staged octets.
105
106 Reports octets written into memory obtained
107 from @ref prepare, flushing the staging
108 buffer when it runs low.
109
110 @param n The number of octets written.
111
112 @return An awaitable yielding `(error_code)`.
113 */
114 capy::io_task<>
115 2x commit(std::size_t n)
116 {
117 2x return commit_(*s_, *sr_, n);
118 }
119
120 /** Asynchronously commit final octets and end the body.
121
122 @param n The number of octets written.
123
124 @return An awaitable yielding `(error_code)`.
125 */
126 capy::io_task<>
127 1x commit_eof(std::size_t n)
128 {
129 1x return commit_eof_(*s_, *sr_, n);
130 }
131
132 /** Asynchronously end the body with no more octets.
133
134 @return An awaitable yielding `(error_code)`.
135 */
136 capy::io_task<>
137 42x write_eof()
138 {
139 42x return commit_eof_(*s_, *sr_, 0);
140 }
141
142 /** Asynchronously write the message header.
143
144 Flushes pending output without ending the
145 body. This sends the header, if needed, and
146 any staged data after it.
147
148 Mainly used for `Expect: 100-continue`,
149 where the server must receive the header
150 before the body is generated.
151
152 Once the header is sent, the framing and
153 encoding are fixed. Small bodies can no
154 longer switch to `Content-Length` or
155 identity encoding.
156
157 @return An awaitable yielding
158 `(error_code)`.
159 */
160 capy::io_task<>
161 2x write_header()
162 {
163 2x return drain_(*s_, *sr_);
164 }
165
166 /** Asynchronously write body octets.
167
168 Writes at least one octet of `buffers`
169 unless it is empty; small inputs coalesce
170 without I/O. May consume the input only
171 partially.
172
173 @param buffers The octets to write.
174
175 @return An awaitable yielding
176 `(error_code,std::size_t)`.
177 */
178 template<capy::ConstBufferSequence CB>
179 capy::io_task<std::size_t>
180 2x write_some(CB buffers)
181 {
182 2x return drive_(*s_, *sr_, std::move(buffers), true);
183 }
184
185 /** Asynchronously write a whole buffer sequence.
186
187 Writes until `buffers` is fully consumed or
188 an error occurs.
189
190 @param buffers The octets to write.
191
192 @return An awaitable yielding
193 `(error_code,std::size_t)`.
194 */
195 template<capy::ConstBufferSequence CB>
196 capy::io_task<std::size_t>
197 13x write(CB buffers)
198 {
199 13x return drive_(*s_, *sr_, std::move(buffers), true);
200 }
201
202 /** Asynchronously write final octets and end the body.
203
204 Writes all of `buffers` and completes the
205 message. The whole body is supplied up
206 front, which is what lets a small body of
207 undeclared size go out with
208 `Content-Length` framing.
209
210 @param buffers The final octets to write.
211
212 @return An awaitable yielding
213 `(error_code,std::size_t)`.
214 */
215 template<capy::ConstBufferSequence CB>
216 capy::io_task<std::size_t>
217 26x write_eof(CB buffers)
218 {
219 26x return drive_(*s_, *sr_, std::move(buffers), false);
220 }
221
222 private:
223 template<capy::ConstBufferSequence CB>
224 static capy::io_task<std::size_t>
225 drive_(
226 S& stream,
227 serializer& sr,
228 CB buffers,
229 bool more);
230
231 static capy::io_task<>
232 drain_(S& stream, serializer& sr);
233
234 static capy::io_task<>
235 commit_(
236 S& stream,
237 serializer& sr,
238 std::size_t n);
239
240 static capy::io_task<>
241 commit_eof_(
242 S& stream,
243 serializer& sr,
244 std::size_t n);
245 };
246
247 //------------------------------------------------
248
249 template<capy::WriteStream S>
250 template<capy::ConstBufferSequence CB>
251 capy::io_task<std::size_t>
252
1/1
✓ Branch 1 taken 87 times.
87x message_writer<S>::
253 drive_(
254 S& stream,
255 serializer& sr,
256 CB buffers,
257 bool more)
258 {
259 capy::consuming_buffers cb(buffers);
260 capy::const_buffer dest[16];
261 std::size_t total = 0;
262 for(;;)
263 {
264 auto const fr = sr.frame(dest, cb.data(), more);
265 if(fr.has_error())
266 co_return { fr.error(), total };
267 auto [ec, n] = co_await stream.write_some(*fr);
268 auto const k = sr.consume(n);
269 cb.consume(k);
270 total += k;
271 if(ec || n == 0)
272 co_return { ec, total };
273 }
274 174x }
275
276 template<capy::WriteStream S>
277 capy::io_task<>
278
1/1
✓ Branch 1 taken 3 times.
3x message_writer<S>::
279 drain_(S& stream, serializer& sr)
280 {
281 auto [ec, n] = co_await drive_(
282 stream, sr, capy::const_buffer{}, true);
283 (void)n;
284 co_return { ec };
285 6x }
286
287 template<capy::WriteStream S>
288 capy::io_task<>
289
1/1
✓ Branch 1 taken 2 times.
2x message_writer<S>::
290 commit_(
291 S& stream,
292 serializer& sr,
293 std::size_t n)
294 {
295 sr.commit(n);
296 if(!sr.should_drain())
297 co_return {};
298 co_return co_await drain_(stream, sr);
299 4x }
300
301 template<capy::WriteStream S>
302 capy::io_task<>
303
1/1
✓ Branch 1 taken 43 times.
43x message_writer<S>::
304 commit_eof_(
305 S& stream,
306 serializer& sr,
307 std::size_t n)
308 {
309 sr.commit(n);
310 auto [ec, k] = co_await drive_(
311 stream, sr, capy::const_buffer{}, false);
312 (void)k;
313 co_return { ec };
314 86x }
315
316 } // namespace burl
317 } // namespace boost
318
319 #endif
320