100.00% Lines (17/17) 100.00% Functions (7/7)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3   // Copyright (c) 2024 Mohammad Nejati 3   // Copyright (c) 2024 Mohammad Nejati
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 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) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/http 8   // Official repository: https://github.com/cppalliance/http
9   // 9   //
10   10  
11   #ifndef BOOST_HTTP_PARSER_HPP 11   #ifndef BOOST_HTTP_PARSER_HPP
12   #define BOOST_HTTP_PARSER_HPP 12   #define BOOST_HTTP_PARSER_HPP
13   13  
14   #include <boost/http/config.hpp> 14   #include <boost/http/config.hpp>
15   #include <boost/http/detail/header.hpp> 15   #include <boost/http/detail/header.hpp>
16   #include <boost/http/detail/type_traits.hpp> 16   #include <boost/http/detail/type_traits.hpp>
17   #include <boost/http/error.hpp> 17   #include <boost/http/error.hpp>
18   18  
19   #include <boost/capy/buffers/buffer_copy.hpp> 19   #include <boost/capy/buffers/buffer_copy.hpp>
20   #include <boost/capy/buffers/buffer_slice.hpp> 20   #include <boost/capy/buffers/buffer_slice.hpp>
21   #include <boost/capy/concept/read_stream.hpp> 21   #include <boost/capy/concept/read_stream.hpp>
22   #include <boost/capy/concept/write_sink.hpp> 22   #include <boost/capy/concept/write_sink.hpp>
23   #include <boost/capy/cond.hpp> 23   #include <boost/capy/cond.hpp>
24   #include <boost/capy/error.hpp> 24   #include <boost/capy/error.hpp>
25   #include <boost/capy/io_task.hpp> 25   #include <boost/capy/io_task.hpp>
26   #include <boost/core/span.hpp> 26   #include <boost/core/span.hpp>
27   27  
28   #include <array> 28   #include <array>
29   #include <cstddef> 29   #include <cstddef>
30   #include <cstdint> 30   #include <cstdint>
31   #include <memory> 31   #include <memory>
32   32  
33   namespace boost { 33   namespace boost {
34   namespace http { 34   namespace http {
35   35  
36   // Forward declaration 36   // Forward declaration
37   class request_parser; 37   class request_parser;
38   class response_parser; 38   class response_parser;
39   class static_request; 39   class static_request;
40   class static_response; 40   class static_response;
41   41  
42   //------------------------------------------------ 42   //------------------------------------------------
43   43  
44   /** A parser for HTTP/1 messages. 44   /** A parser for HTTP/1 messages.
45   45  
46   This parser uses a single block of memory allocated 46   This parser uses a single block of memory allocated
47   during construction and guarantees it will never 47   during construction and guarantees it will never
48   exceed the specified size. This space is reused for 48   exceed the specified size. This space is reused for
49   parsing multiple HTTP messages ( one at a time ). 49   parsing multiple HTTP messages ( one at a time ).
50   50  
51   The allocated space is used for: 51   The allocated space is used for:
52   52  
53   @li Buffering raw input from a socket 53   @li Buffering raw input from a socket
54   @li Storing HTTP headers with O(1) access to 54   @li Storing HTTP headers with O(1) access to
55   method, target, and status code 55   method, target, and status code
56   @li Storing all or part of an HTTP message body 56   @li Storing all or part of an HTTP message body
57   @li Storing state for inflate algorithms 57   @li Storing state for inflate algorithms
58   58  
59   The parser is strict. Any malformed input according 59   The parser is strict. Any malformed input according
60   to the HTTP ABNFs is treated as an unrecoverable 60   to the HTTP ABNFs is treated as an unrecoverable
61   error. 61   error.
62   62  
63   @see 63   @see
64   @ref response_parser, 64   @ref response_parser,
65   @ref request_parser. 65   @ref request_parser.
66   */ 66   */
67   class parser 67   class parser
68   { 68   {
69   public: 69   public:
70   template<capy::ReadStream Stream> 70   template<capy::ReadStream Stream>
71   class source; 71   class source;
72   72  
73   /// Buffer type returned from @ref prepare. 73   /// Buffer type returned from @ref prepare.
74   using mutable_buffers_type = 74   using mutable_buffers_type =
75   boost::span<capy::mutable_buffer const>; 75   boost::span<capy::mutable_buffer const>;
76   76  
77   /// Buffer type returned from @ref pull_body. 77   /// Buffer type returned from @ref pull_body.
78   using const_buffers_type = 78   using const_buffers_type =
79   boost::span<capy::const_buffer const>; 79   boost::span<capy::const_buffer const>;
80   80  
81   //-------------------------------------------- 81   //--------------------------------------------
82   // 82   //
83   // Observers 83   // Observers
84   // 84   //
85   //-------------------------------------------- 85   //--------------------------------------------
86   86  
87   /// Check if a complete header has been parsed. 87   /// Check if a complete header has been parsed.
88   BOOST_HTTP_DECL 88   BOOST_HTTP_DECL
89   bool 89   bool
90   got_header() const noexcept; 90   got_header() const noexcept;
91   91  
92   /// Check if a complete message has been parsed. 92   /// Check if a complete message has been parsed.
93   BOOST_HTTP_DECL 93   BOOST_HTTP_DECL
94   bool 94   bool
95   is_complete() const noexcept; 95   is_complete() const noexcept;
96   96  
97   //-------------------------------------------- 97   //--------------------------------------------
98   // 98   //
99   // Modifiers 99   // Modifiers
100   // 100   //
101   //-------------------------------------------- 101   //--------------------------------------------
102   102  
103   /// Prepare for a new stream. 103   /// Prepare for a new stream.
104   BOOST_HTTP_DECL 104   BOOST_HTTP_DECL
105   void 105   void
106   reset() noexcept; 106   reset() noexcept;
107   107  
108   /** Prepare for a new message. 108   /** Prepare for a new message.
109   109  
110   @par Preconditions 110   @par Preconditions
111   Either this is the first message in the stream, 111   Either this is the first message in the stream,
112   or the previous message has been fully parsed. 112   or the previous message has been fully parsed.
113   */ 113   */
114   BOOST_HTTP_DECL 114   BOOST_HTTP_DECL
115   void 115   void
116   start(); 116   start();
117   117  
118   /** Return a buffer for reading input. 118   /** Return a buffer for reading input.
119   119  
120   After writing to the buffer, call @ref commit 120   After writing to the buffer, call @ref commit
121   with the number of bytes written. 121   with the number of bytes written.
122   122  
123   @par Preconditions 123   @par Preconditions
124   @ref parse returned @ref condition::need_more_input. 124   @ref parse returned @ref condition::need_more_input.
125   125  
126   @par Postconditions 126   @par Postconditions
127   A call to @ref commit or @ref commit_eof is 127   A call to @ref commit or @ref commit_eof is
128   required before calling @ref prepare again. 128   required before calling @ref prepare again.
129   129  
130   @par Exception Safety 130   @par Exception Safety
131   Strong guarantee. 131   Strong guarantee.
132   132  
133   @return A non-empty mutable buffer. 133   @return A non-empty mutable buffer.
134   134  
135   @see @ref commit, @ref commit_eof. 135   @see @ref commit, @ref commit_eof.
136   */ 136   */
137   BOOST_HTTP_DECL 137   BOOST_HTTP_DECL
138   mutable_buffers_type 138   mutable_buffers_type
139   prepare(); 139   prepare();
140   140  
141   /** Commit bytes to the input buffer. 141   /** Commit bytes to the input buffer.
142   142  
143   @par Preconditions 143   @par Preconditions
144   @li `n <= capy::buffer_size( this->prepare() )` 144   @li `n <= capy::buffer_size( this->prepare() )`
145   @li No prior call to @ref commit or @ref commit_eof 145   @li No prior call to @ref commit or @ref commit_eof
146   since the last @ref prepare 146   since the last @ref prepare
147   147  
148   @par Postconditions 148   @par Postconditions
149   Buffers from @ref prepare are invalidated. 149   Buffers from @ref prepare are invalidated.
150   150  
151   @par Exception Safety 151   @par Exception Safety
152   Strong guarantee. 152   Strong guarantee.
153   153  
154   @param n The number of bytes written. 154   @param n The number of bytes written.
155   155  
156   @see @ref parse, @ref prepare. 156   @see @ref parse, @ref prepare.
157   */ 157   */
158   BOOST_HTTP_DECL 158   BOOST_HTTP_DECL
159   void 159   void
160   commit( 160   commit(
161   std::size_t n); 161   std::size_t n);
162   162  
163   /** Indicate end of input. 163   /** Indicate end of input.
164   164  
165   Call this when the underlying stream has closed 165   Call this when the underlying stream has closed
166   and no more data will arrive. 166   and no more data will arrive.
167   167  
168   @par Postconditions 168   @par Postconditions
169   Buffers from @ref prepare are invalidated. 169   Buffers from @ref prepare are invalidated.
170   170  
171   @par Exception Safety 171   @par Exception Safety
172   Strong guarantee. 172   Strong guarantee.
173   173  
174   @see @ref parse, @ref prepare. 174   @see @ref parse, @ref prepare.
175   */ 175   */
176   BOOST_HTTP_DECL 176   BOOST_HTTP_DECL
177   void 177   void
178   commit_eof(); 178   commit_eof();
179   179  
180   /** Parse pending input data. 180   /** Parse pending input data.
181   181  
182   Returns immediately after the header is fully 182   Returns immediately after the header is fully
183   parsed to allow @ref set_body_limit to be called 183   parsed to allow @ref set_body_limit to be called
184   before body parsing begins. If an error occurs 184   before body parsing begins. If an error occurs
185   during body parsing, the parsed header remains 185   during body parsing, the parsed header remains
186   valid and accessible. 186   valid and accessible.
187   187  
188   When `ec == condition::need_more_input`, read 188   When `ec == condition::need_more_input`, read
189   more data and call @ref commit before calling 189   more data and call @ref commit before calling
190   this function again. 190   this function again.
191   191  
192   When `ec == error::end_of_stream`, the stream 192   When `ec == error::end_of_stream`, the stream
193   closed cleanly. Call @ref reset to reuse the 193   closed cleanly. Call @ref reset to reuse the
194   parser for a new stream. 194   parser for a new stream.
195   195  
196   @param ec Set to the error, if any occurred. 196   @param ec Set to the error, if any occurred.
197   197  
198   @see @ref start, @ref prepare, @ref commit. 198   @see @ref start, @ref prepare, @ref commit.
199   */ 199   */
200   BOOST_HTTP_DECL 200   BOOST_HTTP_DECL
201   void 201   void
202   parse( 202   parse(
203   system::error_code& ec); 203   system::error_code& ec);
204   204  
205   /** Set maximum body size for the current message. 205   /** Set maximum body size for the current message.
206   206  
207   Overrides @ref parser_config::body_limit for this 207   Overrides @ref parser_config::body_limit for this
208   message only. The limit resets to the default 208   message only. The limit resets to the default
209   for subsequent messages. 209   for subsequent messages.
210   210  
211   @par Preconditions 211   @par Preconditions
212   `this->got_header() == true` and body parsing 212   `this->got_header() == true` and body parsing
213   has not started. 213   has not started.
214   214  
215   @par Exception Safety 215   @par Exception Safety
216   Strong guarantee. 216   Strong guarantee.
217   217  
218   @param n The body size limit in bytes. 218   @param n The body size limit in bytes.
219   219  
220   @see @ref parser_config::body_limit. 220   @see @ref parser_config::body_limit.
221   */ 221   */
222   BOOST_HTTP_DECL 222   BOOST_HTTP_DECL
223   void 223   void
224   set_body_limit(std::uint64_t n); 224   set_body_limit(std::uint64_t n);
225   225  
226   /** Return available body data. 226   /** Return available body data.
227   227  
228   Use this to incrementally process body data. 228   Use this to incrementally process body data.
229   Call @ref consume_body after processing to 229   Call @ref consume_body after processing to
230   release the buffer space. 230   release the buffer space.
231   231  
232   @par Example 232   @par Example
233   @code 233   @code
234   request_parser pr( ctx ); 234   request_parser pr( ctx );
235   pr.start(); 235   pr.start();
236   co_await pr.read_header( stream ); 236   co_await pr.read_header( stream );
237   237  
238   while( ! pr.is_complete() ) 238   while( ! pr.is_complete() )
239   { 239   {
240   co_await read_some( stream, pr ); 240   co_await read_some( stream, pr );
241   auto cbs = pr.pull_body(); 241   auto cbs = pr.pull_body();
242   // process cbs ... 242   // process cbs ...
243   pr.consume_body( capy::buffer_size( cbs ) ); 243   pr.consume_body( capy::buffer_size( cbs ) );
244   } 244   }
245   @endcode 245   @endcode
246   246  
247   @par Preconditions 247   @par Preconditions
248   `this->got_header() == true` 248   `this->got_header() == true`
249   249  
250   @par Postconditions 250   @par Postconditions
251   The returned buffer is invalidated by any 251   The returned buffer is invalidated by any
252   modifying member function. 252   modifying member function.
253   253  
254   @par Exception Safety 254   @par Exception Safety
255   Strong guarantee. 255   Strong guarantee.
256   256  
257   @return Buffers containing available body data. 257   @return Buffers containing available body data.
258   258  
259   @see @ref consume_body. 259   @see @ref consume_body.
260   */ 260   */
261   BOOST_HTTP_DECL 261   BOOST_HTTP_DECL
262   const_buffers_type 262   const_buffers_type
263   pull_body(); 263   pull_body();
264   264  
265   /** Consume bytes from available body data. 265   /** Consume bytes from available body data.
266   266  
267   @par Preconditions 267   @par Preconditions
268   `n <= capy::buffer_size( this->pull_body() )` 268   `n <= capy::buffer_size( this->pull_body() )`
269   269  
270   @par Exception Safety 270   @par Exception Safety
271   Strong guarantee. 271   Strong guarantee.
272   272  
273   @param n The number of bytes to consume. 273   @param n The number of bytes to consume.
274   274  
275   @see @ref pull_body. 275   @see @ref pull_body.
276   */ 276   */
277   BOOST_HTTP_DECL 277   BOOST_HTTP_DECL
278   void 278   void
279   consume_body(std::size_t n); 279   consume_body(std::size_t n);
280   280  
281   /** Return the complete body. 281   /** Return the complete body.
282   282  
283   Use this when the entire message fits within 283   Use this when the entire message fits within
284   the parser's internal buffer. 284   the parser's internal buffer.
285   285  
286   @par Example 286   @par Example
287   @code 287   @code
288   request_parser pr( ctx ); 288   request_parser pr( ctx );
289   pr.start(); 289   pr.start();
290   co_await pr.read_header( stream ); 290   co_await pr.read_header( stream );
291   // ... read entire body ... 291   // ... read entire body ...
292   core::string_view body = pr.body(); 292   core::string_view body = pr.body();
293   @endcode 293   @endcode
294   294  
295   @par Preconditions 295   @par Preconditions
296   @li `this->is_complete() == true` 296   @li `this->is_complete() == true`
297   @li No previous call to @ref consume_body 297   @li No previous call to @ref consume_body
298   298  
299   @par Exception Safety 299   @par Exception Safety
300   Strong guarantee. 300   Strong guarantee.
301   301  
302   @return A string view of the complete body. 302   @return A string view of the complete body.
303   303  
304   @see @ref is_complete. 304   @see @ref is_complete.
305   */ 305   */
306   BOOST_HTTP_DECL 306   BOOST_HTTP_DECL
307   core::string_view 307   core::string_view
308   body() const; 308   body() const;
309   309  
  310 + /** Return true if data is buffered past the message.
  311 +
  312 + After a complete message, returns true when the
  313 + parser's buffer still holds octets that lie
  314 + beyond it, such as the start of a pipelined
  315 + message or data the peer sent past the message
  316 + framing. Returns false before the message is
  317 + complete.
  318 +
  319 + This does not include the message body, which is
  320 + retrieved separately via @ref body or
  321 + @ref pull_body.
  322 +
  323 + @return true if octets remain buffered past the
  324 + completed message.
  325 +
  326 + @see @ref is_complete, @ref release_buffered_data.
  327 + */
  328 + BOOST_HTTP_DECL
  329 + bool
  330 + has_buffered_data() const noexcept;
  331 +
310   /** Return unconsumed data past the last message. 332   /** Return unconsumed data past the last message.
311   333  
312   Use this after an upgrade or CONNECT request 334   Use this after an upgrade or CONNECT request
313   to retrieve protocol-dependent data that 335   to retrieve protocol-dependent data that
314   follows the HTTP message. 336   follows the HTTP message.
315   337  
316   @return A string view of leftover data. 338   @return A string view of leftover data.
317   339  
318   @see @ref metadata::upgrade, @ref metadata::connection. 340   @see @ref metadata::upgrade, @ref metadata::connection.
319   */ 341   */
320   BOOST_HTTP_DECL 342   BOOST_HTTP_DECL
321   core::string_view 343   core::string_view
322   release_buffered_data() noexcept; 344   release_buffered_data() noexcept;
323   345  
324   /** Asynchronously read the HTTP headers. 346   /** Asynchronously read the HTTP headers.
325   347  
326   Reads from the stream until the headers are 348   Reads from the stream until the headers are
327   complete or an error occurs. 349   complete or an error occurs.
328   350  
329   @par Preconditions 351   @par Preconditions
330   @li @ref reset has been called 352   @li @ref reset has been called
331   @li @ref start has been called 353   @li @ref start has been called
332   354  
333   @param stream The stream to read from. 355   @param stream The stream to read from.
334   356  
335   @return An awaitable yielding `(error_code)`. 357   @return An awaitable yielding `(error_code)`.
336   358  
337   @see @ref read. 359   @see @ref read.
338   */ 360   */
339   template<capy::ReadStream Stream> 361   template<capy::ReadStream Stream>
340   capy::io_task<> 362   capy::io_task<>
341   read_header(Stream& stream); 363   read_header(Stream& stream);
342   364  
343   /** Asynchronously read a complete HTTP message. 365   /** Asynchronously read a complete HTTP message.
344   366  
345   Reads from the stream until the message is fully 367   Reads from the stream until the message is fully
346   parsed or an error occurs. The body is accumulated 368   parsed or an error occurs. The body is accumulated
347   in the parser's internal buffer and can be retrieved 369   in the parser's internal buffer and can be retrieved
348   via @ref body after completion. 370   via @ref body after completion.
349   371  
350   If the parser's internal buffer fills before the 372   If the parser's internal buffer fills before the
351   message is complete, the operation completes with 373   message is complete, the operation completes with
352   @ref error::in_place_overflow. 374   @ref error::in_place_overflow.
353   375  
354   @par Preconditions 376   @par Preconditions
355   @li @ref reset has been called 377   @li @ref reset has been called
356   @li @ref start has been called 378   @li @ref start has been called
357   379  
358   @param stream The stream to read from. 380   @param stream The stream to read from.
359   381  
360   @return An awaitable yielding `(error_code)`. 382   @return An awaitable yielding `(error_code)`.
361   383  
362   @see @ref body, @ref read_header. 384   @see @ref body, @ref read_header.
363   */ 385   */
364   template<capy::ReadStream Stream> 386   template<capy::ReadStream Stream>
365   capy::io_task<> 387   capy::io_task<>
366   read(Stream& stream); 388   read(Stream& stream);
367   389  
368   /** Asynchronously read body data into buffers. 390   /** Asynchronously read body data into buffers.
369   391  
370   Reads from the stream and copies body data into 392   Reads from the stream and copies body data into
371   the provided buffers with complete-fill semantics. 393   the provided buffers with complete-fill semantics.
372   Returns `capy::error::eof` when the body is complete. 394   Returns `capy::error::eof` when the body is complete.
373   395  
374   @par Preconditions 396   @par Preconditions
375   @li @ref reset has been called 397   @li @ref reset has been called
376   @li @ref start has been called 398   @li @ref start has been called
377   399  
378   @param stream The stream to read from. 400   @param stream The stream to read from.
379   401  
380   @param buffers The buffers to read into. 402   @param buffers The buffers to read into.
381   403  
382   @return An awaitable yielding `(error_code,std::size_t)`. 404   @return An awaitable yielding `(error_code,std::size_t)`.
383   405  
384   @see @ref read_header. 406   @see @ref read_header.
385   */ 407   */
386   template<capy::ReadStream Stream, capy::MutableBufferSequence MB> 408   template<capy::ReadStream Stream, capy::MutableBufferSequence MB>
387   capy::io_task<std::size_t> 409   capy::io_task<std::size_t>
388   read(Stream& stream, MB buffers); 410   read(Stream& stream, MB buffers);
389   411  
390   /** Return a source for reading body data. 412   /** Return a source for reading body data.
391   413  
392   The returned source satisfies @ref capy::BufferSource. 414   The returned source satisfies @ref capy::BufferSource.
393   On first pull, headers are automatically parsed if 415   On first pull, headers are automatically parsed if
394   not yet received. 416   not yet received.
395   417  
396   @par Example 418   @par Example
397   @code 419   @code
398   request_parser pr( ctx ); 420   request_parser pr( ctx );
399   pr.start(); 421   pr.start();
400   auto body = pr.source_for( socket ); 422   auto body = pr.source_for( socket );
401   423  
402   capy::const_buffer arr[16]; 424   capy::const_buffer arr[16];
403   auto [ec, bufs] = co_await body.pull( arr ); 425   auto [ec, bufs] = co_await body.pull( arr );
404   body.consume( buffer_size( bufs ) ); 426   body.consume( buffer_size( bufs ) );
405   @endcode 427   @endcode
406   428  
407   @param stream The stream to read from. 429   @param stream The stream to read from.
408   430  
409   @return A source satisfying @ref capy::BufferSource. 431   @return A source satisfying @ref capy::BufferSource.
410   432  
411   @see @ref read_header, @ref capy::BufferSource. 433   @see @ref read_header, @ref capy::BufferSource.
412   */ 434   */
413   template<capy::ReadStream Stream> 435   template<capy::ReadStream Stream>
414   source<Stream> 436   source<Stream>
415   source_for(Stream& stream) noexcept; 437   source_for(Stream& stream) noexcept;
416   438  
417   /** Read body from stream and push to a WriteSink. 439   /** Read body from stream and push to a WriteSink.
418   440  
419   Reads body data from the stream and pushes each chunk to 441   Reads body data from the stream and pushes each chunk to
420   the sink. The sink must consume all bytes from each write. 442   the sink. The sink must consume all bytes from each write.
421   443  
422   @param stream The stream to read body data from. 444   @param stream The stream to read body data from.
423   445  
424   @param sink The sink to receive body data. 446   @param sink The sink to receive body data.
425   447  
426   @return An awaitable yielding `(error_code)`. 448   @return An awaitable yielding `(error_code)`.
427   449  
428   @see WriteSink. 450   @see WriteSink.
429   */ 451   */
430   template<capy::WriteSink Sink> 452   template<capy::WriteSink Sink>
431   capy::io_task<> 453   capy::io_task<>
432   read(capy::ReadStream auto& stream, Sink&& sink); 454   read(capy::ReadStream auto& stream, Sink&& sink);
433   455  
434   private: 456   private:
435   friend class request_parser; 457   friend class request_parser;
436   friend class response_parser; 458   friend class response_parser;
437   class impl; 459   class impl;
438   460  
439   BOOST_HTTP_DECL ~parser(); 461   BOOST_HTTP_DECL ~parser();
440   BOOST_HTTP_DECL parser() noexcept; 462   BOOST_HTTP_DECL parser() noexcept;
441   BOOST_HTTP_DECL parser(parser&& other) noexcept; 463   BOOST_HTTP_DECL parser(parser&& other) noexcept;
442   BOOST_HTTP_DECL parser( 464   BOOST_HTTP_DECL parser(
443   std::shared_ptr<parser_config_impl const> cfg, 465   std::shared_ptr<parser_config_impl const> cfg,
444   detail::kind k); 466   detail::kind k);
445   BOOST_HTTP_DECL void assign(parser&& other) noexcept; 467   BOOST_HTTP_DECL void assign(parser&& other) noexcept;
446   468  
447   BOOST_HTTP_DECL 469   BOOST_HTTP_DECL
448   void 470   void
449   start_impl(bool); 471   start_impl(bool);
450   472  
451   static_request const& 473   static_request const&
452   safe_get_request() const; 474   safe_get_request() const;
453   475  
454   static_response const& 476   static_response const&
455   safe_get_response() const; 477   safe_get_response() const;
456   478  
457   impl* impl_; 479   impl* impl_;
458   }; 480   };
459   481  
460   /** A source for reading the message body. 482   /** A source for reading the message body.
461   483  
462   This type satisfies @ref capy::BufferSource. It can be 484   This type satisfies @ref capy::BufferSource. It can be
463   constructed immediately after parser construction; on 485   constructed immediately after parser construction; on
464   first pull, headers are automatically parsed if not 486   first pull, headers are automatically parsed if not
465   yet received. 487   yet received.
466   488  
467   @tparam Stream A type satisfying @ref capy::ReadStream. 489   @tparam Stream A type satisfying @ref capy::ReadStream.
468   490  
469   @see @ref parser::source_for. 491   @see @ref parser::source_for.
470   */ 492   */
471   template<capy::ReadStream Stream> 493   template<capy::ReadStream Stream>
472   class parser::source 494   class parser::source
473   { 495   {
474   Stream* stream_; 496   Stream* stream_;
475   parser* pr_; 497   parser* pr_;
476   498  
477   public: 499   public:
478   /// Default constructor. 500   /// Default constructor.
479   source() noexcept 501   source() noexcept
480   : stream_(nullptr) 502   : stream_(nullptr)
481   , pr_(nullptr) 503   , pr_(nullptr)
482   { 504   {
483   } 505   }
484   506  
485   /// Construct a source for reading body data. 507   /// Construct a source for reading body data.
HITCBC 486   355 source(Stream& stream, parser& pr) noexcept 508   355 source(Stream& stream, parser& pr) noexcept
HITCBC 487   355 : stream_(&stream) 509   355 : stream_(&stream)
HITCBC 488   355 , pr_(&pr) 510   355 , pr_(&pr)
489   { 511   {
HITCBC 490   355 } 512   355 }
491   513  
492   /** Pull buffer data from the body. 514   /** Pull buffer data from the body.
493   515  
494   On first invocation, reads headers if not yet parsed. 516   On first invocation, reads headers if not yet parsed.
495   Returns buffer descriptors pointing to internal parser 517   Returns buffer descriptors pointing to internal parser
496   memory. When the body is complete, returns an empty span. 518   memory. When the body is complete, returns an empty span.
497   519  
498   @param dest Span of const_buffer to fill. 520   @param dest Span of const_buffer to fill.
499   521  
500   @return An awaitable yielding `(error_code,std::span<const_buffer>)`. 522   @return An awaitable yielding `(error_code,std::span<const_buffer>)`.
501   */ 523   */
502   capy::io_task<std::span<capy::const_buffer>> 524   capy::io_task<std::span<capy::const_buffer>>
503   pull(std::span<capy::const_buffer> dest); 525   pull(std::span<capy::const_buffer> dest);
504   526  
505   /** Consume bytes from pulled body data. 527   /** Consume bytes from pulled body data.
506   528  
507   Advances the read position by the specified number of 529   Advances the read position by the specified number of
508   bytes. The next pull returns data starting after the 530   bytes. The next pull returns data starting after the
509   consumed bytes. 531   consumed bytes.
510   532  
511   @param n The number of bytes to consume. 533   @param n The number of bytes to consume.
512   */ 534   */
513   void 535   void
514   consume(std::size_t n) noexcept; 536   consume(std::size_t n) noexcept;
515   }; 537   };
516   538  
517   template<capy::ReadStream Stream> 539   template<capy::ReadStream Stream>
518   capy::io_task<> 540   capy::io_task<>
HITCBC 519   749 parser:: 541   749 parser::
520   read_header(Stream& stream) 542   read_header(Stream& stream)
521   { 543   {
522   system::error_code ec; 544   system::error_code ec;
523   for(;;) 545   for(;;)
524   { 546   {
525   parse(ec); 547   parse(ec);
526   548  
527   if(got_header()) 549   if(got_header())
528   co_return {}; 550   co_return {};
529   551  
530   if(ec != condition::need_more_input) 552   if(ec != condition::need_more_input)
531   co_return {std::error_code(ec)}; 553   co_return {std::error_code(ec)};
532   554  
533   auto mbs = prepare(); 555   auto mbs = prepare();
534   556  
535   auto [read_ec, n] = co_await stream.read_some(mbs); 557   auto [read_ec, n] = co_await stream.read_some(mbs);
536   if(read_ec == capy::cond::eof) 558   if(read_ec == capy::cond::eof)
537   commit_eof(); 559   commit_eof();
538   else if(!read_ec) 560   else if(!read_ec)
539   commit(n); 561   commit(n);
540   else 562   else
541   co_return {read_ec}; 563   co_return {read_ec};
542   } 564   }
HITCBC 543   1498 } 565   1498 }
544   566  
545   template<capy::ReadStream Stream> 567   template<capy::ReadStream Stream>
546   capy::io_task<> 568   capy::io_task<>
HITCBC 547   274 parser:: 569   274 parser::
548   read(Stream& stream) 570   read(Stream& stream)
549   { 571   {
550   system::error_code ec; 572   system::error_code ec;
551   for(;;) 573   for(;;)
552   { 574   {
553   parse(ec); 575   parse(ec);
554   576  
555   if(is_complete()) 577   if(is_complete())
556   co_return {}; 578   co_return {};
557   579  
558   if(ec && ec != condition::need_more_input) 580   if(ec && ec != condition::need_more_input)
559   co_return {std::error_code(ec)}; 581   co_return {std::error_code(ec)};
560   582  
561   if(ec == condition::need_more_input) 583   if(ec == condition::need_more_input)
562   { 584   {
563   auto mbs = prepare(); 585   auto mbs = prepare();
564   586  
565   auto [read_ec, n] = co_await stream.read_some(mbs); 587   auto [read_ec, n] = co_await stream.read_some(mbs);
566   if(read_ec == capy::cond::eof) 588   if(read_ec == capy::cond::eof)
567   commit_eof(); 589   commit_eof();
568   else if(!read_ec) 590   else if(!read_ec)
569   commit(n); 591   commit(n);
570   else 592   else
571   co_return {read_ec}; 593   co_return {read_ec};
572   } 594   }
573   } 595   }
HITCBC 574   548 } 596   548 }
575   597  
576   template<capy::ReadStream Stream, capy::MutableBufferSequence MB> 598   template<capy::ReadStream Stream, capy::MutableBufferSequence MB>
577   capy::io_task<std::size_t> 599   capy::io_task<std::size_t>
578   parser:: 600   parser::
579   read(Stream& stream, MB buffers) 601   read(Stream& stream, MB buffers)
580   { 602   {
581   if(capy::buffer_empty(buffers)) 603   if(capy::buffer_empty(buffers))
582   co_return {{}, 0}; 604   co_return {{}, 0};
583   605  
584   std::size_t total = 0; 606   std::size_t total = 0;
585   auto dest = capy::buffer_slice(buffers); 607   auto dest = capy::buffer_slice(buffers);
586   608  
587   for(;;) 609   for(;;)
588   { 610   {
589   system::error_code ec; 611   system::error_code ec;
590   parse(ec); 612   parse(ec);
591   613  
592   if(got_header()) 614   if(got_header())
593   { 615   {
594   auto body_data = pull_body(); 616   auto body_data = pull_body();
595   if(capy::buffer_size(body_data) > 0) 617   if(capy::buffer_size(body_data) > 0)
596   { 618   {
597   std::size_t copied = capy::buffer_copy(dest.data(), body_data); 619   std::size_t copied = capy::buffer_copy(dest.data(), body_data);
598   consume_body(copied); 620   consume_body(copied);
599   total += copied; 621   total += copied;
600   dest.remove_prefix(copied); 622   dest.remove_prefix(copied);
601   623  
602   if(capy::buffer_empty(dest.data())) 624   if(capy::buffer_empty(dest.data()))
603   co_return {{}, total}; 625   co_return {{}, total};
604   } 626   }
605   627  
606   if(is_complete()) 628   if(is_complete())
607   co_return {capy::error::eof, total}; 629   co_return {capy::error::eof, total};
608   } 630   }
609   631  
610   if(ec == condition::need_more_input) 632   if(ec == condition::need_more_input)
611   { 633   {
612   auto mbs = prepare(); 634   auto mbs = prepare();
613   auto [read_ec, n] = co_await stream.read_some(mbs); 635   auto [read_ec, n] = co_await stream.read_some(mbs);
614   636  
615   if(read_ec == capy::cond::eof) 637   if(read_ec == capy::cond::eof)
616   commit_eof(); 638   commit_eof();
617   else if(!read_ec) 639   else if(!read_ec)
618   commit(n); 640   commit(n);
619   else 641   else
620   co_return {read_ec, total}; 642   co_return {read_ec, total};
621   643  
622   continue; 644   continue;
623   } 645   }
624   646  
625   if(ec) 647   if(ec)
626   co_return {ec, total}; 648   co_return {ec, total};
627   } 649   }
628   } 650   }
629   651  
630   template<capy::ReadStream Stream> 652   template<capy::ReadStream Stream>
631   parser::source<Stream> 653   parser::source<Stream>
HITCBC 632   355 parser:: 654   355 parser::
633   source_for(Stream& stream) noexcept 655   source_for(Stream& stream) noexcept
634   { 656   {
HITCBC 635   355 return source<Stream>(stream, *this); 657   355 return source<Stream>(stream, *this);
636   } 658   }
637   659  
638   template<capy::ReadStream Stream> 660   template<capy::ReadStream Stream>
639   capy::io_task<std::span<capy::const_buffer>> 661   capy::io_task<std::span<capy::const_buffer>>
HITCBC 640   922 parser::source<Stream>:: 662   922 parser::source<Stream>::
641   pull(std::span<capy::const_buffer> dest) 663   pull(std::span<capy::const_buffer> dest)
642   { 664   {
643   // Read headers if not yet parsed 665   // Read headers if not yet parsed
644   if(!pr_->got_header()) 666   if(!pr_->got_header())
645   { 667   {
646   auto [ec] = co_await pr_->read_header(*stream_); 668   auto [ec] = co_await pr_->read_header(*stream_);
647   if(ec) 669   if(ec)
648   co_return {ec, {}}; 670   co_return {ec, {}};
649   } 671   }
650   672  
651   for(;;) 673   for(;;)
652   { 674   {
653   system::error_code ec; 675   system::error_code ec;
654   pr_->parse(ec); 676   pr_->parse(ec);
655   677  
656   auto body_data = pr_->pull_body(); 678   auto body_data = pr_->pull_body();
657   if(capy::buffer_size(body_data) > 0) 679   if(capy::buffer_size(body_data) > 0)
658   { 680   {
659   std::size_t count = (std::min)(body_data.size(), dest.size()); 681   std::size_t count = (std::min)(body_data.size(), dest.size());
660   for(std::size_t i = 0; i < count; ++i) 682   for(std::size_t i = 0; i < count; ++i)
661   dest[i] = body_data[i]; 683   dest[i] = body_data[i];
662   co_return {{}, dest.first(count)}; 684   co_return {{}, dest.first(count)};
663   } 685   }
664   686  
665   if(pr_->is_complete()) 687   if(pr_->is_complete())
666   co_return {capy::error::eof, {}}; 688   co_return {capy::error::eof, {}};
667   689  
668   if(ec == condition::need_more_input) 690   if(ec == condition::need_more_input)
669   { 691   {
670   auto mbs = pr_->prepare(); 692   auto mbs = pr_->prepare();
671   auto [read_ec, n] = co_await stream_->read_some(mbs); 693   auto [read_ec, n] = co_await stream_->read_some(mbs);
672   694  
673   if(read_ec == capy::cond::eof) 695   if(read_ec == capy::cond::eof)
674   pr_->commit_eof(); 696   pr_->commit_eof();
675   else if(!read_ec) 697   else if(!read_ec)
676   pr_->commit(n); 698   pr_->commit(n);
677   else 699   else
678   co_return {read_ec, {}}; 700   co_return {read_ec, {}};
679   701  
680   continue; 702   continue;
681   } 703   }
682   704  
683   if(ec) 705   if(ec)
684   co_return {ec, {}}; 706   co_return {ec, {}};
685   } 707   }
HITCBC 686   1844 } 708   1844 }
687   709  
688   template<capy::ReadStream Stream> 710   template<capy::ReadStream Stream>
689   void 711   void
HITCBC 690   568 parser::source<Stream>:: 712   568 parser::source<Stream>::
691   consume(std::size_t n) noexcept 713   consume(std::size_t n) noexcept
692   { 714   {
HITCBC 693   568 pr_->consume_body(n); 715   568 pr_->consume_body(n);
HITCBC 694   568 } 716   568 }
695   717  
696   template<capy::WriteSink Sink> 718   template<capy::WriteSink Sink>
697   capy::io_task<> 719   capy::io_task<>
HITCBC 698   138 parser:: 720   138 parser::
699   read(capy::ReadStream auto& stream, Sink&& sink) 721   read(capy::ReadStream auto& stream, Sink&& sink)
700   { 722   {
701   for(;;) 723   for(;;)
702   { 724   {
703   system::error_code ec; 725   system::error_code ec;
704   parse(ec); 726   parse(ec);
705   727  
706   if(got_header()) 728   if(got_header())
707   { 729   {
708   auto body_data = pull_body(); 730   auto body_data = pull_body();
709   if(capy::buffer_size(body_data) > 0) 731   if(capy::buffer_size(body_data) > 0)
710   { 732   {
711   auto [write_ec, n] = co_await sink.write(body_data); 733   auto [write_ec, n] = co_await sink.write(body_data);
712   if(write_ec) 734   if(write_ec)
713   co_return {write_ec}; 735   co_return {write_ec};
714   consume_body(n); 736   consume_body(n);
715   } 737   }
716   738  
717   if(is_complete()) 739   if(is_complete())
718   { 740   {
719   auto [eof_ec] = co_await sink.write_eof(); 741   auto [eof_ec] = co_await sink.write_eof();
720   co_return {eof_ec}; 742   co_return {eof_ec};
721   } 743   }
722   } 744   }
723   745  
724   if(ec == condition::need_more_input) 746   if(ec == condition::need_more_input)
725   { 747   {
726   auto mbs = prepare(); 748   auto mbs = prepare();
727   auto [read_ec, n] = co_await stream.read_some(mbs); 749   auto [read_ec, n] = co_await stream.read_some(mbs);
728   750  
729   if(read_ec == capy::cond::eof) 751   if(read_ec == capy::cond::eof)
730   commit_eof(); 752   commit_eof();
731   else if(!read_ec) 753   else if(!read_ec)
732   commit(n); 754   commit(n);
733   else 755   else
734   co_return {read_ec}; 756   co_return {read_ec};
735   757  
736   continue; 758   continue;
737   } 759   }
738   760  
739   if(ec) 761   if(ec)
740   co_return {std::error_code(ec)}; 762   co_return {std::error_code(ec)};
741   } 763   }
HITCBC 742   276 } 764   276 }
743   765  
744   } // http 766   } // http
745   } // boost 767   } // boost
746   768  
747   #endif 769   #endif