GCC Code Coverage Report


Directory: libs/http_proto/
File: boost/http_proto/parser.hpp
Date: 2024-09-13 19:44:07
Exec Total Coverage
Lines: 11 11 100.0%
Functions: 4 4 100.0%
Branches: 5 6 83.3%

Line Branch Exec Source
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
2/2
✓ Branch 0 taken 582 times.
✓ Branch 1 taken 132 times.
1296 st_ == state::reset ||
209
1/2
✓ Branch 0 taken 582 times.
✗ Branch 1 not taken.
582 ( st_ == state::complete &&
210
2/2
✓ Branch 0 taken 342 times.
✓ Branch 1 taken 240 times.
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
451