Line data Source code
1 : //
2 : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.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/cppalliance/http_proto
8 : //
9 :
10 : #ifndef BOOST_HTTP_PROTO_PARSER_HPP
11 : #define BOOST_HTTP_PROTO_PARSER_HPP
12 :
13 : #include <boost/http_proto/detail/config.hpp>
14 : #include <boost/http_proto/error.hpp>
15 : #include <boost/http_proto/header_limits.hpp>
16 : #include <boost/http_proto/sink.hpp>
17 : #include <boost/http_proto/detail/header.hpp>
18 : #include <boost/http_proto/detail/type_traits.hpp>
19 : #include <boost/http_proto/detail/workspace.hpp>
20 : #include <boost/buffers/circular_buffer.hpp>
21 : #include <boost/buffers/flat_buffer.hpp>
22 : #include <boost/buffers/mutable_buffer_pair.hpp>
23 : #include <boost/buffers/mutable_buffer_span.hpp>
24 : #include <boost/buffers/type_traits.hpp>
25 : #include <boost/buffers/any_dynamic_buffer.hpp>
26 : #include <boost/url/grammar/error.hpp>
27 : #include <cstddef>
28 : #include <cstdint>
29 : #include <functional>
30 : #include <memory>
31 : #include <utility>
32 :
33 : namespace boost {
34 : namespace http_proto {
35 :
36 : #ifndef BOOST_HTTP_PROTO_DOCS
37 : class parser_service;
38 : class request_parser;
39 : class response_parser;
40 : class context;
41 : namespace detail {
42 : class filter;
43 : } // detail
44 : #endif
45 :
46 : /** A parser for HTTP/1 messages.
47 :
48 : The parser is strict. Any malformed
49 : inputs according to the documented
50 : HTTP ABNFs is treated as an
51 : unrecoverable error.
52 : */
53 : class BOOST_SYMBOL_VISIBLE
54 : parser
55 : {
56 : BOOST_HTTP_PROTO_DECL
57 : parser(context& ctx, detail::kind);
58 :
59 : public:
60 : /** Parser configuration settings
61 :
62 : @see
63 : @li <a href="https://stackoverflow.com/questions/686217/maximum-on-http-header-values"
64 : >Maximum on HTTP header values (Stackoverflow)</a>
65 : */
66 : struct config_base
67 : {
68 : header_limits headers;
69 :
70 : /** Largest allowed size for a content body.
71 :
72 : The size of the body is measured
73 : after removing any transfer encodings,
74 : including a chunked encoding.
75 : */
76 : std::uint64_t body_limit = 64 * 1024;
77 :
78 : /** True if parser can decode deflate transfer and content encodings.
79 :
80 : The deflate decoder must already be
81 : installed thusly, or else an exception
82 : is thrown.
83 :
84 : @par Install Deflate Decoder
85 : @code
86 : deflate_decoder_service::config cfg;
87 : cfg.install( ctx );
88 : @endcode
89 : */
90 : bool apply_deflate_decoder = false;
91 :
92 : /** Minimum space for payload buffering.
93 :
94 : This value controls the following
95 : settings:
96 :
97 : @li The smallest allocated size of
98 : the buffers used for reading
99 : and decoding the payload.
100 :
101 : @li The lowest guaranteed size of
102 : an in-place body.
103 :
104 : @li The largest size used to reserve
105 : space in dynamic buffer bodies
106 : when the payload size is not
107 : known ahead of time.
108 :
109 : This cannot be zero, and this cannot
110 : be greater than @ref body_limit.
111 : */
112 : std::size_t min_buffer = 4096;
113 :
114 : /** Largest permissible output size in prepare.
115 :
116 : This cannot be zero.
117 : */
118 : std::size_t max_prepare = std::size_t(-1);
119 :
120 : /** Space to reserve for type-erasure.
121 : */
122 : std::size_t max_type_erase = 1024;
123 : };
124 :
125 : using mutable_buffers_type =
126 : buffers::mutable_buffer_span;
127 :
128 : using const_buffers_type =
129 : buffers::const_buffer_span;
130 :
131 : struct stream;
132 :
133 : //--------------------------------------------
134 : //
135 : // Special Members
136 : //
137 : //--------------------------------------------
138 :
139 : /** Destructor.
140 : */
141 : BOOST_HTTP_PROTO_DECL
142 : ~parser();
143 :
144 : /** Constructor (deleted)
145 : */
146 : parser(parser&&) = delete;
147 :
148 : /** Assignment (deleted)
149 : */
150 : parser& operator=(parser&&) = delete;
151 :
152 : //--------------------------------------------
153 : //
154 : // Observers
155 : //
156 : //--------------------------------------------
157 :
158 : #if 0
159 : /** Return true if any input was committed.
160 : */
161 : bool
162 : got_some() const noexcept
163 : {
164 : return st_ != state::need_start;
165 : }
166 : #endif
167 :
168 : /** Return true if the complete header was parsed.
169 : */
170 : bool
171 53704 : got_header() const noexcept
172 : {
173 53704 : return st_ > state::header;
174 : }
175 :
176 : /** Returns `true` if a complete message has been parsed.
177 :
178 : Calling @ref reset prepares the parser
179 : to process the next message in the stream.
180 :
181 : */
182 : bool
183 52344 : is_complete() const noexcept
184 : {
185 52344 : return st_ == state::complete;
186 : }
187 :
188 : /** Returns `true` if the end of the stream was reached.
189 :
190 : The end of the stream is encountered
191 : when one of the following conditions
192 : occurs:
193 :
194 : @li @ref commit_eof was called and there
195 : is no more data left to parse, or
196 :
197 : @li An unrecoverable error occurred
198 : during parsing.
199 :
200 : When the end of stream is reached, the
201 : function @ref reset must be called
202 : to start parsing a new stream.
203 : */
204 : bool
205 714 : is_end_of_stream() const noexcept
206 : {
207 : return
208 1296 : st_ == state::reset ||
209 582 : ( st_ == state::complete &&
210 1296 : got_eof_);
211 : }
212 :
213 : //--------------------------------------------
214 : //
215 : // Modifiers
216 : //
217 : //--------------------------------------------
218 :
219 : /** Prepare for a new stream.
220 : */
221 : BOOST_HTTP_PROTO_DECL
222 : void
223 : reset() noexcept;
224 :
225 : /** Prepare for the next message on the stream.
226 : */
227 : void
228 9872 : start()
229 : {
230 9872 : start_impl(false);
231 9867 : }
232 :
233 : private:
234 : // New message on the current stream
235 : BOOST_HTTP_PROTO_DECL void
236 : start_impl(bool head_response);
237 : public:
238 :
239 : /** Return the input buffer
240 : */
241 : BOOST_HTTP_PROTO_DECL
242 : mutable_buffers_type
243 : prepare();
244 :
245 : /** Commit bytes to the input buffer
246 : */
247 : BOOST_HTTP_PROTO_DECL
248 : void
249 : commit(
250 : std::size_t n);
251 :
252 : /** Indicate there will be no more input
253 :
254 : @par Postconditions
255 : All buffer sequences previously obtained
256 : by calling @ref prepare are invalidated.
257 : */
258 : BOOST_HTTP_PROTO_DECL
259 : void
260 : commit_eof();
261 :
262 : /** Parse pending input data
263 : */
264 : // VFALCO return result<void>?
265 : BOOST_HTTP_PROTO_DECL
266 : void
267 : parse(
268 : system::error_code& ec);
269 :
270 : /** Attach a body.
271 :
272 : This function attaches the specified elastic
273 : buffer as the storage for the message body.
274 : The parser acquires ownership of the object
275 : `eb` and destroys it when:
276 :
277 : @li @ref is_complete returns `true`, or
278 : @li @ref reset is called, or
279 : @li an unrecoverable parsing error occurs, or
280 : @li the parser is destroyed.
281 : */
282 : // VFALCO Should this function have
283 : // error_code& ec and call parse?
284 : template<class ElasticBuffer>
285 : #ifndef BOOST_HTTP_PROTO_DOCS
286 : typename std::enable_if<
287 : ! detail::is_reference_wrapper<
288 : ElasticBuffer>::value &&
289 : ! is_sink<ElasticBuffer>::value>::type
290 : #else
291 : void
292 : #endif
293 : set_body(ElasticBuffer&& eb);
294 :
295 : /** Attach a body.
296 :
297 : This function attaches the specified elastic
298 : buffer reference as the storage for the message body.
299 : Ownership is not transferred; the caller must
300 : ensure that the lifetime of the object
301 : reference by `eb` extends until:
302 :
303 : @li @ref is_complete returns `true`, or
304 : @li @ref reset is called, or
305 : @li an unrecoverable parsing error occurs, or
306 : @li the parser is destroyed.
307 : */
308 : template<class ElasticBuffer>
309 : void set_body(
310 : std::reference_wrapper<ElasticBuffer> eb);
311 :
312 : /** Attach a body
313 : */
314 : template<class Sink>
315 : #ifndef BOOST_HTTP_PROTO_DOCS
316 : typename std::enable_if<
317 : is_sink<Sink>::value,
318 : typename std::decay<Sink>::type
319 : >::type&
320 : #else
321 : typename std::decay<Sink>::type&
322 : #endif
323 : set_body(Sink&& sink);
324 :
325 : /** Return the available body data.
326 :
327 : The returned buffer span will be invalidated if any member
328 : function of the parser is subsequently called.
329 : */
330 : BOOST_HTTP_PROTO_DECL
331 : const_buffers_type
332 : pull_body();
333 :
334 : /** Consumes bytes from the available body data.
335 : */
336 : BOOST_HTTP_PROTO_DECL
337 : void
338 : consume_body(std::size_t n);
339 :
340 : /** Return the complete body as a contiguous character buffer.
341 : */
342 : BOOST_HTTP_PROTO_DECL
343 : core::string_view
344 : body() const noexcept;
345 :
346 : //--------------------------------------------
347 :
348 : /** Return any leftover data
349 :
350 : This is used to forward unconsumed data
351 : that could lie past the last message.
352 : For example on a CONNECT request there
353 : could be additional protocol-dependent
354 : data that we want to retrieve.
355 : */
356 : // VFALCO rename to get_leftovers()?
357 : BOOST_HTTP_PROTO_DECL
358 : core::string_view
359 : release_buffered_data() noexcept;
360 :
361 : private:
362 : friend class request_parser;
363 : friend class response_parser;
364 :
365 : detail::header const*
366 : safe_get_header() const;
367 : bool is_plain() const noexcept;
368 : void on_headers(system::error_code&);
369 : BOOST_HTTP_PROTO_DECL void on_set_body();
370 : void init_dynamic(system::error_code&);
371 :
372 : static constexpr unsigned buffers_N = 8;
373 :
374 : enum class state
375 : {
376 : // order matters
377 : reset,
378 : start,
379 : header,
380 : body,
381 : set_body,
382 : complete
383 : };
384 :
385 : enum class how
386 : {
387 : in_place,
388 : elastic,
389 : sink
390 : };
391 :
392 : context& ctx_;
393 : parser_service& svc_;
394 :
395 : detail::workspace ws_;
396 : detail::header h_;
397 : std::uint64_t body_avail_ = 0;
398 : std::uint64_t body_total_ = 0;
399 : std::uint64_t payload_remain_ = 0;
400 : std::uint64_t chunk_remain_ = 0;
401 : std::size_t nprepare_ = 0;
402 :
403 : // used to store initial headers + any potential overread
404 : buffers::flat_buffer fb_;
405 :
406 : // used for raw input once headers are read
407 : buffers::circular_buffer cb0_;
408 :
409 : // used for transformed output, if applicable
410 : // can be empty/null
411 : buffers::circular_buffer cb1_;
412 :
413 : // used to provide stable storage when returning
414 : // `mutable_buffers_type` from relevant functions
415 : buffers::mutable_buffer_pair mbp_;
416 :
417 : // used to provide stable storage when returning
418 : // `const_buffers_type` from relevant functions
419 : buffers::const_buffer_pair cbp_;
420 :
421 : buffers::circular_buffer* body_buf_ = nullptr;
422 : buffers::any_dynamic_buffer* eb_ = nullptr;
423 : detail::filter* filt_ = nullptr;
424 : sink* sink_ = nullptr;
425 :
426 : state st_ = state::start;
427 : how how_ = how::in_place;
428 : bool got_eof_ = false;
429 : // bool need_more_;
430 : bool head_response_ = false;
431 : bool needs_chunk_close_ = false;
432 : bool trailer_headers_ = false;
433 : };
434 :
435 : //------------------------------------------------
436 :
437 : /** Install the parser service.
438 : */
439 : BOOST_HTTP_PROTO_DECL
440 : void
441 : install_parser_service(
442 : context& ctx,
443 : parser::config_base const& cfg);
444 :
445 : } // http_proto
446 : } // boost
447 :
448 : #include <boost/http_proto/impl/parser.hpp>
449 :
450 : #endif
|