Line | Branch | Exec | Source |
---|---|---|---|
1 | // | ||
2 | // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.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/CPPAlliance/http_proto | ||
8 | // | ||
9 | |||
10 | #ifndef BOOST_HTTP_PROTO_IMPL_SERIALIZER_IPP | ||
11 | #define BOOST_HTTP_PROTO_IMPL_SERIALIZER_IPP | ||
12 | |||
13 | #include <boost/http_proto/serializer.hpp> | ||
14 | #include <boost/http_proto/detail/except.hpp> | ||
15 | #include <boost/buffers/buffer_copy.hpp> | ||
16 | #include <boost/buffers/buffer_size.hpp> | ||
17 | #include <boost/core/ignore_unused.hpp> | ||
18 | #include <stddef.h> | ||
19 | |||
20 | namespace boost { | ||
21 | namespace http_proto { | ||
22 | |||
23 | //------------------------------------------------ | ||
24 | |||
25 | void | ||
26 | ✗ | consume_buffers( | |
27 | buffers::const_buffer*& p, | ||
28 | std::size_t& n, | ||
29 | std::size_t bytes) | ||
30 | { | ||
31 | ✗ | while(n > 0) | |
32 | { | ||
33 | ✗ | if(bytes < p->size()) | |
34 | { | ||
35 | ✗ | *p += bytes; | |
36 | ✗ | return; | |
37 | } | ||
38 | ✗ | bytes -= p->size(); | |
39 | ✗ | ++p; | |
40 | ✗ | --n; | |
41 | } | ||
42 | |||
43 | // Precondition violation | ||
44 | ✗ | if(bytes > 0) | |
45 | ✗ | detail::throw_invalid_argument(); | |
46 | } | ||
47 | |||
48 | template<class MutableBuffers> | ||
49 | void | ||
50 | 6 | write_chunk_header( | |
51 | MutableBuffers const& dest0, | ||
52 | std::size_t size) noexcept | ||
53 | { | ||
54 | static constexpr char hexdig[] = | ||
55 | "0123456789ABCDEF"; | ||
56 | char buf[18]; | ||
57 | 6 | auto p = buf + 16; | |
58 |
2/2✓ Branch 0 taken 48 times.
✓ Branch 1 taken 3 times.
|
102 | for(std::size_t i = 16; i--;) |
59 | { | ||
60 | 96 | *--p = hexdig[size & 0xf]; | |
61 | 96 | size >>= 4; | |
62 | } | ||
63 | 6 | buf[16] = '\r'; | |
64 | 6 | buf[17] = '\n'; | |
65 | 6 | auto n = buffers::buffer_copy( | |
66 | dest0, | ||
67 | buffers::const_buffer( | ||
68 | buf, sizeof(buf))); | ||
69 | ignore_unused(n); | ||
70 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
6 | BOOST_ASSERT(n == 18); |
71 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
6 | BOOST_ASSERT( |
72 | buffers::buffer_size(dest0) == n); | ||
73 | 6 | } | |
74 | |||
75 | //------------------------------------------------ | ||
76 | |||
77 | 12 | serializer:: | |
78 | 12 | ~serializer() | |
79 | { | ||
80 | 12 | } | |
81 | |||
82 | 10 | serializer:: | |
83 | 10 | serializer() | |
84 | 10 | : serializer(65536) | |
85 | { | ||
86 | 10 | } | |
87 | |||
88 | serializer:: | ||
89 | serializer( | ||
90 | serializer&&) noexcept = default; | ||
91 | |||
92 | 12 | serializer:: | |
93 | serializer( | ||
94 | 12 | std::size_t buffer_size) | |
95 | 12 | : ws_(buffer_size) | |
96 | { | ||
97 | 12 | } | |
98 | |||
99 | void | ||
100 | ✗ | serializer:: | |
101 | reset() noexcept | ||
102 | { | ||
103 | ✗ | } | |
104 | |||
105 | //------------------------------------------------ | ||
106 | |||
107 | auto | ||
108 | 14 | serializer:: | |
109 | prepare() -> | ||
110 | system::result< | ||
111 | const_buffers_type> | ||
112 | { | ||
113 | // Precondition violation | ||
114 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | if(is_done_) |
115 | ✗ | detail::throw_logic_error(); | |
116 | |||
117 | // Expect: 100-continue | ||
118 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 10 times.
|
14 | if(is_expect_continue_) |
119 | { | ||
120 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
|
4 | if(out_.data() == hp_) |
121 | 2 | return const_buffers_type(hp_, 1); | |
122 | 2 | is_expect_continue_ = false; | |
123 | 2 | BOOST_HTTP_PROTO_RETURN_EC( | |
124 | error::expect_100_continue); | ||
125 | } | ||
126 | |||
127 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 7 times.
|
10 | if(st_ == style::empty) |
128 | { | ||
129 | 9 | return const_buffers_type( | |
130 | 3 | out_.data(), | |
131 | 3 | out_.size()); | |
132 | } | ||
133 | |||
134 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
|
7 | if(st_ == style::buffers) |
135 | { | ||
136 | 9 | return const_buffers_type( | |
137 | 3 | out_.data(), | |
138 | 3 | out_.size()); | |
139 | } | ||
140 | |||
141 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | if(st_ == style::source) |
142 | { | ||
143 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
|
4 | if(! is_chunked_) |
144 | { | ||
145 | 3 | auto rv = src_->read( | |
146 | ✗ | tmp0_.prepare( | |
147 | 3 | tmp0_.capacity() - | |
148 |
2/4✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
|
3 | tmp0_.size())); |
149 | 3 | tmp0_.commit(rv.bytes); | |
150 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
3 | if(rv.ec.failed()) |
151 | ✗ | return rv.ec; | |
152 | 3 | more_ = ! rv.finished; | |
153 | } | ||
154 | else | ||
155 | { | ||
156 | 1 | if((tmp0_.capacity() - | |
157 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | tmp0_.size()) > |
158 | chunked_overhead_) | ||
159 | { | ||
160 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | auto dest = tmp0_.prepare(18); |
161 | 1 | write_chunk_header(dest, 0); | |
162 | 1 | tmp0_.commit(18); | |
163 | 1 | auto rv = src_->read( | |
164 | ✗ | tmp0_.prepare( | |
165 | 1 | tmp0_.capacity() - | |
166 | 2 - // CRLF | ||
167 | 1 | 5 - // final chunk | |
168 |
2/4✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
1 | tmp0_.size())); |
169 | 1 | tmp0_.commit(rv.bytes); | |
170 | // VFALCO FIXME! | ||
171 | //if(rv.bytes == 0) | ||
172 | //tmp0_.uncommit(18); // undo | ||
173 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | if(rv.ec.failed()) |
174 | ✗ | return rv.ec; | |
175 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if(rv.bytes > 0) |
176 | { | ||
177 | // rewrite with correct size | ||
178 | 1 | write_chunk_header( | |
179 | dest, rv.bytes); | ||
180 | // terminate chunk | ||
181 | 1 | tmp0_.commit( | |
182 | buffers::buffer_copy( | ||
183 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | tmp0_.prepare(2), |
184 | 2 | buffers::const_buffer( | |
185 | "\r\n", 2))); | ||
186 | } | ||
187 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if(rv.finished) |
188 | { | ||
189 | 1 | tmp0_.commit( | |
190 | buffers::buffer_copy( | ||
191 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | tmp0_.prepare(5), |
192 | 2 | buffers::const_buffer( | |
193 | "0\r\n\r\n", 5))); | ||
194 | } | ||
195 | 1 | more_ = ! rv.finished; | |
196 | } | ||
197 | } | ||
198 | |||
199 | 4 | std::size_t n = 0; | |
200 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 times.
|
4 | if(out_.data() == hp_) |
201 | 3 | ++n; | |
202 |
2/2✓ Branch 3 taken 8 times.
✓ Branch 4 taken 4 times.
|
12 | for(buffers::const_buffer const& b : tmp0_.data()) |
203 | 8 | out_[n++] = b; | |
204 | |||
205 | 12 | return const_buffers_type( | |
206 | 4 | out_.data(), | |
207 | 4 | out_.size()); | |
208 | } | ||
209 | |||
210 | ✗ | if(st_ == style::stream) | |
211 | { | ||
212 | ✗ | std::size_t n = 0; | |
213 | ✗ | if(out_.data() == hp_) | |
214 | ✗ | ++n; | |
215 | ✗ | if(tmp0_.size() == 0 && more_) | |
216 | { | ||
217 | ✗ | BOOST_HTTP_PROTO_RETURN_EC( | |
218 | error::need_data); | ||
219 | } | ||
220 | ✗ | for(buffers::const_buffer const& b : tmp0_.data()) | |
221 | ✗ | out_[n++] = b; | |
222 | |||
223 | ✗ | return const_buffers_type( | |
224 | ✗ | out_.data(), | |
225 | ✗ | out_.size()); | |
226 | } | ||
227 | |||
228 | // should never get here | ||
229 | ✗ | detail::throw_logic_error(); | |
230 | } | ||
231 | |||
232 | void | ||
233 | 12 | serializer:: | |
234 | consume( | ||
235 | std::size_t n) | ||
236 | { | ||
237 | // Precondition violation | ||
238 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if(is_done_) |
239 | ✗ | detail::throw_logic_error(); | |
240 | |||
241 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 10 times.
|
12 | if(is_expect_continue_) |
242 | { | ||
243 | // Cannot consume more than | ||
244 | // the header on 100-continue | ||
245 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
2 | if(n > hp_->size()) |
246 | ✗ | detail::throw_invalid_argument(); | |
247 | |||
248 | 2 | out_.consume(n); | |
249 | 2 | return; | |
250 | } | ||
251 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 2 taken 2 times.
|
10 | else if(out_.data() == hp_) |
252 | { | ||
253 | // consume header | ||
254 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
|
8 | if(n < hp_->size()) |
255 | { | ||
256 | ✗ | out_.consume(n); | |
257 | ✗ | return; | |
258 | } | ||
259 | 8 | n -= hp_->size(); | |
260 | 8 | out_.consume(hp_->size()); | |
261 | } | ||
262 | |||
263 |
3/3✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 4 times.
|
10 | switch(st_) |
264 | { | ||
265 | 3 | default: | |
266 | case style::empty: | ||
267 | 3 | out_.consume(n); | |
268 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | if(out_.empty()) |
269 | 3 | is_done_ = true; | |
270 | 3 | return; | |
271 | |||
272 | 3 | case style::buffers: | |
273 | 3 | out_.consume(n); | |
274 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | if(out_.empty()) |
275 | 3 | is_done_ = true; | |
276 | 3 | return; | |
277 | |||
278 | 4 | case style::source: | |
279 | case style::stream: | ||
280 | 4 | tmp0_.consume(n); | |
281 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
8 | if( tmp0_.size() == 0 && |
282 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | ! more_) |
283 | 4 | is_done_ = true; | |
284 | 4 | return; | |
285 | } | ||
286 | } | ||
287 | |||
288 | //------------------------------------------------ | ||
289 | |||
290 | void | ||
291 | 14 | serializer:: | |
292 | copy( | ||
293 | buffers::const_buffer* dest, | ||
294 | buffers::const_buffer const* src, | ||
295 | std::size_t n) noexcept | ||
296 | { | ||
297 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
|
14 | while(n--) |
298 | 7 | *dest++ = *src++; | |
299 | 7 | } | |
300 | |||
301 | void | ||
302 | 17 | serializer:: | |
303 | start_init( | ||
304 | message_view_base const& m) | ||
305 | { | ||
306 | 17 | ws_.clear(); | |
307 | |||
308 | // VFALCO what do we do with | ||
309 | // metadata error code failures? | ||
310 | // m.ph_->md.maybe_throw(); | ||
311 | |||
312 | 17 | is_done_ = false; | |
313 | |||
314 | 17 | is_expect_continue_ = | |
315 | 17 | m.ph_->md.expect.is_100_continue; | |
316 | |||
317 | // Transfer-Encoding | ||
318 | { | ||
319 | 17 | auto const& te = | |
320 | 17 | m.ph_->md.transfer_encoding; | |
321 | 17 | is_chunked_ = te.is_chunked; | |
322 | } | ||
323 | 17 | } | |
324 | |||
325 | void | ||
326 | 4 | serializer:: | |
327 | start_empty( | ||
328 | message_view_base const& m) | ||
329 | { | ||
330 | 4 | start_init(m); | |
331 | |||
332 | 4 | st_ = style::empty; | |
333 | |||
334 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
|
4 | if(! is_chunked_) |
335 | { | ||
336 | out_ = make_array( | ||
337 | 3 | 1); // header | |
338 | } | ||
339 | else | ||
340 | { | ||
341 | out_ = make_array( | ||
342 | 1 + // header | ||
343 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | 1); // final chunk |
344 | |||
345 | // Buffer is too small | ||
346 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | if(ws_.size() < 5) |
347 | ✗ | detail::throw_length_error(); | |
348 | |||
349 | buffers::mutable_buffer dest( | ||
350 | 1 | ws_.data(), 5); | |
351 | 1 | buffers::buffer_copy( | |
352 | dest, | ||
353 | 1 | buffers::const_buffer( | |
354 | "0\r\n\r\n", 5)); | ||
355 | 1 | out_[1] = dest; | |
356 | } | ||
357 | |||
358 | 4 | hp_ = &out_[0]; | |
359 | 4 | *hp_ = { m.ph_->cbuf, m.ph_->size }; | |
360 | 4 | } | |
361 | |||
362 | void | ||
363 | 7 | serializer:: | |
364 | start_buffers( | ||
365 | message_view_base const& m) | ||
366 | { | ||
367 | 7 | st_ = style::buffers; | |
368 | |||
369 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
|
7 | if(! is_chunked_) |
370 | { | ||
371 | //if(! cod_) | ||
372 | { | ||
373 | out_ = make_array( | ||
374 | 1 + // header | ||
375 | 6 | buf_.size()); // body | |
376 | 12 | copy(&out_[1], | |
377 | 6 | buf_.data(), buf_.size()); | |
378 | } | ||
379 | #if 0 | ||
380 | else | ||
381 | { | ||
382 | out_ = make_array( | ||
383 | 1 + // header | ||
384 | 2); // tmp1 | ||
385 | } | ||
386 | #endif | ||
387 | } | ||
388 | else | ||
389 | { | ||
390 | //if(! cod_) | ||
391 | { | ||
392 | out_ = make_array( | ||
393 | 1 + // header | ||
394 | 1 + // chunk size | ||
395 | 1 | buf_.size() + // body | |
396 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | 1); // final chunk |
397 | 2 | copy(&out_[2], | |
398 | 1 | buf_.data(), buf_.size()); | |
399 | |||
400 | // Buffer is too small | ||
401 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | if(ws_.size() < 18 + 7) |
402 | ✗ | detail::throw_length_error(); | |
403 | 1 | buffers::mutable_buffer s1(ws_.data(), 18); | |
404 | 1 | buffers::mutable_buffer s2(ws_.data(), 18 + 7); | |
405 | 1 | s2 += 18; // VFALCO HACK | |
406 | 1 | write_chunk_header( | |
407 | s1, | ||
408 | 1 | buffers::buffer_size(buf_)); | |
409 | 1 | buffers::buffer_copy(s2, buffers::const_buffer( | |
410 | "\r\n" | ||
411 | "0\r\n" | ||
412 | "\r\n", 7)); | ||
413 | 1 | out_[1] = s1; | |
414 | 1 | out_[out_.size() - 1] = s2; | |
415 | } | ||
416 | #if 0 | ||
417 | else | ||
418 | { | ||
419 | out_ = make_array( | ||
420 | 1 + // header | ||
421 | 2); // tmp1 | ||
422 | } | ||
423 | #endif | ||
424 | } | ||
425 | |||
426 | 7 | hp_ = &out_[0]; | |
427 | 7 | *hp_ = { m.ph_->cbuf, m.ph_->size }; | |
428 | 7 | } | |
429 | |||
430 | void | ||
431 | 6 | serializer:: | |
432 | start_source( | ||
433 | message_view_base const& m, | ||
434 | source* src) | ||
435 | { | ||
436 | 6 | st_ = style::source; | |
437 | 6 | src_ = src; | |
438 | out_ = make_array( | ||
439 | 1 + // header | ||
440 | 6 | 2); // tmp | |
441 | //if(! cod_) | ||
442 | { | ||
443 | buffered_base::allocator a( | ||
444 | 6 | ws_.data(), ws_.size()/2, false); | |
445 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | src->init(a); |
446 |
1/2✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
6 | ws_.reserve_front(a.size_used()); |
447 | |||
448 | 6 | tmp0_ = { ws_.data(), ws_.size() }; | |
449 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
|
6 | if(tmp0_.capacity() < |
450 | 18 + // chunk size | ||
451 | 1 + // body (1 byte) | ||
452 | 2 + // CRLF | ||
453 | 5) // final chunk | ||
454 | ✗ | detail::throw_length_error(); | |
455 | } | ||
456 | #if 0 | ||
457 | else | ||
458 | { | ||
459 | buffers::buffered_base::allocator a( | ||
460 | ws_.data(), ws_.size()/3, false); | ||
461 | src->init(a); | ||
462 | ws_.reserve(a.size_used()); | ||
463 | |||
464 | auto const n = ws_.size() / 2; | ||
465 | |||
466 | tmp0_ = { ws_.data(), ws_.size() / 2 }; | ||
467 | ws_.reserve(n); | ||
468 | |||
469 | // Buffer is too small | ||
470 | if(ws_.size() < 1) | ||
471 | detail::throw_length_error(); | ||
472 | |||
473 | tmp1_ = { ws_.data(), ws_.size() }; | ||
474 | } | ||
475 | #endif | ||
476 | |||
477 | 6 | hp_ = &out_[0]; | |
478 | 6 | *hp_ = { m.ph_->cbuf, m.ph_->size }; | |
479 | 6 | } | |
480 | |||
481 | auto | ||
482 | ✗ | serializer:: | |
483 | start_stream( | ||
484 | message_view_base const& m) -> | ||
485 | stream | ||
486 | { | ||
487 | ✗ | start_init(m); | |
488 | |||
489 | ✗ | st_ = style::stream; | |
490 | out_ = make_array( | ||
491 | 1 + // header | ||
492 | ✗ | 2); // tmp | |
493 | //if(! cod_) | ||
494 | { | ||
495 | ✗ | tmp0_ = { ws_.data(), ws_.size() }; | |
496 | ✗ | if(tmp0_.capacity() < | |
497 | 18 + // chunk size | ||
498 | 1 + // body (1 byte) | ||
499 | 2 + // CRLF | ||
500 | 5) // final chunk | ||
501 | ✗ | detail::throw_length_error(); | |
502 | } | ||
503 | #if 0 | ||
504 | else | ||
505 | { | ||
506 | auto const n = ws_.size() / 2; | ||
507 | tmp0_ = { ws_.data(), n }; | ||
508 | ws_.reserve(n); | ||
509 | |||
510 | // Buffer is too small | ||
511 | if(ws_.size() < 1) | ||
512 | detail::throw_length_error(); | ||
513 | |||
514 | tmp1_ = { ws_.data(), ws_.size() }; | ||
515 | } | ||
516 | #endif | ||
517 | |||
518 | ✗ | hp_ = &out_[0]; | |
519 | ✗ | *hp_ = { m.ph_->cbuf, m.ph_->size }; | |
520 | |||
521 | ✗ | more_ = true; | |
522 | |||
523 | ✗ | return stream{*this}; | |
524 | } | ||
525 | |||
526 | //------------------------------------------------ | ||
527 | |||
528 | std::size_t | ||
529 | ✗ | serializer:: | |
530 | stream:: | ||
531 | capacity() const | ||
532 | { | ||
533 | ✗ | auto const n = | |
534 | chunked_overhead_ + | ||
535 | 2 + // CRLF | ||
536 | 5; // final chunk | ||
537 | ✗ | return sr_->tmp0_.capacity() - n; // VFALCO ? | |
538 | } | ||
539 | |||
540 | std::size_t | ||
541 | ✗ | serializer:: | |
542 | stream:: | ||
543 | size() const | ||
544 | { | ||
545 | ✗ | return sr_->tmp0_.size(); | |
546 | } | ||
547 | |||
548 | auto | ||
549 | ✗ | serializer:: | |
550 | stream:: | ||
551 | prepare( | ||
552 | std::size_t n) const -> | ||
553 | buffers_type | ||
554 | { | ||
555 | ✗ | return sr_->tmp0_.prepare(n); | |
556 | } | ||
557 | |||
558 | void | ||
559 | ✗ | serializer:: | |
560 | stream:: | ||
561 | commit(std::size_t n) const | ||
562 | { | ||
563 | ✗ | sr_->tmp0_.commit(n); | |
564 | ✗ | } | |
565 | |||
566 | void | ||
567 | ✗ | serializer:: | |
568 | stream:: | ||
569 | close() const | ||
570 | { | ||
571 | // Precondition violation | ||
572 | ✗ | if(! sr_->more_) | |
573 | ✗ | detail::throw_logic_error(); | |
574 | ✗ | sr_->more_ = false; | |
575 | ✗ | } | |
576 | |||
577 | //------------------------------------------------ | ||
578 | |||
579 | } // http_proto | ||
580 | } // boost | ||
581 | |||
582 | #endif | ||
583 |