]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/beast/http/parser.hpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / boost / beast / http / parser.hpp
CommitLineData
b32b8144
FG
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#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
24namespace boost {
25namespace beast {
26namespace 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 @b Body.
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*/
45template<
46 bool isRequest,
47 class Body,
48 class Allocator = std::allocator<char>>
49class parser
50 : public basic_parser<isRequest,
51 parser<isRequest, Body, Allocator>>
52{
53 static_assert(is_body<Body>::value,
54 "Body requirements not met");
55
56 static_assert(is_body_reader<Body>::value,
57 "BodyReader requirements not met");
58
59 template<bool, class, class>
60 friend class parser;
61
62 using base_type = basic_parser<isRequest,
63 parser<isRequest, Body, Allocator>>;
64
65 message<isRequest, Body, basic_fields<Allocator>> m_;
66 typename Body::reader wr_;
67 bool rd_inited_ = false;
68
69 std::function<void(
70 std::uint64_t,
71 string_view,
72 error_code&)> cb_h_;
73
74 std::function<std::size_t(
75 std::uint64_t,
76 string_view,
77 error_code&)> cb_b_;
78
79public:
80 /// The type of message returned by the parser
81 using value_type =
82 message<isRequest, Body, basic_fields<Allocator>>;
83
84 /// Destructor
85 ~parser() = default;
86
87 /// Constructor
88 parser();
89
90 /// Constructor
91 parser(parser const&) = delete;
92
93 /// Assignment
94 parser& operator=(parser const&) = delete;
95
96 /** Constructor
97
98 After the move, the only valid operation
99 on the moved-from object is destruction.
100 */
101 parser(parser&& other) = default;
102
103 /** Constructor
104
105 @param args Optional arguments forwarded to the
106 @ref http::header constructor.
107
108 @note This function participates in overload
109 resolution only if the first argument is not a
110 @ref parser.
111 */
112#if BOOST_BEAST_DOXYGEN
113 template<class... Args>
114 explicit
115 parser(Args&&... args);
116#else
117 template<class Arg1, class... ArgN,
118 class = typename std::enable_if<
119 ! detail::is_parser<typename
120 std::decay<Arg1>::type>::value>::type>
121 explicit
122 parser(Arg1&& arg1, ArgN&&... argn);
123#endif
124
125 /** Construct a parser from another parser, changing the Body type.
126
127 This constructs a new parser by move constructing the
128 header from another parser with a different body type. The
129 constructed-from parser must not have any parsed body octets or
130 initialized @b BodyReader, otherwise an exception is generated.
131
132 @par Example
133 @code
134 // Deferred body type commitment
135 request_parser<empty_body> req0;
136 ...
137 request_parser<string_body> req{std::move(req0)};
138 @endcode
139
140 If an exception is thrown, the state of the constructed-from
141 parser is undefined.
142
143 @param parser The other parser to construct from. After
144 this call returns, the constructed-from parser may only
145 be destroyed.
146
147 @param args Optional arguments forwarded to the message
148 constructor.
149
150 @throws std::invalid_argument Thrown when the constructed-from
151 parser has already initialized a body reader.
152
153 @note This function participates in overload resolution only
154 if the other parser uses a different body type.
155 */
156#if BOOST_BEAST_DOXYGEN
157 template<class OtherBody, class... Args>
158#else
159 template<class OtherBody, class... Args,
160 class = typename std::enable_if<
161 ! std::is_same<Body, OtherBody>::value>::type>
162#endif
163 explicit
164 parser(parser<isRequest, OtherBody,
165 Allocator>&& parser, Args&&... args);
166
167 /** Returns the parsed message.
168
169 Depending on the parser's progress,
170 parts of this object may be incomplete.
171 */
172 value_type const&
173 get() const
174 {
175 return m_;
176 }
177
178 /** Returns the parsed message.
179
180 Depending on the parser's progress,
181 parts of this object may be incomplete.
182 */
183 value_type&
184 get()
185 {
186 return m_;
187 }
188
189 /** Returns ownership of the parsed message.
190
191 Ownership is transferred to the caller.
192 Depending on the parser's progress,
193 parts of this object may be incomplete.
194
195 @par Requires
196
197 @ref value_type is @b MoveConstructible
198 */
199 value_type
200 release()
201 {
202 static_assert(std::is_move_constructible<decltype(m_)>::value,
203 "MoveConstructible requirements not met");
204 return std::move(m_);
205 }
206
207 /** Set a callback to be invoked on each chunk header.
208
209 The callback will be invoked once for every chunk in the message
210 payload, as well as once for the last chunk. The invocation
211 happens after the chunk header is available but before any body
212 octets have been parsed.
213
214 The extensions are provided in raw, validated form, use
215 @ref chunk_extensions::parse to parse the extensions into a
216 structured container for easier access.
217 The implementation type-erases the callback without requiring
218 a dynamic allocation. For this reason, the callback object is
219 passed by a non-constant reference.
220
221 @par Example
222 @code
223 auto callback =
224 [](std::uint64_t size, string_view extensions, error_code& ec)
225 {
226 //...
227 };
228 parser.on_chunk_header(callback);
229 @endcode
230
231 @param cb The function to set, which must be invocable with
232 this equivalent signature:
233 @code
234 void
235 on_chunk_header(
236 std::uint64_t size, // Size of the chunk, zero for the last chunk
237 string_view extensions, // The chunk-extensions in raw form
238 error_code& ec); // May be set by the callback to indicate an error
239 @endcode
240 */
241 template<class Callback>
242 void
243 on_chunk_header(Callback& cb)
244 {
245 // Callback may not be constant, caller is responsible for
246 // managing the lifetime of the callback. Copies are not made.
247 BOOST_STATIC_ASSERT(! std::is_const<Callback>::value);
248
249 // Can't set the callback after receiving any chunk data!
250 BOOST_ASSERT(! rd_inited_);
251
252 cb_h_ = std::ref(cb);
253 }
254
255 /** Set a callback to be invoked on chunk body data
256
257 The provided function object will be invoked one or more times
258 to provide buffers corresponding to the chunk body for the current
259 chunk. The callback receives the number of octets remaining in this
260 chunk body including the octets in the buffer provided.
261
262 The callback must return the number of octets actually consumed.
263 Any octets not consumed will be presented again in a subsequent
264 invocation of the callback.
265 The implementation type-erases the callback without requiring
266 a dynamic allocation. For this reason, the callback object is
267 passed by a non-constant reference.
268
269 @par Example
270 @code
271 auto callback =
272 [](std::uint64_t remain, string_view body, error_code& ec)
273 {
274 //...
275 };
276 parser.on_chunk_body(callback);
277 @endcode
278
279 @param cb The function to set, which must be invocable with
280 this equivalent signature:
281 @code
282 std::size_t
283 on_chunk_header(
284 std::uint64_t remain, // Octets remaining in this chunk, includes `body`
285 string_view body, // A buffer holding some or all of the remainder of the chunk body
286 error_code& ec); // May be set by the callback to indicate an error
287 @endcode
288 */
289 template<class Callback>
290 void
291 on_chunk_body(Callback& cb)
292 {
293 // Callback may not be constant, caller is responsible for
294 // managing the lifetime of the callback. Copies are not made.
295 BOOST_STATIC_ASSERT(! std::is_const<Callback>::value);
296
297 // Can't set the callback after receiving any chunk data!
298 BOOST_ASSERT(! rd_inited_);
299
300 cb_b_ = std::ref(cb);
301 }
302
303private:
304 friend class basic_parser<isRequest, parser>;
305
306 void
307 on_request_impl(
308 verb method,
309 string_view method_str,
310 string_view target,
311 int version,
312 error_code& ec)
313 {
314 try
315 {
316 m_.target(target);
317 if(method != verb::unknown)
318 m_.method(method);
319 else
320 m_.method_string(method_str);
321 ec.assign(0, ec.category());
322 }
323 catch(std::bad_alloc const&)
324 {
325 ec = error::bad_alloc;
326 }
327 m_.version(version);
328 }
329
330 void
331 on_response_impl(
332 int code,
333 string_view reason,
334 int version,
335 error_code& ec)
336 {
337 m_.result(code);
338 m_.version(version);
339 try
340 {
341 m_.reason(reason);
342 ec.assign(0, ec.category());
343 }
344 catch(std::bad_alloc const&)
345 {
346 ec = error::bad_alloc;
347 }
348 }
349
350 void
351 on_field_impl(
352 field name,
353 string_view name_string,
354 string_view value,
355 error_code& ec)
356 {
357 try
358 {
359 m_.insert(name, name_string, value);
360 ec.assign(0, ec.category());
361 }
362 catch(std::bad_alloc const&)
363 {
364 ec = error::bad_alloc;
365 }
366 }
367
368 void
369 on_header_impl(error_code& ec)
370 {
371 ec.assign(0, ec.category());
372 }
373
374 void
375 on_body_init_impl(
376 boost::optional<std::uint64_t> const& content_length,
377 error_code& ec)
378 {
379 wr_.init(content_length, ec);
380 rd_inited_ = true;
381 }
382
383 std::size_t
384 on_body_impl(
385 string_view body,
386 error_code& ec)
387 {
388 return wr_.put(boost::asio::buffer(
389 body.data(), body.size()), ec);
390 }
391
392 void
393 on_chunk_header_impl(
394 std::uint64_t size,
395 string_view extensions,
396 error_code& ec)
397 {
398 if(cb_h_)
399 return cb_h_(size, extensions, ec);
400 ec.assign(0, ec.category());
401 }
402
403 std::size_t
404 on_chunk_body_impl(
405 std::uint64_t remain,
406 string_view body,
407 error_code& ec)
408 {
409 if(cb_b_)
410 return cb_b_(remain, body, ec);
411 return wr_.put(boost::asio::buffer(
412 body.data(), body.size()), ec);
413 }
414
415 void
416 on_finish_impl(error_code& ec)
417 {
418 wr_.finish(ec);
419 }
420};
421
422/// An HTTP/1 parser for producing a request message.
423template<class Body, class Allocator = std::allocator<char>>
424using request_parser = parser<true, Body, Allocator>;
425
426/// An HTTP/1 parser for producing a response message.
427template<class Body, class Allocator = std::allocator<char>>
428using response_parser = parser<false, Body, Allocator>;
429
430} // http
431} // beast
432} // boost
433
434#include <boost/beast/http/impl/parser.ipp>
435
436#endif