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