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