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