RESTinio
Loading...
Searching...
No Matches
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 *
9get_http_parser_ctx( llhttp_t * parser )
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
19inline int
20restinio_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
42inline int
43restinio_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
71inline 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
101inline void
102append_last_field_accessor( http_header_fields_t & fields, string_view_t value )
103{
104 fields.append_last_field( value );
105}
106
107inline int
108restinio_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
136inline 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
144inline int
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
177inline int
178restinio_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
202inline int
203restinio_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
233inline int
234restinio_chunk_complete_cb( llhttp_t * /*parser*/ )
235{
236 // There is nothing to do.
237 return 0;
238}
239
240template< typename Http_Methods >
241int
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/// @{
267inline int
268restinio_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
303inline 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
322inline int
323restinio_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
347inline int
349{
350 // There is nothing to do.
351 return 0;
352}
353/// @}
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_header_field_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)
int restinio_message_complete_cb(llhttp_t *parser)
int restinio_header_value_cb(llhttp_t *parser, const char *at, size_t length)
int restinio_chunk_extension_name_complete_cb(llhttp_t *parser)
int restinio_chunk_extension_name_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_chunk_complete_cb(llhttp_t *)
void append_last_field_accessor(http_header_fields_t &fields, string_view_t value)
int restinio_chunk_header_cb(llhttp_t *parser)
int restinio_header_value_complete_cb(llhttp_t *parser)
int restinio_chunk_extension_value_complete_cb(llhttp_t *)
int restinio_headers_complete_cb(llhttp_t *parser)
int restinio_header_field_cb(llhttp_t *parser, const char *at, size_t length)