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