• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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