]>
Commit | Line | Data |
---|---|---|
b32b8144 | 1 | // |
92f5a8d4 | 2 | // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) |
b32b8144 FG |
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 | |
92f5a8d4 | 38 | meet the requirements of <em>Body</em>. |
b32b8144 FG |
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 | |
92f5a8d4 | 50 | : public basic_parser<isRequest> |
b32b8144 FG |
51 | { |
52 | static_assert(is_body<Body>::value, | |
92f5a8d4 | 53 | "Body type requirements not met"); |
b32b8144 FG |
54 | |
55 | static_assert(is_body_reader<Body>::value, | |
92f5a8d4 | 56 | "BodyReader type requirements not met"); |
b32b8144 FG |
57 | |
58 | template<bool, class, class> | |
59 | friend class parser; | |
60 | ||
b32b8144 | 61 | message<isRequest, Body, basic_fields<Allocator>> m_; |
11fdf7f2 | 62 | typename Body::reader rd_; |
b32b8144 | 63 | bool rd_inited_ = false; |
92f5a8d4 | 64 | bool used_ = false; |
b32b8144 FG |
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 | ||
11fdf7f2 | 84 | /// Constructor (disallowed) |
b32b8144 FG |
85 | parser(parser const&) = delete; |
86 | ||
11fdf7f2 | 87 | /// Assignment (disallowed) |
b32b8144 FG |
88 | parser& operator=(parser const&) = delete; |
89 | ||
11fdf7f2 TL |
90 | /// Constructor (disallowed) |
91 | parser(parser&& other) = delete; | |
b32b8144 | 92 | |
11fdf7f2 TL |
93 | /// Constructor |
94 | parser(); | |
b32b8144 FG |
95 | |
96 | /** Constructor | |
97 | ||
98 | @param args Optional arguments forwarded to the | |
92f5a8d4 | 99 | @ref http::message constructor. |
b32b8144 FG |
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 | |
92f5a8d4 | 123 | initialized <em>BodyReader</em>, otherwise an exception is generated. |
b32b8144 FG |
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& | |
166 | 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& | |
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 | |
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 | |
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 | |
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: | |
11fdf7f2 TL |
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 | ||
b32b8144 FG |
330 | void |
331 | on_request_impl( | |
332 | verb method, | |
333 | string_view method_str, | |
334 | string_view target, | |
335 | int version, | |
92f5a8d4 TL |
336 | error_code& ec, |
337 | std::true_type) | |
b32b8144 | 338 | { |
92f5a8d4 TL |
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_) | |
b32b8144 | 347 | { |
92f5a8d4 TL |
348 | ec = error::stale_parser; |
349 | return; | |
b32b8144 | 350 | } |
92f5a8d4 TL |
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); | |
b32b8144 FG |
358 | m_.version(version); |
359 | } | |
360 | ||
92f5a8d4 TL |
361 | void |
362 | on_request_impl( | |
363 | verb, string_view, string_view, | |
364 | int, error_code&, std::false_type) | |
365 | { | |
366 | } | |
367 | ||
368 | void | |
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 | ||
b32b8144 FG |
381 | void |
382 | on_response_impl( | |
383 | int code, | |
384 | string_view reason, | |
385 | int version, | |
92f5a8d4 TL |
386 | error_code& ec, |
387 | std::true_type) | |
b32b8144 | 388 | { |
92f5a8d4 TL |
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_) | |
b32b8144 | 397 | { |
92f5a8d4 TL |
398 | ec = error::stale_parser; |
399 | return; | |
b32b8144 | 400 | } |
92f5a8d4 TL |
401 | used_ = true; |
402 | ||
403 | m_.result(code); | |
404 | m_.version(version); | |
405 | m_.reason(reason); | |
406 | } | |
407 | ||
408 | void | |
409 | on_response_impl( | |
410 | int, string_view, int, | |
411 | error_code&, std::false_type) | |
412 | { | |
413 | } | |
414 | ||
415 | void | |
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>{}); | |
b32b8144 FG |
425 | } |
426 | ||
427 | void | |
428 | on_field_impl( | |
429 | field name, | |
430 | string_view name_string, | |
431 | string_view value, | |
92f5a8d4 | 432 | error_code&) override |
b32b8144 | 433 | { |
92f5a8d4 | 434 | m_.insert(name, name_string, value); |
b32b8144 FG |
435 | } |
436 | ||
437 | void | |
92f5a8d4 | 438 | on_header_impl(error_code& ec) override |
b32b8144 | 439 | { |
92f5a8d4 | 440 | ec = {}; |
b32b8144 FG |
441 | } |
442 | ||
443 | void | |
444 | on_body_init_impl( | |
445 | boost::optional<std::uint64_t> const& content_length, | |
92f5a8d4 | 446 | error_code& ec) override |
b32b8144 | 447 | { |
11fdf7f2 | 448 | rd_.init(content_length, ec); |
b32b8144 FG |
449 | rd_inited_ = true; |
450 | } | |
451 | ||
452 | std::size_t | |
453 | on_body_impl( | |
454 | string_view body, | |
92f5a8d4 | 455 | error_code& ec) override |
b32b8144 | 456 | { |
92f5a8d4 | 457 | return rd_.put(net::buffer( |
b32b8144 FG |
458 | body.data(), body.size()), ec); |
459 | } | |
460 | ||
461 | void | |
462 | on_chunk_header_impl( | |
463 | std::uint64_t size, | |
464 | string_view extensions, | |
92f5a8d4 | 465 | error_code& ec) override |
b32b8144 FG |
466 | { |
467 | if(cb_h_) | |
468 | return cb_h_(size, extensions, ec); | |
b32b8144 FG |
469 | } |
470 | ||
471 | std::size_t | |
472 | on_chunk_body_impl( | |
473 | std::uint64_t remain, | |
474 | string_view body, | |
92f5a8d4 | 475 | error_code& ec) override |
b32b8144 FG |
476 | { |
477 | if(cb_b_) | |
478 | return cb_b_(remain, body, ec); | |
92f5a8d4 | 479 | return rd_.put(net::buffer( |
b32b8144 FG |
480 | body.data(), body.size()), ec); |
481 | } | |
482 | ||
483 | void | |
92f5a8d4 TL |
484 | on_finish_impl( |
485 | error_code& ec) override | |
b32b8144 | 486 | { |
11fdf7f2 | 487 | rd_.finish(ec); |
b32b8144 FG |
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 | ||
92f5a8d4 | 503 | #include <boost/beast/http/impl/parser.hpp> |
b32b8144 FG |
504 | |
505 | #endif |