GCC Code Coverage Report


Directory: libs/http_proto/
File: boost/http_proto/parser.hpp
Date: 2024-08-30 20:13:36
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 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 53656 got_header() const noexcept
171 {
172 53656 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 52302 is_complete() const noexcept
183 {
184 52302 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
2/2
✓ Branch 0 taken 582 times.
✓ Branch 1 taken 132 times.
1296 st_ == state::reset ||
208
1/2
✓ Branch 0 taken 582 times.
✗ Branch 1 not taken.
582 ( st_ == state::complete &&
209
2/2
✓ Branch 0 taken 342 times.
✓ Branch 1 taken 240 times.
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 9830 start()
228 {
229 9830 start_impl(false);
230 9825 }
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.
325
326 The returned buffer span will be invalidated if any member
327 function of the parser is subsequently called.
328 */
329 BOOST_HTTP_PROTO_DECL
330 const_buffers_type
331 pull_body();
332
333 /** Consumes bytes from the available body data.
334 */
335 BOOST_HTTP_PROTO_DECL
336 void
337 consume_body(std::size_t n);
338
339 /** Return the complete body as a contiguous character buffer.
340 */
341 BOOST_HTTP_PROTO_DECL
342 core::string_view
343 body() const noexcept;
344
345 //--------------------------------------------
346
347 /** Return any leftover data
348
349 This is used to forward unconsumed data
350 that could lie past the last message.
351 For example on a CONNECT request there
352 could be additional protocol-dependent
353 data that we want to retrieve.
354 */
355 // VFALCO rename to get_leftovers()?
356 BOOST_HTTP_PROTO_DECL
357 core::string_view
358 release_buffered_data() noexcept;
359
360 private:
361 friend class request_parser;
362 friend class response_parser;
363
364 detail::header const*
365 safe_get_header() const;
366 bool is_plain() const noexcept;
367 void on_headers(system::error_code&);
368 BOOST_HTTP_PROTO_DECL void on_set_body();
369 void init_dynamic(system::error_code&);
370
371 static constexpr unsigned buffers_N = 8;
372
373 enum class state
374 {
375 // order matters
376 reset,
377 start,
378 header,
379 body,
380 set_body,
381 complete
382 };
383
384 enum class how
385 {
386 in_place,
387 elastic,
388 sink
389 };
390
391 context& ctx_;
392 parser_service& svc_;
393
394 detail::workspace ws_;
395 detail::header h_;
396 std::uint64_t body_avail_ = 0;
397 std::uint64_t body_total_ = 0;
398 std::uint64_t payload_remain_ = 0;
399 std::size_t chunk_remain_ = 0;
400 std::size_t nprepare_ = 0;
401
402 // used to store initial headers + any potential overread
403 buffers::flat_buffer fb_;
404
405 // used for raw input once headers are read
406 buffers::circular_buffer cb0_;
407
408 // used for transformed output, if applicable
409 // can be empty/null
410 buffers::circular_buffer cb1_;
411
412 // used to provide stable storage when returning
413 // `mutable_buffers_type` from relevant functions
414 buffers::mutable_buffer_pair mbp_;
415
416 // used to provide stable storage when returning
417 // `const_buffers_type` from relevant functions
418 buffers::const_buffer_pair cbp_;
419
420 buffers::circular_buffer* body_buf_ = nullptr;
421 buffers::any_dynamic_buffer* eb_ = nullptr;
422 filter* filt_ = nullptr;
423 sink* sink_ = nullptr;
424
425 state st_ = state::start;
426 how how_ = how::in_place;
427 bool got_eof_ = false;
428 // bool need_more_;
429 bool head_response_ = false;;
430 bool needs_chunk_close_ = false;
431 };
432
433 //------------------------------------------------
434
435 /** Install the parser service.
436 */
437 BOOST_HTTP_PROTO_DECL
438 void
439 install_parser_service(
440 context& ctx,
441 parser::config_base const& cfg);
442
443 } // http_proto
444 } // boost
445
446 #include <boost/http_proto/impl/parser.hpp>
447
448 #endif
449