GCC Code Coverage Report


Directory: libs/http_proto/
File: include/boost/http_proto/parser.hpp
Date: 2024-12-31 05:34:49
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 // 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
2/2
✓ Branch 0 taken 582 times.
✓ Branch 1 taken 132 times.
1296 st_ == state::reset ||
212
1/2
✓ Branch 0 taken 582 times.
✗ Branch 1 not taken.
582 ( st_ == state::complete &&
213
2/2
✓ Branch 0 taken 342 times.
✓ Branch 1 taken 240 times.
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
454