RESTinio
parser_callbacks.ipp
Go to the documentation of this file.
1 /*
2  restinio
3 */
4 
5 /**
6  * @brief A helper function to get the pointer to a context object.
7  */
8 [[nodiscard]] inline restinio::impl::http_parser_ctx_t *
10 {
11  return reinterpret_cast< restinio::impl::http_parser_ctx_t * >(
12  parser->data );
13 }
14 
15 /*!
16  Callbacks used with http parser.
17 */
18 
19 inline int
20 restinio_url_cb( llhttp_t * parser, const char * at, size_t length )
21 {
22  try
23  {
24  auto * ctx = get_http_parser_ctx( parser );
25 
26  ctx->m_header.append_request_target( at, length );
27 
28  if( ctx->m_header.request_target().length() >
29  ctx->m_limits.max_url_size() )
30  {
31  return -1;
32  }
33  }
34  catch( const std::exception & )
35  {
36  return -1;
37  }
38 
39  return 0;
40 }
41 
42 inline int
43 restinio_header_field_cb( llhttp_t * parser, const char *at, size_t length )
44 {
45  try
46  {
47  auto * ctx = get_http_parser_ctx( parser );
48 
49  // Maybe there are too many fields?
50  if( ctx->m_total_field_count == ctx->m_limits.max_field_count() )
51  {
52  return -1;
53  }
54 
55  if( ctx->m_current_field_name.size() + length >
56  ctx->m_limits.max_field_name_size() )
57  {
58  return -1;
59  }
60 
61  ctx->m_current_field_name.append( at, length );
62  }
63  catch( const std::exception & )
64  {
65  return -1;
66  }
67 
68  return 0;
69 }
70 
71 inline int
73 {
74  try
75  {
76  auto * ctx = get_http_parser_ctx( parser );
77 
78  auto & fields = ctx->m_leading_headers_completed
79  ? ctx->m_chunked_info_block.m_trailing_fields
80  : ctx->m_header;
81 
82  // Note: moving `ctx->m_current_field_name`
83  // also cleans the placeholder, so it
84  // becomes ready to accumulating next field.
85  fields.add_field(
86  std::move( ctx->m_current_field_name ),
87  std::string{} );
88 
89  // At this point the number of parsed fields can be incremented.
90  ctx->m_total_field_count += 1u;
91  }
92  catch( const std::exception & )
93  {
94  return -1;
95  }
96 
97  return 0;
98 }
99 
100 
101 inline void
102 append_last_field_accessor( http_header_fields_t & fields, string_view_t value )
103 {
104  fields.append_last_field( value );
105 }
106 
107 inline int
108 restinio_header_value_cb( llhttp_t * parser, const char *at, size_t length )
109 {
110  try
111  {
112  auto * ctx = get_http_parser_ctx( parser );
113 
114  http_header_fields_t & fields = ctx->m_leading_headers_completed
115  ? ctx->m_chunked_info_block.m_trailing_fields
116  : ctx->m_header;
117 
118  if( ctx->m_last_value_total_size + length >=
119  ctx->m_limits.max_field_value_size() )
120  {
121  return -1;
122  }
123 
124  ctx->m_last_value_total_size += length;
125 
126  append_last_field_accessor( fields, std::string{ at, length } );
127  }
128  catch( const std::exception & )
129  {
130  return -1;
131  }
132 
133  return 0;
134 }
135 
136 inline int
138 {
139  // Reset value size counter for the next time.
140  get_http_parser_ctx( parser )->m_last_value_total_size = 0;
141  return 0;
142 }
143 
144 inline int
145 restinio_headers_complete_cb( llhttp_t * parser )
146 {
147  auto * ctx = get_http_parser_ctx( parser );
148  // Next time header_name/header_value callback should store
149  // values of trailing fields.
150  ctx->m_leading_headers_completed = true;
151 
152  if( ULLONG_MAX != parser->content_length &&
153  0 < parser->content_length )
154  {
155  // Maximum body size can be checked right now.
156  if( parser->content_length > ctx->m_limits.max_body_size() )
157  {
158  return -1;
159  }
160 
161  try
162  {
163  ctx->m_body.reserve(
164  ::restinio::utils::impl::uint64_to_size_t(
165  parser->content_length) );
166  }
167  catch( const std::exception & )
168  {
169  return -1;
170  }
171  }
172 
173  return 0;
174 }
175 
176 
177 inline int
178 restinio_body_cb( llhttp_t * parser, const char *at, size_t length )
179 {
180  try
181  {
182  auto * ctx = get_http_parser_ctx( parser );
183 
184  // The total size of the body should be checked.
185  const auto total_length = static_cast<std::uint64_t>(
186  ctx->m_body.size() ) + length;
187  if( total_length > ctx->m_limits.max_body_size() )
188  {
189  return -1;
190  }
191 
192  ctx->m_body.append( at, length );
193  }
194  catch( const std::exception & )
195  {
196  return -1;
197  }
198 
199  return 0;
200 }
201 
202 inline int
203 restinio_chunk_header_cb( llhttp_t * parser )
204 {
205  try
206  {
207  // In on_chunk_header callback parser->content_length contains
208  // the size of the next chunk.
209  // If that size is 0 then it is the last chunk and it should be
210  // ignored.
211  if( 0u != parser->content_length )
212  {
213  auto * ctx = get_http_parser_ctx( parser );
214 
215  // Store an info about the new chunk.
216  // If there will be an error at the next stage of parsing
217  // the incoming request the whole request's data will be dropped.
218  // So there is no need to care about that new item in m_chunks.
219  ctx->m_chunked_info_block.m_chunks.emplace_back(
220  ctx->m_body.size(),
221  ::restinio::utils::impl::uint64_to_size_t(parser->content_length),
222  std::move( ctx->m_chunk_ext_params ) );
223  }
224  }
225  catch( const std::exception & )
226  {
227  return -1;
228  }
229 
230  return 0;
231 }
232 
233 inline int
235 {
236  // There is nothing to do.
237  return 0;
238 }
239 
240 template< typename Http_Methods >
241 int
242 restinio_message_complete_cb( llhttp_t * parser )
243 {
244  auto * ctx = get_http_parser_ctx( parser );
245 
246  ctx->m_message_complete = true;
247  ctx->m_header.method( Http_Methods::from_nodejs( parser->method ) );
248 
249  if( 0 == llhttp_get_upgrade( parser ) )
250  {
251  ctx->m_header.should_keep_alive( 0 != llhttp_should_keep_alive( parser ) );
252  }
253  else
254  {
255  ctx->m_header.connection( http_connection_header_t::upgrade );
256  }
257 
258  return HPE_PAUSED;
259 }
260 
261 /*!
262  * @name Chunked encoding callbacks.
263  *
264  * @since v.0.7.0
265  */
266 /// @{
267 inline int
268 restinio_chunk_extension_name_cb( llhttp_t * parser, const char * at, size_t length )
269 {
270  try
271  {
272  auto * ctx = get_http_parser_ctx( parser );
273 
274  if( !ctx->m_chunk_ext_params )
275  {
276  ctx->m_chunk_ext_params = std::make_unique<chunk_ext_params_t>();
277  }
278 
279  auto * ext_params = ctx->m_chunk_ext_params.get();
280 
281  // Maybe there are too many fields?
282  if( ext_params->size() == ctx->m_limits.max_field_count() )
283  {
284  return -1;
285  }
286 
287  if( ctx->m_current_field_name.size() + length >
288  ctx->m_limits.max_field_name_size() )
289  {
290  return -1;
291  }
292 
293  ctx->m_current_field_name.append( at, length );
294  }
295  catch( const std::exception & )
296  {
297  return -1;
298  }
299 
300  return 0;
301 }
302 
303 inline int
305 {
306  try
307  {
308  auto * ctx = get_http_parser_ctx( parser );
309  auto * ext_params = ctx->m_chunk_ext_params.get();
310 
311  ext_params->emplace_back(
312  chunk_ext_param_t{ std::move( ctx->m_current_field_name ), {} } );
313  }
314  catch( const std::exception & )
315  {
316  return -1;
317  }
318 
319  return 0;
320 }
321 
322 inline int
323 restinio_chunk_extension_value_cb( llhttp_t * parser, const char * at, size_t length )
324 {
325  try
326  {
327  auto * ctx = get_http_parser_ctx( parser );
328  auto & value_receiver_str =
329  ctx->m_chunk_ext_params->back().m_value;
330 
331  if( value_receiver_str.size() + length >
332  ctx->m_limits.max_field_value_size() )
333  {
334  return -1;
335  }
336 
337  value_receiver_str.append( at, length );
338  }
339  catch( const std::exception & )
340  {
341  return -1;
342  }
343 
344  return 0;
345 }
346 
347 inline int
349 {
350  // There is nothing to do.
351  return 0;
352 }
353 /// @}
int restinio_chunk_extension_name_cb(llhttp_t *parser, const char *at, size_t length)
void append_last_field_accessor(http_header_fields_t &fields, string_view_t value)
int restinio_message_complete_cb(llhttp_t *parser)
int restinio_chunk_extension_name_complete_cb(llhttp_t *parser)
int restinio_header_field_complete_cb(llhttp_t *parser)
int restinio_chunk_extension_value_complete_cb(llhttp_t *)
int restinio_chunk_header_cb(llhttp_t *parser)
int restinio_header_field_cb(llhttp_t *parser, const char *at, size_t length)
int restinio_chunk_extension_value_cb(llhttp_t *parser, const char *at, size_t length)
int restinio_header_value_cb(llhttp_t *parser, const char *at, size_t length)
restinio::impl::http_parser_ctx_t * get_http_parser_ctx(llhttp_t *parser)
A helper function to get the pointer to a context object.
int restinio_chunk_complete_cb(llhttp_t *)
int restinio_headers_complete_cb(llhttp_t *parser)
int restinio_header_value_complete_cb(llhttp_t *parser)
int restinio_body_cb(llhttp_t *parser, const char *at, size_t length)
int restinio_url_cb(llhttp_t *parser, const char *at, size_t length)