1 //
2 // Copyright (c) 2016-2019 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 #ifndef BOOST_BEAST_HTTP_PARSER_HPP
11 #define BOOST_BEAST_HTTP_PARSER_HPP
12 
13 #include <boost/beast/core/detail/config.hpp>
14 #include <boost/beast/http/basic_parser.hpp>
15 #include <boost/beast/http/message.hpp>
16 #include <boost/beast/http/type_traits.hpp>
17 #include <boost/optional.hpp>
18 #include <boost/throw_exception.hpp>
19 #include <functional>
20 #include <memory>
21 #include <type_traits>
22 #include <utility>
23 
24 namespace boost {
25 namespace beast {
26 namespace http {
27 
28 /** An HTTP/1 parser for producing a message.
29 
30     This class uses the basic HTTP/1 wire format parser to convert
31     a series of octets into a @ref message using the @ref basic_fields
32     container to represent the fields.
33 
34     @tparam isRequest Indicates whether a request or response
35     will be parsed.
36 
37     @tparam Body The type used to represent the body. This must
38     meet the requirements of <em>Body</em>.
39 
40     @tparam Allocator The type of allocator used with the
41     @ref basic_fields container.
42 
43     @note A new instance of the parser is required for each message.
44 */
45 template<
46     bool isRequest,
47     class Body,
48     class Allocator = std::allocator<char>>
49 class parser
50     : public basic_parser<isRequest>
51 {
52     static_assert(is_body<Body>::value,
53         "Body type requirements not met");
54 
55     static_assert(is_body_reader<Body>::value,
56         "BodyReader type requirements not met");
57 
58     template<bool, class, class>
59     friend class parser;
60 
61     message<isRequest, Body, basic_fields<Allocator>> m_;
62     typename Body::reader rd_;
63     bool rd_inited_ = false;
64     bool used_ = false;
65 
66     std::function<void(
67         std::uint64_t,
68         string_view,
69         error_code&)> cb_h_;
70 
71     std::function<std::size_t(
72         std::uint64_t,
73         string_view,
74         error_code&)> cb_b_;
75 
76 public:
77     /// The type of message returned by the parser
78     using value_type =
79         message<isRequest, Body, basic_fields<Allocator>>;
80 
81     /// Destructor
82     ~parser() = default;
83 
84     /// Constructor (disallowed)
85     parser(parser const&) = delete;
86 
87     /// Assignment (disallowed)
88     parser& operator=(parser const&) = delete;
89 
90     /// Constructor (disallowed)
91     parser(parser&& other) = delete;
92 
93     /// Constructor
94     parser();
95 
96     /** Constructor
97 
98         @param args Optional arguments forwarded to the
99         @ref http::message constructor.
100 
101         @note This function participates in overload
102         resolution only if the first argument is not a
103         @ref parser.
104     */
105 #if BOOST_BEAST_DOXYGEN
106     template<class... Args>
107     explicit
108     parser(Args&&... args);
109 #else
110     template<class Arg1, class... ArgN,
111         class = typename std::enable_if<
112             ! detail::is_parser<typename
113                 std::decay<Arg1>::type>::value>::type>
114     explicit
115     parser(Arg1&& arg1, ArgN&&... argn);
116 #endif
117 
118     /** Construct a parser from another parser, changing the Body type.
119 
120         This constructs a new parser by move constructing the
121         header from another parser with a different body type. The
122         constructed-from parser must not have any parsed body octets or
123         initialized <em>BodyReader</em>, otherwise an exception is generated.
124 
125         @par Example
126         @code
127         // Deferred body type commitment
128         request_parser<empty_body> req0;
129         ...
130         request_parser<string_body> req{std::move(req0)};
131         @endcode
132 
133         If an exception is thrown, the state of the constructed-from
134         parser is undefined.
135 
136         @param parser The other parser to construct from. After
137         this call returns, the constructed-from parser may only
138         be destroyed.
139 
140         @param args Optional arguments forwarded to the message
141         constructor.
142 
143         @throws std::invalid_argument Thrown when the constructed-from
144         parser has already initialized a body reader.
145 
146         @note This function participates in overload resolution only
147         if the other parser uses a different body type.
148     */
149 #if BOOST_BEAST_DOXYGEN
150     template<class OtherBody, class... Args>
151 #else
152     template<class OtherBody, class... Args,
153         class = typename std::enable_if<
154             ! std::is_same<Body, OtherBody>::value>::type>
155 #endif
156     explicit
157     parser(parser<isRequest, OtherBody,
158         Allocator>&& parser, Args&&... args);
159 
160     /** Returns the parsed message.
161 
162         Depending on the parser's progress,
163         parts of this object may be incomplete.
164     */
165     value_type const&
get() const166     get() const
167     {
168         return m_;
169     }
170 
171     /** Returns the parsed message.
172 
173         Depending on the parser's progress,
174         parts of this object may be incomplete.
175     */
176     value_type&
get()177     get()
178     {
179         return m_;
180     }
181 
182     /** Returns ownership of the parsed message.
183 
184         Ownership is transferred to the caller.
185         Depending on the parser's progress,
186         parts of this object may be incomplete.
187 
188         @par Requires
189 
190         @ref value_type is @b MoveConstructible
191     */
192     value_type
release()193     release()
194     {
195         static_assert(std::is_move_constructible<decltype(m_)>::value,
196             "MoveConstructible requirements not met");
197         return std::move(m_);
198     }
199 
200     /** Set a callback to be invoked on each chunk header.
201 
202         The callback will be invoked once for every chunk in the message
203         payload, as well as once for the last chunk. The invocation
204         happens after the chunk header is available but before any body
205         octets have been parsed.
206 
207         The extensions are provided in raw, validated form, use
208         @ref chunk_extensions::parse to parse the extensions into a
209         structured container for easier access.
210         The implementation type-erases the callback without requiring
211         a dynamic allocation. For this reason, the callback object is
212         passed by a non-constant reference.
213 
214         @par Example
215         @code
216         auto callback =
217             [](std::uint64_t size, string_view extensions, error_code& ec)
218             {
219                 //...
220             };
221         parser.on_chunk_header(callback);
222         @endcode
223 
224         @param cb The function to set, which must be invocable with
225         this equivalent signature:
226         @code
227         void
228         on_chunk_header(
229             std::uint64_t size,         // Size of the chunk, zero for the last chunk
230             string_view extensions,     // The chunk-extensions in raw form
231             error_code& ec);            // May be set by the callback to indicate an error
232         @endcode
233     */
234     template<class Callback>
235     void
on_chunk_header(Callback & cb)236     on_chunk_header(Callback& cb)
237     {
238         // Callback may not be constant, caller is responsible for
239         // managing the lifetime of the callback. Copies are not made.
240         BOOST_STATIC_ASSERT(! std::is_const<Callback>::value);
241 
242         // Can't set the callback after receiving any chunk data!
243         BOOST_ASSERT(! rd_inited_);
244 
245         cb_h_ = std::ref(cb);
246     }
247 
248     /** Set a callback to be invoked on chunk body data
249 
250         The provided function object will be invoked one or more times
251         to provide buffers corresponding to the chunk body for the current
252         chunk. The callback receives the number of octets remaining in this
253         chunk body including the octets in the buffer provided.
254 
255         The callback must return the number of octets actually consumed.
256         Any octets not consumed will be presented again in a subsequent
257         invocation of the callback.
258         The implementation type-erases the callback without requiring
259         a dynamic allocation. For this reason, the callback object is
260         passed by a non-constant reference.
261 
262         @par Example
263         @code
264         auto callback =
265             [](std::uint64_t remain, string_view body, error_code& ec)
266             {
267                 //...
268             };
269         parser.on_chunk_body(callback);
270         @endcode
271 
272         @param cb The function to set, which must be invocable with
273         this equivalent signature:
274         @code
275         std::size_t
276         on_chunk_header(
277             std::uint64_t remain,       // Octets remaining in this chunk, includes `body`
278             string_view body,           // A buffer holding some or all of the remainder of the chunk body
279             error_code& ec);            // May be set by the callback to indicate an error
280         @endcode
281         */
282     template<class Callback>
283     void
on_chunk_body(Callback & cb)284     on_chunk_body(Callback& cb)
285     {
286         // Callback may not be constant, caller is responsible for
287         // managing the lifetime of the callback. Copies are not made.
288         BOOST_STATIC_ASSERT(! std::is_const<Callback>::value);
289 
290         // Can't set the callback after receiving any chunk data!
291         BOOST_ASSERT(! rd_inited_);
292 
293         cb_b_ = std::ref(cb);
294     }
295 
296 private:
297     parser(std::true_type);
298     parser(std::false_type);
299 
300     template<class OtherBody, class... Args,
301         class = typename std::enable_if<
302             ! std::is_same<Body, OtherBody>::value>::type>
303     parser(
304         std::true_type,
305         parser<isRequest, OtherBody, Allocator>&& parser,
306         Args&&... args);
307 
308     template<class OtherBody, class... Args,
309         class = typename std::enable_if<
310             ! std::is_same<Body, OtherBody>::value>::type>
311     parser(
312         std::false_type,
313         parser<isRequest, OtherBody, Allocator>&& parser,
314         Args&&... args);
315 
316     template<class Arg1, class... ArgN,
317         class = typename std::enable_if<
318             ! detail::is_parser<typename
319                 std::decay<Arg1>::type>::value>::type>
320     explicit
321     parser(Arg1&& arg1, std::true_type, ArgN&&... argn);
322 
323     template<class Arg1, class... ArgN,
324         class = typename std::enable_if<
325             ! detail::is_parser<typename
326                 std::decay<Arg1>::type>::value>::type>
327     explicit
328     parser(Arg1&& arg1, std::false_type, ArgN&&... argn);
329 
330     void
on_request_impl(verb method,string_view method_str,string_view target,int version,error_code & ec,std::true_type)331     on_request_impl(
332         verb method,
333         string_view method_str,
334         string_view target,
335         int version,
336         error_code& ec,
337         std::true_type)
338     {
339         // If this assert goes off, it means you tried to re-use a
340         // parser after it was done reading a message. This is not
341         // allowed, you need to create a new parser for each message.
342         // The easiest way to do that is to store the parser in
343         // an optional object.
344 
345         BOOST_ASSERT(! used_);
346         if(used_)
347         {
348             ec = error::stale_parser;
349             return;
350         }
351         used_ = true;
352 
353         m_.target(target);
354         if(method != verb::unknown)
355             m_.method(method);
356         else
357             m_.method_string(method_str);
358         m_.version(version);
359     }
360 
361     void
on_request_impl(verb,string_view,string_view,int,error_code &,std::false_type)362     on_request_impl(
363         verb, string_view, string_view,
364         int, error_code&, std::false_type)
365     {
366     }
367 
368     void
on_request_impl(verb method,string_view method_str,string_view target,int version,error_code & ec)369     on_request_impl(
370         verb method,
371         string_view method_str,
372         string_view target,
373         int version,
374         error_code& ec) override
375     {
376         this->on_request_impl(
377             method, method_str, target, version, ec,
378             std::integral_constant<bool, isRequest>{});
379     }
380 
381     void
on_response_impl(int code,string_view reason,int version,error_code & ec,std::true_type)382     on_response_impl(
383         int code,
384         string_view reason,
385         int version,
386         error_code& ec,
387         std::true_type)
388     {
389         // If this assert goes off, it means you tried to re-use a
390         // parser after it was done reading a message. This is not
391         // allowed, you need to create a new parser for each message.
392         // The easiest way to do that is to store the parser in
393         // an optional object.
394 
395         BOOST_ASSERT(! used_);
396         if(used_)
397         {
398             ec = error::stale_parser;
399             return;
400         }
401         used_ = true;
402 
403         m_.result(code);
404         m_.version(version);
405         m_.reason(reason);
406     }
407 
408     void
on_response_impl(int,string_view,int,error_code &,std::false_type)409     on_response_impl(
410         int, string_view, int,
411         error_code&, std::false_type)
412     {
413     }
414 
415     void
on_response_impl(int code,string_view reason,int version,error_code & ec)416     on_response_impl(
417         int code,
418         string_view reason,
419         int version,
420         error_code& ec) override
421     {
422         this->on_response_impl(
423             code, reason, version, ec,
424             std::integral_constant<bool, ! isRequest>{});
425     }
426 
427     void
on_field_impl(field name,string_view name_string,string_view value,error_code &)428     on_field_impl(
429         field name,
430         string_view name_string,
431         string_view value,
432         error_code&) override
433     {
434         m_.insert(name, name_string, value);
435     }
436 
437     void
on_header_impl(error_code & ec)438     on_header_impl(error_code& ec) override
439     {
440         ec = {};
441     }
442 
443     void
on_body_init_impl(boost::optional<std::uint64_t> const & content_length,error_code & ec)444     on_body_init_impl(
445         boost::optional<std::uint64_t> const& content_length,
446         error_code& ec) override
447     {
448         rd_.init(content_length, ec);
449         rd_inited_ = true;
450     }
451 
452     std::size_t
on_body_impl(string_view body,error_code & ec)453     on_body_impl(
454         string_view body,
455         error_code& ec) override
456     {
457         return rd_.put(net::buffer(
458             body.data(), body.size()), ec);
459     }
460 
461     void
on_chunk_header_impl(std::uint64_t size,string_view extensions,error_code & ec)462     on_chunk_header_impl(
463         std::uint64_t size,
464         string_view extensions,
465         error_code& ec) override
466     {
467         if(cb_h_)
468             return cb_h_(size, extensions, ec);
469     }
470 
471     std::size_t
on_chunk_body_impl(std::uint64_t remain,string_view body,error_code & ec)472     on_chunk_body_impl(
473         std::uint64_t remain,
474         string_view body,
475         error_code& ec) override
476     {
477         if(cb_b_)
478             return cb_b_(remain, body, ec);
479         return rd_.put(net::buffer(
480             body.data(), body.size()), ec);
481     }
482 
483     void
on_finish_impl(error_code & ec)484     on_finish_impl(
485         error_code& ec) override
486     {
487         rd_.finish(ec);
488     }
489 };
490 
491 /// An HTTP/1 parser for producing a request message.
492 template<class Body, class Allocator = std::allocator<char>>
493 using request_parser = parser<true, Body, Allocator>;
494 
495 /// An HTTP/1 parser for producing a response message.
496 template<class Body, class Allocator = std::allocator<char>>
497 using response_parser = parser<false, Body, Allocator>;
498 
499 } // http
500 } // beast
501 } // boost
502 
503 #include <boost/beast/http/impl/parser.hpp>
504 
505 #endif
506