1 //
2 // Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot 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/boostorg/beast
8 //
9
10 #include <boost/beast.hpp>
11 #include <iostream>
12
13 /* This file contains the functions and classes found in the documentation
14
15 They are compiled and run as part of the unit tests, so you can copy
16 the code and use it in your own projects as a starting point for
17 building a network application.
18 */
19
20 // The documentation assumes the boost::beast::http namespace
21 namespace boost {
22 namespace beast {
23 namespace http {
24
25 //------------------------------------------------------------------------------
26 //
27 // Example: Expect 100-continue
28 //
29 //------------------------------------------------------------------------------
30
31 //[example_http_send_expect_100_continue
32
33 /** Send a request with Expect: 100-continue
34
35 This function will send a request with the Expect: 100-continue
36 field by first sending the header, then waiting for a successful
37 response from the server before continuing to send the body. If
38 a non-successful server response is received, the function
39 returns immediately.
40
41 @param stream The remote HTTP server stream.
42
43 @param buffer The buffer used for reading.
44
45 @param req The request to send. This function modifies the object:
46 the Expect header field is inserted into the message if it does
47 not already exist, and set to "100-continue".
48
49 @param ec Set to the error, if any occurred.
50 */
51 template<
52 class SyncStream,
53 class DynamicBuffer,
54 class Body, class Allocator>
55 void
send_expect_100_continue(SyncStream & stream,DynamicBuffer & buffer,request<Body,basic_fields<Allocator>> & req,error_code & ec)56 send_expect_100_continue(
57 SyncStream& stream,
58 DynamicBuffer& buffer,
59 request<Body, basic_fields<Allocator>>& req,
60 error_code& ec)
61 {
62 static_assert(is_sync_stream<SyncStream>::value,
63 "SyncStream requirements not met");
64
65 static_assert(
66 boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
67 "DynamicBuffer requirements not met");
68
69 // Insert or replace the Expect field
70 req.set(field::expect, "100-continue");
71
72 // Create the serializer
73 request_serializer<Body, basic_fields<Allocator>> sr{req};
74
75 // Send just the header
76 write_header(stream, sr, ec);
77 if(ec)
78 return;
79
80 // Read the response from the server.
81 // A robust client could set a timeout here.
82 {
83 response<string_body> res;
84 read(stream, buffer, res, ec);
85 if(ec)
86 return;
87 if(res.result() != status::continue_)
88 {
89 // The server indicated that it will not
90 // accept the request, so skip sending the body.
91 return;
92 }
93 }
94
95 // Server is OK with the request, send the body
96 write(stream, sr, ec);
97 }
98
99 //]
100
101 //[example_http_receive_expect_100_continue
102
103 /** Receive a request, handling Expect: 100-continue if present.
104
105 This function will read a request from the specified stream.
106 If the request contains the Expect: 100-continue field, a
107 status response will be delivered.
108
109 @param stream The remote HTTP client stream.
110
111 @param buffer The buffer used for reading.
112
113 @param ec Set to the error, if any occurred.
114 */
115 template<
116 class SyncStream,
117 class DynamicBuffer>
118 void
receive_expect_100_continue(SyncStream & stream,DynamicBuffer & buffer,error_code & ec)119 receive_expect_100_continue(
120 SyncStream& stream,
121 DynamicBuffer& buffer,
122 error_code& ec)
123 {
124 static_assert(is_sync_stream<SyncStream>::value,
125 "SyncStream requirements not met");
126
127 static_assert(
128 boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
129 "DynamicBuffer requirements not met");
130
131 // Declare a parser for a request with a string body
132 request_parser<string_body> parser;
133
134 // Read the header
135 read_header(stream, buffer, parser, ec);
136 if(ec)
137 return;
138
139 // Check for the Expect field value
140 if(parser.get()[field::expect] == "100-continue")
141 {
142 // send 100 response
143 response<empty_body> res;
144 res.version(11);
145 res.result(status::continue_);
146 res.set(field::server, "test");
147 write(stream, res, ec);
148 if(ec)
149 return;
150 }
151
152 // Read the rest of the message.
153 //
154 read(stream, buffer, parser, ec);
155 }
156
157 //]
158
159 //------------------------------------------------------------------------------
160 //
161 // Example: Send Child Process Output
162 //
163 //------------------------------------------------------------------------------
164
165 //[example_http_send_cgi_response
166
167 /** Send the output of a child process as an HTTP response.
168
169 The output of the child process comes from a @b SyncReadStream. Data
170 will be sent continuously as it is produced, without the requirement
171 that the entire process output is buffered before being sent. The
172 response will use the chunked transfer encoding.
173
174 @param input A stream to read the child process output from.
175
176 @param output A stream to write the HTTP response to.
177
178 @param ec Set to the error, if any occurred.
179 */
180 template<
181 class SyncReadStream,
182 class SyncWriteStream>
183 void
send_cgi_response(SyncReadStream & input,SyncWriteStream & output,error_code & ec)184 send_cgi_response(
185 SyncReadStream& input,
186 SyncWriteStream& output,
187 error_code& ec)
188 {
189 static_assert(is_sync_read_stream<SyncReadStream>::value,
190 "SyncReadStream requirements not met");
191
192 static_assert(is_sync_write_stream<SyncWriteStream>::value,
193 "SyncWriteStream requirements not met");
194
195 // Set up the response. We use the buffer_body type,
196 // allowing serialization to use manually provided buffers.
197 response<buffer_body> res;
198
199 res.result(status::ok);
200 res.version(11);
201 res.set(field::server, "Beast");
202 res.set(field::transfer_encoding, "chunked");
203
204 // No data yet, but we set more = true to indicate
205 // that it might be coming later. Otherwise the
206 // serializer::is_done would return true right after
207 // sending the header.
208 res.body().data = nullptr;
209 res.body().more = true;
210
211 // Create the serializer.
212 response_serializer<buffer_body, fields> sr{res};
213
214 // Send the header immediately.
215 write_header(output, sr, ec);
216 if(ec)
217 return;
218
219 // Alternate between reading from the child process
220 // and sending all the process output until there
221 // is no more output.
222 do
223 {
224 // Read a buffer from the child process
225 char buffer[2048];
226 auto bytes_transferred = input.read_some(
227 boost::asio::buffer(buffer, sizeof(buffer)), ec);
228 if(ec == boost::asio::error::eof)
229 {
230 ec = {};
231
232 // `nullptr` indicates there is no buffer
233 res.body().data = nullptr;
234
235 // `false` means no more data is coming
236 res.body().more = false;
237 }
238 else
239 {
240 if(ec)
241 return;
242
243 // Point to our buffer with the bytes that
244 // we received, and indicate that there may
245 // be some more data coming
246 res.body().data = buffer;
247 res.body().size = bytes_transferred;
248 res.body().more = true;
249 }
250
251 // Write everything in the body buffer
252 write(output, sr, ec);
253
254 // This error is returned by body_buffer during
255 // serialization when it is done sending the data
256 // provided and needs another buffer.
257 if(ec == error::need_buffer)
258 {
259 ec = {};
260 continue;
261 }
262 if(ec)
263 return;
264 }
265 while(! sr.is_done());
266 }
267
268 //]
269
270 //--------------------------------------------------------------------------
271 //
272 // Example: HEAD Request
273 //
274 //--------------------------------------------------------------------------
275
276 //[example_http_do_head_response
277
278 /** Handle a HEAD request for a resource.
279 */
280 template<
281 class SyncStream,
282 class DynamicBuffer
283 >
do_server_head(SyncStream & stream,DynamicBuffer & buffer,error_code & ec)284 void do_server_head(
285 SyncStream& stream,
286 DynamicBuffer& buffer,
287 error_code& ec)
288 {
289 static_assert(is_sync_stream<SyncStream>::value,
290 "SyncStream requirements not met");
291 static_assert(
292 boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
293 "DynamicBuffer requirements not met");
294
295 // We deliver this payload for all GET requests
296 static std::string const payload = "Hello, world!";
297
298 // Read the request
299 request<string_body> req;
300 read(stream, buffer, req, ec);
301 if(ec)
302 return;
303
304 // Set up the response, starting with the common fields
305 response<string_body> res;
306 res.version(11);
307 res.set(field::server, "test");
308
309 // Now handle request-specific fields
310 switch(req.method())
311 {
312 case verb::head:
313 case verb::get:
314 {
315 // A HEAD request is handled by delivering the same
316 // set of headers that would be sent for a GET request,
317 // including the Content-Length, except for the body.
318 res.result(status::ok);
319 res.content_length(payload.size());
320
321 // For GET requests, we include the body
322 if(req.method() == verb::get)
323 {
324 // We deliver the same payload for GET requests
325 // regardless of the target. A real server might
326 // deliver a file based on the target.
327 res.body() = payload;
328 }
329 break;
330 }
331
332 default:
333 {
334 // We return responses indicating an error if
335 // we do not recognize the request method.
336 res.result(status::bad_request);
337 res.set(field::content_type, "text/plain");
338 res.body() = "Invalid request-method '" + std::string(req.method_string()) + "'";
339 res.prepare_payload();
340 break;
341 }
342 }
343
344 // Send the response
345 write(stream, res, ec);
346 if(ec)
347 return;
348 }
349
350 //]
351
352 //[example_http_do_head_request
353
354 /** Send a HEAD request for a resource.
355
356 This function submits a HEAD request for the specified resource
357 and returns the response.
358
359 @param res The response. This is an output parameter.
360
361 @param stream The synchronous stream to use.
362
363 @param buffer The buffer to use.
364
365 @param target The request target.
366
367 @param ec Set to the error, if any occurred.
368
369 @throws std::invalid_argument if target is empty.
370 */
371 template<
372 class SyncStream,
373 class DynamicBuffer
374 >
375 response<empty_body>
do_head_request(SyncStream & stream,DynamicBuffer & buffer,string_view target,error_code & ec)376 do_head_request(
377 SyncStream& stream,
378 DynamicBuffer& buffer,
379 string_view target,
380 error_code& ec)
381 {
382 // Do some type checking to be a good citizen
383 static_assert(is_sync_stream<SyncStream>::value,
384 "SyncStream requirements not met");
385 static_assert(
386 boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
387 "DynamicBuffer requirements not met");
388
389 // The interfaces we are using are low level and do not
390 // perform any checking of arguments; so we do it here.
391 if(target.empty())
392 throw std::invalid_argument("target may not be empty");
393
394 // Build the HEAD request for the target
395 request<empty_body> req;
396 req.version(11);
397 req.method(verb::head);
398 req.target(target);
399 req.set(field::user_agent, "test");
400
401 // A client MUST send a Host header field in all HTTP/1.1 request messages.
402 // https://tools.ietf.org/html/rfc7230#section-5.4
403 req.set(field::host, "localhost");
404
405 // Now send it
406 write(stream, req, ec);
407 if(ec)
408 return {};
409
410 // Create a parser to read the response.
411 // We use the `empty_body` type since
412 // a response to a HEAD request MUST NOT
413 // include a body.
414 response_parser<empty_body> p;
415
416 // Inform the parser that there will be no body.
417 p.skip(true);
418
419 // Read the message. Even though fields like
420 // Content-Length or Transfer-Encoding may be
421 // set, the message will not contain a body.
422 read(stream, buffer, p, ec);
423 if(ec)
424 return {};
425
426 // Transfer ownership of the response to the caller.
427 return p.release();
428 }
429
430 //]
431
432 //------------------------------------------------------------------------------
433 //
434 // Example: HTTP Relay
435 //
436 //------------------------------------------------------------------------------
437
438 //[example_http_relay
439
440 /** Relay an HTTP message.
441
442 This function efficiently relays an HTTP message from a downstream
443 client to an upstream server, or from an upstream server to a
444 downstream client. After the message header is read from the input,
445 a user provided transformation function is invoked which may change
446 the contents of the header before forwarding to the output. This may
447 be used to adjust fields such as Server, or proxy fields.
448
449 @param output The stream to write to.
450
451 @param input The stream to read from.
452
453 @param buffer The buffer to use for the input.
454
455 @param transform The header transformation to apply. The function will
456 be called with this signature:
457 @code
458 template<class Body>
459 void transform(message<
460 isRequest, Body, Fields>&, // The message to transform
461 error_code&); // Set to the error, if any
462 @endcode
463
464 @param ec Set to the error if any occurred.
465
466 @tparam isRequest `true` to relay a request.
467
468 @tparam Fields The type of fields to use for the message.
469 */
470 template<
471 bool isRequest,
472 class SyncWriteStream,
473 class SyncReadStream,
474 class DynamicBuffer,
475 class Transform>
476 void
relay(SyncWriteStream & output,SyncReadStream & input,DynamicBuffer & buffer,error_code & ec,Transform && transform)477 relay(
478 SyncWriteStream& output,
479 SyncReadStream& input,
480 DynamicBuffer& buffer,
481 error_code& ec,
482 Transform&& transform)
483 {
484 static_assert(is_sync_write_stream<SyncWriteStream>::value,
485 "SyncWriteStream requirements not met");
486
487 static_assert(is_sync_read_stream<SyncReadStream>::value,
488 "SyncReadStream requirements not met");
489
490 // A small buffer for relaying the body piece by piece
491 char buf[2048];
492
493 // Create a parser with a buffer body to read from the input.
494 parser<isRequest, buffer_body> p;
495
496 // Create a serializer from the message contained in the parser.
497 serializer<isRequest, buffer_body, fields> sr{p.get()};
498
499 // Read just the header from the input
500 read_header(input, buffer, p, ec);
501 if(ec)
502 return;
503
504 // Apply the caller's header transformation
505 transform(p.get(), ec);
506 if(ec)
507 return;
508
509 // Send the transformed message to the output
510 write_header(output, sr, ec);
511 if(ec)
512 return;
513
514 // Loop over the input and transfer it to the output
515 do
516 {
517 if(! p.is_done())
518 {
519 // Set up the body for writing into our small buffer
520 p.get().body().data = buf;
521 p.get().body().size = sizeof(buf);
522
523 // Read as much as we can
524 read(input, buffer, p, ec);
525
526 // This error is returned when buffer_body uses up the buffer
527 if(ec == error::need_buffer)
528 ec = {};
529 if(ec)
530 return;
531
532 // Set up the body for reading.
533 // This is how much was parsed:
534 p.get().body().size = sizeof(buf) - p.get().body().size;
535 p.get().body().data = buf;
536 p.get().body().more = ! p.is_done();
537 }
538 else
539 {
540 p.get().body().data = nullptr;
541 p.get().body().size = 0;
542 }
543
544 // Write everything in the buffer (which might be empty)
545 write(output, sr, ec);
546
547 // This error is returned when buffer_body uses up the buffer
548 if(ec == error::need_buffer)
549 ec = {};
550 if(ec)
551 return;
552 }
553 while(! p.is_done() && ! sr.is_done());
554 }
555
556 //]
557
558 //------------------------------------------------------------------------------
559 //
560 // Example: Serialize to std::ostream
561 //
562 //------------------------------------------------------------------------------
563
564 //[example_http_write_ostream
565
566 // The detail namespace means "not public"
567 namespace detail {
568
569 // This helper is needed for C++11.
570 // When invoked with a buffer sequence, writes the buffers `to the std::ostream`.
571 template<class Serializer>
572 class write_ostream_helper
573 {
574 Serializer& sr_;
575 std::ostream& os_;
576
577 public:
write_ostream_helper(Serializer & sr,std::ostream & os)578 write_ostream_helper(Serializer& sr, std::ostream& os)
579 : sr_(sr)
580 , os_(os)
581 {
582 }
583
584 // This function is called by the serializer
585 template<class ConstBufferSequence>
586 void
operator ()(error_code & ec,ConstBufferSequence const & buffers) const587 operator()(error_code& ec, ConstBufferSequence const& buffers) const
588 {
589 // Error codes must be cleared on success
590 ec = {};
591
592 // Keep a running total of how much we wrote
593 std::size_t bytes_transferred = 0;
594
595 // Loop over the buffer sequence
596 for(auto it = boost::asio::buffer_sequence_begin(buffers);
597 it != boost::asio::buffer_sequence_end(buffers); ++it)
598 {
599 // This is the next buffer in the sequence
600 boost::asio::const_buffer const buffer = *it;
601
602 // Write it to the std::ostream
603 os_.write(
604 reinterpret_cast<char const*>(buffer.data()),
605 buffer.size());
606
607 // If the std::ostream fails, convert it to an error code
608 if(os_.fail())
609 {
610 ec = make_error_code(errc::io_error);
611 return;
612 }
613
614 // Adjust our running total
615 bytes_transferred += buffer_size(buffer);
616 }
617
618 // Inform the serializer of the amount we consumed
619 sr_.consume(bytes_transferred);
620 }
621 };
622
623 } // detail
624
625 /** Write a message to a `std::ostream`.
626
627 This function writes the serialized representation of the
628 HTTP/1 message to the sream.
629
630 @param os The `std::ostream` to write to.
631
632 @param msg The message to serialize.
633
634 @param ec Set to the error, if any occurred.
635 */
636 template<
637 bool isRequest,
638 class Body,
639 class Fields>
640 void
write_ostream(std::ostream & os,message<isRequest,Body,Fields> & msg,error_code & ec)641 write_ostream(
642 std::ostream& os,
643 message<isRequest, Body, Fields>& msg,
644 error_code& ec)
645 {
646 // Create the serializer instance
647 serializer<isRequest, Body, Fields> sr{msg};
648
649 // This lambda is used as the "visit" function
650 detail::write_ostream_helper<decltype(sr)> lambda{sr, os};
651 do
652 {
653 // In C++14 we could use a generic lambda but since we want
654 // to require only C++11, the lambda is written out by hand.
655 // This function call retrieves the next serialized buffers.
656 sr.next(ec, lambda);
657 if(ec)
658 return;
659 }
660 while(! sr.is_done());
661 }
662
663 //]
664
665 //------------------------------------------------------------------------------
666 //
667 // Example: Parse from std::istream
668 //
669 //------------------------------------------------------------------------------
670
671 //[example_http_read_istream
672
673 /** Read a message from a `std::istream`.
674
675 This function attempts to parse a complete HTTP/1 message from the stream.
676
677 @param is The `std::istream` to read from.
678
679 @param buffer The buffer to use.
680
681 @param msg The message to store the result.
682
683 @param ec Set to the error, if any occurred.
684 */
685 template<
686 class Allocator,
687 bool isRequest,
688 class Body>
689 void
read_istream(std::istream & is,basic_flat_buffer<Allocator> & buffer,message<isRequest,Body,fields> & msg,error_code & ec)690 read_istream(
691 std::istream& is,
692 basic_flat_buffer<Allocator>& buffer,
693 message<isRequest, Body, fields>& msg,
694 error_code& ec)
695 {
696 // Create the message parser
697 //
698 // Arguments passed to the parser's constructor are
699 // forwarded to the message constructor. Here, we use
700 // a move construction in case the caller has constructed
701 // their message in a non-default way.
702 //
703 parser<isRequest, Body> p{std::move(msg)};
704
705 do
706 {
707 // Extract whatever characters are presently available in the istream
708 if(is.rdbuf()->in_avail() > 0)
709 {
710 // Get a mutable buffer sequence for writing
711 auto const b = buffer.prepare(
712 static_cast<std::size_t>(is.rdbuf()->in_avail()));
713
714 // Now get everything we can from the istream
715 buffer.commit(static_cast<std::size_t>(is.readsome(
716 reinterpret_cast<char*>(b.data()), b.size())));
717 }
718 else if(buffer.size() == 0)
719 {
720 // Our buffer is empty and we need more characters,
721 // see if we've reached the end of file on the istream
722 if(! is.eof())
723 {
724 // Get a mutable buffer sequence for writing
725 auto const b = buffer.prepare(1024);
726
727 // Try to get more from the istream. This might block.
728 is.read(reinterpret_cast<char*>(b.data()), b.size());
729
730 // If an error occurs on the istream then return it to the caller.
731 if(is.fail() && ! is.eof())
732 {
733 // We'll just re-use io_error since std::istream has no error_code interface.
734 ec = make_error_code(errc::io_error);
735 return;
736 }
737
738 // Commit the characters we got to the buffer.
739 buffer.commit(static_cast<std::size_t>(is.gcount()));
740 }
741 else
742 {
743 // Inform the parser that we've reached the end of the istream.
744 p.put_eof(ec);
745 if(ec)
746 return;
747 break;
748 }
749 }
750
751 // Write the data to the parser
752 auto const bytes_used = p.put(buffer.data(), ec);
753
754 // This error means that the parser needs additional octets.
755 if(ec == error::need_more)
756 ec = {};
757 if(ec)
758 return;
759
760 // Consume the buffer octets that were actually parsed.
761 buffer.consume(bytes_used);
762 }
763 while(! p.is_done());
764
765 // Transfer ownership of the message container in the parser to the caller.
766 msg = p.release();
767 }
768
769 //]
770
771 //------------------------------------------------------------------------------
772 //
773 // Example: Deferred Body Type
774 //
775 //------------------------------------------------------------------------------
776
777 //[example_http_defer_body
778
779 /** Handle a form POST request, choosing a body type depending on the Content-Type.
780
781 This reads a request from the input stream. If the method is POST, and
782 the Content-Type is "application/x-www-form-urlencoded " or
783 "multipart/form-data", a `string_body` is used to receive and store
784 the message body. Otherwise, a `dynamic_body` is used to store the message
785 body. After the request is received, the handler will be invoked with the
786 request.
787
788 @param stream The stream to read from.
789
790 @param buffer The buffer to use for reading.
791
792 @param handler The handler to invoke when the request is complete.
793 The handler must be invokable with this signature:
794 @code
795 template<class Body>
796 void handler(request<Body>&& req);
797 @endcode
798
799 @throws system_error Thrown on failure.
800 */
801 template<
802 class SyncReadStream,
803 class DynamicBuffer,
804 class Handler>
805 void
do_form_request(SyncReadStream & stream,DynamicBuffer & buffer,Handler && handler)806 do_form_request(
807 SyncReadStream& stream,
808 DynamicBuffer& buffer,
809 Handler&& handler)
810 {
811 // Start with an empty_body parser
812 request_parser<empty_body> req0;
813
814 // Read just the header. Otherwise, the empty_body
815 // would generate an error if body octets were received.
816 read_header(stream, buffer, req0);
817
818 // Choose a body depending on the method verb
819 switch(req0.get().method())
820 {
821 case verb::post:
822 {
823 // If this is not a form upload then use a string_body
824 if( req0.get()[field::content_type] != "application/x-www-form-urlencoded" &&
825 req0.get()[field::content_type] != "multipart/form-data")
826 goto do_dynamic_body;
827
828 // Commit to string_body as the body type.
829 // As long as there are no body octets in the parser
830 // we are constructing from, no exception is thrown.
831 request_parser<string_body> req{std::move(req0)};
832
833 // Finish reading the message
834 read(stream, buffer, req);
835
836 // Call the handler. It can take ownership
837 // if desired, since we are calling release()
838 handler(req.release());
839 break;
840 }
841
842 do_dynamic_body:
843 default:
844 {
845 // Commit to dynamic_body as the body type.
846 // As long as there are no body octets in the parser
847 // we are constructing from, no exception is thrown.
848 request_parser<dynamic_body> req{std::move(req0)};
849
850 // Finish reading the message
851 read(stream, buffer, req);
852
853 // Call the handler. It can take ownership
854 // if desired, since we are calling release()
855 handler(req.release());
856 break;
857 }
858 }
859 }
860
861 //]
862
863 //------------------------------------------------------------------------------
864 //
865 // Example: Incremental Read
866 //
867 //------------------------------------------------------------------------------
868
869 //[example_incremental_read
870
871 /* This function reads a message using a fixed size buffer to hold
872 portions of the body, and prints the body contents to a `std::ostream`.
873 */
874 template<
875 bool isRequest,
876 class SyncReadStream,
877 class DynamicBuffer>
878 void
read_and_print_body(std::ostream & os,SyncReadStream & stream,DynamicBuffer & buffer,error_code & ec)879 read_and_print_body(
880 std::ostream& os,
881 SyncReadStream& stream,
882 DynamicBuffer& buffer,
883 error_code& ec)
884 {
885 parser<isRequest, buffer_body> p;
886 read_header(stream, buffer, p, ec);
887 if(ec)
888 return;
889 while(! p.is_done())
890 {
891 char buf[512];
892 p.get().body().data = buf;
893 p.get().body().size = sizeof(buf);
894 read(stream, buffer, p, ec);
895 if(ec == error::need_buffer)
896 ec = {};
897 if(ec)
898 return;
899 os.write(buf, sizeof(buf) - p.get().body().size);
900 }
901 }
902
903 //]
904
905
906 //------------------------------------------------------------------------------
907 //
908 // Example: Expect 100-continue
909 //
910 //------------------------------------------------------------------------------
911
912 //[example_chunk_parsing
913
914 /** Read a message with a chunked body and print the chunks and extensions
915 */
916 template<
917 bool isRequest,
918 class SyncReadStream,
919 class DynamicBuffer>
920 void
print_chunked_body(std::ostream & os,SyncReadStream & stream,DynamicBuffer & buffer,error_code & ec)921 print_chunked_body(
922 std::ostream& os,
923 SyncReadStream& stream,
924 DynamicBuffer& buffer,
925 error_code& ec)
926 {
927 // Declare the parser with an empty body since
928 // we plan on capturing the chunks ourselves.
929 parser<isRequest, empty_body> p;
930
931 // First read the complete header
932 read_header(stream, buffer, p, ec);
933 if(ec)
934 return;
935
936 // This container will hold the extensions for each chunk
937 chunk_extensions ce;
938
939 // This string will hold the body of each chunk
940 std::string chunk;
941
942 // Declare our chunk header callback This is invoked
943 // after each chunk header and also after the last chunk.
944 auto header_cb =
945 [&](std::uint64_t size, // Size of the chunk, or zero for the last chunk
946 string_view extensions, // The raw chunk-extensions string. Already validated.
947 error_code& ev) // We can set this to indicate an error
948 {
949 // Parse the chunk extensions so we can access them easily
950 ce.parse(extensions, ev);
951 if(ev)
952 return;
953
954 // See if the chunk is too big
955 if(size > (std::numeric_limits<std::size_t>::max)())
956 {
957 ev = error::body_limit;
958 return;
959 }
960
961 // Make sure we have enough storage, and
962 // reset the container for the upcoming chunk
963 chunk.reserve(static_cast<std::size_t>(size));
964 chunk.clear();
965 };
966
967 // Set the callback. The function requires a non-const reference so we
968 // use a local variable, since temporaries can only bind to const refs.
969 p.on_chunk_header(header_cb);
970
971 // Declare the chunk body callback. This is called one or
972 // more times for each piece of a chunk body.
973 auto body_cb =
974 [&](std::uint64_t remain, // The number of bytes left in this chunk
975 string_view body, // A buffer holding chunk body data
976 error_code& ec) // We can set this to indicate an error
977 {
978 // If this is the last piece of the chunk body,
979 // set the error so that the call to `read` returns
980 // and we can process the chunk.
981 if(remain == body.size())
982 ec = error::end_of_chunk;
983
984 // Append this piece to our container
985 chunk.append(body.data(), body.size());
986
987 // The return value informs the parser of how much of the body we
988 // consumed. We will indicate that we consumed everything passed in.
989 return body.size();
990 };
991 p.on_chunk_body(body_cb);
992
993 while(! p.is_done())
994 {
995 // Read as much as we can. When we reach the end of the chunk, the chunk
996 // body callback will make the read return with the end_of_chunk error.
997 read(stream, buffer, p, ec);
998 if(! ec)
999 continue;
1000 else if(ec != error::end_of_chunk)
1001 return;
1002 else
1003 ec = {};
1004
1005 // We got a whole chunk, print the extensions:
1006 for(auto const& extension : ce)
1007 {
1008 os << "Extension: " << extension.first;
1009 if(! extension.second.empty())
1010 os << " = " << extension.second << std::endl;
1011 else
1012 os << std::endl;
1013 }
1014
1015 // Now print the chunk body
1016 os << "Chunk Body: " << chunk << std::endl;
1017 }
1018
1019 // Get a reference to the parsed message, this is for convenience
1020 auto const& msg = p.get();
1021
1022 // Check each field promised in the "Trailer" header and output it
1023 for(auto const& name : token_list{msg[field::trailer]})
1024 {
1025 // Find the trailer field
1026 auto it = msg.find(name);
1027 if(it == msg.end())
1028 {
1029 // Oops! They promised the field but failed to deliver it
1030 os << "Missing Trailer: " << name << std::endl;
1031 continue;
1032 }
1033 os << it->name() << ": " << it->value() << std::endl;
1034 }
1035 }
1036
1037 //]
1038
1039 } // http
1040 } // beast
1041 } // boost
1042