/src/mhd2/src/mhd2/h2/h2_req_fields.c
Line | Count | Source |
1 | | /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ |
2 | | /* |
3 | | This file is part of GNU libmicrohttpd. |
4 | | Copyright (C) 2025 Evgeny Grin (Karlson2k) |
5 | | |
6 | | GNU libmicrohttpd is free software; you can redistribute it and/or |
7 | | modify it under the terms of the GNU Lesser General Public |
8 | | License as published by the Free Software Foundation; either |
9 | | version 2.1 of the License, or (at your option) any later version. |
10 | | |
11 | | GNU libmicrohttpd is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | Lesser General Public License for more details. |
15 | | |
16 | | Alternatively, you can redistribute GNU libmicrohttpd and/or |
17 | | modify it under the terms of the GNU General Public License as |
18 | | published by the Free Software Foundation; either version 2 of |
19 | | the License, or (at your option) any later version, together |
20 | | with the eCos exception, as follows: |
21 | | |
22 | | As a special exception, if other files instantiate templates or |
23 | | use macros or inline functions from this file, or you compile this |
24 | | file and link it with other works to produce a work based on this |
25 | | file, this file does not by itself cause the resulting work to be |
26 | | covered by the GNU General Public License. However the source code |
27 | | for this file must still be made available in accordance with |
28 | | section (3) of the GNU General Public License v2. |
29 | | |
30 | | This exception does not invalidate any other reasons why a work |
31 | | based on this file might be covered by the GNU General Public |
32 | | License. |
33 | | |
34 | | You should have received copies of the GNU Lesser General Public |
35 | | License and the GNU General Public License along with this library; |
36 | | if not, see <https://www.gnu.org/licenses/>. |
37 | | */ |
38 | | |
39 | | /** |
40 | | * @file src/mhd2/h2/h2_dec_fields.c |
41 | | * @brief Implementation of HTTP/2 request fields functions |
42 | | * @author Karlson2k (Evgeny Grin) |
43 | | */ |
44 | | |
45 | | #include "mhd_sys_options.h" |
46 | | |
47 | | #include "sys_bool_type.h" |
48 | | #include "sys_base_types.h" |
49 | | |
50 | | #include <string.h> |
51 | | |
52 | | #include "mhd_assert.h" |
53 | | #include "mhd_unreachable.h" |
54 | | #include "mhd_assume.h" |
55 | | |
56 | | #include "mhd_constexpr.h" |
57 | | #include "mhd_buffer.h" |
58 | | #include "mhd_str_types.h" |
59 | | #include "mhd_str_macros.h" |
60 | | |
61 | | #include "mhd_connection.h" |
62 | | #include "h2_conn_data.h" |
63 | | |
64 | | #include "h2_stream_data.h" |
65 | | |
66 | | #include "mhd_str.h" |
67 | | |
68 | | #include "stream_process_request.h" |
69 | | |
70 | | #include "h2_req_item_kinds.h" |
71 | | #include "h2_req_item_struct.h" |
72 | | #include "h2_req_items_funcs.h" |
73 | | |
74 | | #include "hpack/mhd_hpack_codec.h" |
75 | | |
76 | | #include "h2_proc_conn.h" |
77 | | #include "h2_conn_streams.h" |
78 | | |
79 | | #include "h2_req_fields.h" |
80 | | |
81 | | MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ |
82 | | MHD_FN_PAR_INOUT_ (1) MHD_FN_PAR_IN_ (2) |
83 | | MHD_FN_PAR_INOUT_ (4) MHD_FN_PAR_OUT_ (5) enum mhd_H2DecFieldsResult |
84 | | mhd_h2_req_fields_decode (struct mhd_HpackDecContext *restrict hk_dec, |
85 | | const struct mhd_Buffer *restrict enc_data, |
86 | | bool are_trailers, |
87 | | struct mhd_H2ReqItemsBlock *restrict ib, |
88 | | size_t *restrict left_unprocessed) |
89 | 0 | { |
90 | 0 | mhd_constexpr unsigned int max_no_fields = 8u; |
91 | 0 | const enum mhd_H2RequestItemKind field_kind = |
92 | 0 | are_trailers ? mhd_H2_RIK_TRAILER : mhd_H2_RIK_HEADER; |
93 | 0 | size_t pos; |
94 | 0 | unsigned int no_fields; |
95 | 0 | enum mhd_H2DecFieldsResult ret; |
96 | |
|
97 | 0 | pos = 0u; |
98 | 0 | no_fields = 0u; |
99 | 0 | ret = mhd_H2_DEC_FIELDS_OK; |
100 | |
|
101 | 0 | while (pos < enc_data->size) |
102 | 0 | { |
103 | 0 | size_t name_len; |
104 | 0 | size_t val_len; |
105 | 0 | size_t pos_incr; |
106 | 0 | enum mhd_HpackDecResult res; |
107 | 0 | struct mhd_Buffer buff; |
108 | |
|
109 | 0 | mhd_assert (mhd_H2_DEC_FIELDS_OK == ret); |
110 | |
|
111 | 0 | if (! mhd_h2_items_get_buff_new_item (ib, |
112 | 0 | &buff)) |
113 | 0 | return mhd_H2_DEC_FIELDS_NO_SPACE; |
114 | | |
115 | 0 | res = mhd_hpack_dec_data (hk_dec, |
116 | 0 | enc_data->size - pos, |
117 | 0 | (const uint8_t *) enc_data->data + pos, |
118 | 0 | buff.size, |
119 | 0 | buff.data, |
120 | 0 | &name_len, |
121 | 0 | &val_len, |
122 | 0 | &pos_incr); |
123 | |
|
124 | 0 | if (! mhd_HPACK_DEC_RES_IS_ERR (res)) |
125 | 0 | { |
126 | 0 | mhd_assert (0u != pos_incr); |
127 | 0 | pos += pos_incr; |
128 | 0 | } |
129 | |
|
130 | 0 | switch (res) |
131 | 0 | { |
132 | 0 | case mhd_HPACK_DEC_RES_NO_NEW_FIELD: |
133 | 0 | if (max_no_fields < ++no_fields) |
134 | 0 | { |
135 | 0 | ret = mhd_H2_DEC_FIELDS_PROT_ABUSE; |
136 | 0 | break; |
137 | 0 | } |
138 | 0 | mhd_h2_items_cancel_new_item_buff (ib); |
139 | 0 | continue; |
140 | 0 | case mhd_HPACK_DEC_RES_NEW_FIELD: |
141 | 0 | mhd_assert (buff.size >= (name_len + val_len + 2u)); |
142 | 0 | mhd_assert (0 == buff.data[name_len]); |
143 | 0 | mhd_assert (0 == buff.data[name_len + 1 + val_len]); |
144 | 0 | mhd_h2_items_add_new_item_buff (ib, |
145 | 0 | name_len, |
146 | 0 | val_len, |
147 | 0 | (':' == buff.data[0]) ? |
148 | 0 | mhd_H2_RIK_PSEUDOHEADER : field_kind); |
149 | 0 | continue; |
150 | 0 | case mhd_HPACK_DEC_RES_INCOMPLETE: |
151 | 0 | mhd_assert (mhd_H2_DEC_FIELDS_OK == ret); |
152 | 0 | break; |
153 | 0 | case mhd_HPACK_DEC_RES_ALLOC_ERR: |
154 | 0 | ret = mhd_H2_DEC_FIELDS_INT_ERR; |
155 | 0 | break; |
156 | 0 | case mhd_HPACK_DEC_RES_BUFFER_TOO_SMALL: // TODO: support "minimal" decoding for decoder state updates |
157 | 0 | case mhd_HPACK_DEC_RES_STRING_TOO_LONG: |
158 | 0 | ret = mhd_H2_DEC_FIELDS_NO_SPACE; |
159 | 0 | break; |
160 | 0 | case mhd_HPACK_DEC_RES_NUMBER_TOO_LONG: |
161 | 0 | case mhd_HPACK_DEC_RES_DYN_SIZE_UPD_TOO_LARGE: |
162 | 0 | ret = mhd_H2_DEC_FIELDS_PROT_ABUSE; |
163 | 0 | break; |
164 | 0 | case mhd_HPACK_DEC_RES_DYN_SIZE_UPD_MISSING: |
165 | 0 | case mhd_HPACK_DEC_RES_HUFFMAN_ERR: |
166 | 0 | case mhd_HPACK_DEC_RES_HPACK_BAD_IDX: |
167 | 0 | case mhd_HPACK_DEC_RES_HPACK_ERR: |
168 | 0 | ret = mhd_H2_DEC_FIELDS_BROKEN_DATA; |
169 | 0 | break; |
170 | 0 | case mhd_HPACK_DEC_RES_INTERNAL_ERR: |
171 | 0 | default: |
172 | 0 | mhd_UNREACHABLE_D ("Impossible value"); |
173 | 0 | ret = mhd_H2_DEC_FIELDS_INT_ERR; |
174 | 0 | break; |
175 | 0 | } |
176 | 0 | mhd_h2_items_cancel_new_item_buff (ib); |
177 | 0 | break; /* Break the loop */ |
178 | 0 | } |
179 | | |
180 | 0 | mhd_assert (pos <= enc_data->size); |
181 | 0 | *left_unprocessed = enc_data->size - pos; |
182 | |
|
183 | 0 | return ret; |
184 | 0 | } |
185 | | |
186 | | |
187 | | static inline MHD_FN_PAR_NONNULL_ALL_ |
188 | | MHD_FN_PAR_INOUT_ (1) bool |
189 | | req_validate_fields_chars (struct mhd_H2Stream *restrict s) |
190 | 0 | { |
191 | | // TODO: implement checking all field chars for validity, RFC 9113 Section 8.2.1 |
192 | 0 | (void) s; |
193 | 0 | return true; |
194 | 0 | } |
195 | | |
196 | | |
197 | | static inline MHD_FN_PAR_NONNULL_ALL_ |
198 | | MHD_FN_PAR_INOUT_ (1) |
199 | | MHD_FN_PAR_OUT_ (2) bool |
200 | | req_pseudoheaders_preprocess (struct mhd_H2Stream *restrict s, |
201 | | size_t *pos) |
202 | 0 | { |
203 | 0 | static const struct MHD_String ph_method = mhd_MSTR_INIT (":method"); |
204 | 0 | static const struct MHD_String ph_scheme = mhd_MSTR_INIT (":scheme"); |
205 | 0 | static const struct MHD_String ph_authority = mhd_MSTR_INIT (":authority"); |
206 | 0 | static const struct MHD_String ph_path = mhd_MSTR_INIT (":path"); |
207 | 0 | struct mhd_H2ReqItemsBlock *const ib = s->c->h2.mem.req_ib; |
208 | 0 | const char *buff = mhd_h2_items_get_strings_buffc (ib); |
209 | 0 | bool have_method = false; |
210 | 0 | bool have_scheme = false; |
211 | 0 | bool have_authority = false; |
212 | 0 | bool have_path = false; |
213 | |
|
214 | 0 | *pos = 0u; |
215 | 0 | while (1) |
216 | 0 | { |
217 | 0 | const struct mhd_H2ReqItem *item; |
218 | 0 | item = mhd_h2_items_get_item_nc (ib, |
219 | 0 | *pos); |
220 | |
|
221 | 0 | if (NULL == item) |
222 | 0 | break; |
223 | 0 | else if (mhd_H2_RIK_PSEUDOHEADER != item->kind) |
224 | 0 | break; |
225 | 0 | else if ((item->name_len == ph_method.len) && |
226 | 0 | (0 == memcmp (buff + item->offset, |
227 | 0 | ph_method.cstr, |
228 | 0 | ph_method.len))) |
229 | 0 | { |
230 | 0 | if (have_method) |
231 | 0 | return mhd_h2_stream_req_problem (s, |
232 | 0 | mhd_H2_REQ_PRBLM_PSEUDOHDR_DUP); |
233 | 0 | have_method = true; |
234 | 0 | s->req.pos_method = *pos; |
235 | 0 | s->req.method = |
236 | 0 | mhd_parse_http_method (item->val_len, |
237 | 0 | buff + item->offset + ph_method.len + 1u); |
238 | 0 | } |
239 | 0 | else if ((item->name_len == ph_path.len) && |
240 | 0 | (0 == memcmp (buff + item->offset, |
241 | 0 | ph_path.cstr, |
242 | 0 | ph_path.len))) |
243 | 0 | { |
244 | 0 | if (have_path) |
245 | 0 | return mhd_h2_stream_req_problem (s, |
246 | 0 | mhd_H2_REQ_PRBLM_PSEUDOHDR_DUP); |
247 | 0 | have_path = true; |
248 | 0 | s->req.pos_path = *pos; |
249 | 0 | } |
250 | 0 | else if ((item->name_len == ph_authority.len) && |
251 | 0 | (0 == memcmp (buff + item->offset, |
252 | 0 | ph_authority.cstr, |
253 | 0 | ph_authority.len))) |
254 | 0 | { |
255 | 0 | if (have_authority) |
256 | 0 | return mhd_h2_stream_req_problem (s, |
257 | 0 | mhd_H2_REQ_PRBLM_PSEUDOHDR_DUP); |
258 | 0 | have_authority = true; |
259 | 0 | s->req.pos_authority = *pos; |
260 | 0 | } |
261 | 0 | else if ((item->name_len == ph_scheme.len) && |
262 | 0 | (0 == memcmp (buff + item->offset, |
263 | 0 | ph_scheme.cstr, |
264 | 0 | ph_scheme.len))) |
265 | 0 | { |
266 | 0 | if (have_scheme) |
267 | 0 | return mhd_h2_stream_req_problem (s, |
268 | 0 | mhd_H2_REQ_PRBLM_PSEUDOHDR_DUP); |
269 | 0 | have_scheme = true; |
270 | 0 | } |
271 | 0 | mhd_assert (':' == buff[item->offset]); |
272 | 0 | ++(*pos); |
273 | 0 | } |
274 | | |
275 | 0 | if (! have_method) |
276 | 0 | return mhd_h2_stream_req_problem (s, |
277 | 0 | mhd_H2_REQ_PRBLM_PSEUDOHDR_MISSING); |
278 | | |
279 | 0 | if (mhd_HTTP_METHOD_CONNECT != s->req.method) |
280 | 0 | { |
281 | 0 | if (! have_path || ! have_scheme) |
282 | 0 | return mhd_h2_stream_req_problem (s, |
283 | 0 | mhd_H2_REQ_PRBLM_PSEUDOHDR_EXTRA); |
284 | 0 | } |
285 | 0 | else |
286 | 0 | { |
287 | 0 | if (have_path || have_scheme) |
288 | 0 | return mhd_h2_stream_req_problem (s, |
289 | 0 | mhd_H2_REQ_PRBLM_PSEUDOHDR_MISSING); |
290 | 0 | } |
291 | | |
292 | 0 | return true; |
293 | 0 | } |
294 | | |
295 | | |
296 | | static inline MHD_FN_PAR_NONNULL_ALL_ |
297 | | MHD_FN_PAR_INOUT_ (1) |
298 | | MHD_FN_PAR_OUT_ (2) bool |
299 | | req_headers_preprocess (struct mhd_H2Stream *restrict s, |
300 | | size_t *pos) |
301 | 0 | { |
302 | 0 | static const struct MHD_String h_host = mhd_MSTR_INIT ("host"); |
303 | 0 | static const struct MHD_String h_cntn_len = mhd_MSTR_INIT ("content-length"); |
304 | 0 | struct mhd_H2ReqItemsBlock *const ib = s->c->h2.mem.req_ib; |
305 | 0 | const char *buff = mhd_h2_items_get_strings_buffc (ib); |
306 | |
|
307 | 0 | while (1) |
308 | 0 | { |
309 | 0 | const struct mhd_H2ReqItem *item; |
310 | 0 | item = mhd_h2_items_get_item_nc (ib, |
311 | 0 | *pos); |
312 | |
|
313 | 0 | if (NULL == item) |
314 | 0 | break; |
315 | 0 | else if (mhd_H2_RIK_PSEUDOHEADER == item->kind) |
316 | 0 | return mhd_h2_stream_req_problem (s, |
317 | 0 | mhd_H2_REQ_PRBLM_PSEUDOHDR_AFTER_HDR); |
318 | 0 | else if (mhd_H2_RIK_HEADER != item->kind) |
319 | 0 | (void) 0; /* skip */ |
320 | 0 | else if ((item->name_len == h_cntn_len.len) && |
321 | 0 | (0 == memcmp (buff + item->offset, |
322 | 0 | h_cntn_len.cstr, |
323 | 0 | h_cntn_len.len))) |
324 | 0 | { |
325 | 0 | uint_fast64_t cntnt_len; |
326 | |
|
327 | 0 | if ((0u == item->val_len) || |
328 | 0 | (item->val_len != |
329 | 0 | mhd_str_to_uint64 (buff + item->offset + h_cntn_len.len + 1u, |
330 | 0 | &cntnt_len))) |
331 | 0 | return mhd_h2_stream_req_problem (s, |
332 | 0 | mhd_H2_REQ_PRBLM_CNTNT_LEN_WRONG); |
333 | | |
334 | 0 | if ((MHD_SIZE_UNKNOWN != s->req.cntn_size) && |
335 | 0 | (s->req.cntn_size != cntnt_len)) |
336 | 0 | return mhd_h2_stream_req_problem (s, |
337 | 0 | mhd_H2_REQ_PRBLM_CNTNT_LEN_WRONG); |
338 | 0 | else |
339 | 0 | s->req.cntn_size = cntnt_len; |
340 | 0 | } |
341 | 0 | else if ((item->name_len == h_host.len) && |
342 | 0 | (0 == memcmp (buff + item->offset, |
343 | 0 | h_host.cstr, |
344 | 0 | h_host.len))) |
345 | 0 | { |
346 | 0 | if (mhd_H2_REQ_ITEM_POS_INVALID == s->req.pos_authority) |
347 | 0 | s->req.pos_authority = *pos; |
348 | 0 | else |
349 | 0 | { |
350 | 0 | const struct mhd_H2ReqItem *item_auth = |
351 | 0 | mhd_h2_items_get_item_nc (ib, |
352 | 0 | s->req.pos_authority); |
353 | 0 | mhd_assert (NULL != item_auth); |
354 | 0 | if ((item_auth->val_len != item->val_len) || |
355 | 0 | (! mhd_str_equal_caseless_bin_n (buff + item->offset |
356 | 0 | + item->name_len + 1u, |
357 | 0 | buff + item_auth->offset |
358 | 0 | + item_auth->name_len + 1u, |
359 | 0 | item->val_len))) |
360 | 0 | return |
361 | 0 | mhd_h2_stream_req_problem (s, |
362 | 0 | mhd_H2_REQ_PRBLM_HOST_HDR_WRONG_EXTRA); |
363 | 0 | } |
364 | 0 | } |
365 | | |
366 | 0 | ++(*pos); |
367 | 0 | mhd_assert ((mhd_H2_RIK_HEADER != item->kind) || |
368 | 0 | (':' != buff[item->offset])); |
369 | 0 | } |
370 | | |
371 | 0 | return true; |
372 | 0 | } |
373 | | |
374 | | |
375 | | MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ |
376 | | MHD_FN_PAR_INOUT_ (1) bool |
377 | | mhd_h2_req_headers_preprocess (struct mhd_H2Stream *restrict s) |
378 | 0 | { |
379 | 0 | size_t pos; |
380 | |
|
381 | 0 | mhd_assert (mhd_h2_items_debug_get_streamid (s->c->h2.mem.req_ib) |
382 | 0 | == s->stream_id); |
383 | |
|
384 | 0 | if (! req_validate_fields_chars (s)) |
385 | 0 | return false; |
386 | | |
387 | 0 | if (! req_pseudoheaders_preprocess (s, |
388 | 0 | &pos)) |
389 | 0 | return false; |
390 | | |
391 | 0 | mhd_assert (0u != pos); |
392 | 0 | mhd_assert (mhd_HTTP_METHOD_NO_METHOD != s->req.method); |
393 | |
|
394 | 0 | if (! req_headers_preprocess (s, |
395 | 0 | &pos)) |
396 | 0 | return false; |
397 | | |
398 | 0 | return true; |
399 | 0 | } |
400 | | |
401 | | |
402 | | MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ |
403 | | MHD_FN_PAR_INOUT_ (1) bool |
404 | | mhd_h2_req_uri_parse (struct mhd_H2Stream *restrict s) |
405 | 0 | { |
406 | 0 | struct mhd_H2ReqItemsBlock *const restrict ib = s->c->h2.mem.req_ib; |
407 | 0 | char *const restrict buff = mhd_h2_items_get_strings_buff (ib); |
408 | 0 | struct mhd_H2ReqItem *restrict item = |
409 | 0 | mhd_h2_items_get_item_n (ib, |
410 | 0 | s->req.pos_path); |
411 | 0 | const size_t path_start = (size_t) (item->offset + item->name_len + 1u); |
412 | 0 | char *questn_mark; |
413 | |
|
414 | 0 | questn_mark = (char*) memchr (buff + path_start, |
415 | 0 | '?', |
416 | 0 | (size_t) item->val_len); |
417 | 0 | if (NULL == questn_mark) |
418 | 0 | { |
419 | 0 | item->val_len = |
420 | 0 | (uint_least32_t) |
421 | 0 | mhd_str_dec_norm_uri_path ((size_t) item->val_len, |
422 | 0 | buff + path_start); |
423 | 0 | } |
424 | 0 | else |
425 | 0 | { |
426 | 0 | const size_t path_len = (size_t) (questn_mark - (buff + path_start)); |
427 | 0 | const size_t uri_end = (size_t) (path_start + item->val_len); |
428 | 0 | size_t i = path_start + path_len + 1u; |
429 | |
|
430 | 0 | mhd_assert (path_len < item->val_len); |
431 | |
|
432 | 0 | buff[path_start + path_len] = '\0'; /* Zero-terminate the path */ |
433 | 0 | item->val_len = |
434 | 0 | (uint_least32_t) |
435 | 0 | mhd_str_dec_norm_uri_path (path_len, |
436 | 0 | buff + path_start); |
437 | |
|
438 | 0 | do |
439 | 0 | { |
440 | 0 | size_t name_start; |
441 | 0 | size_t name_len; |
442 | 0 | size_t value_start; |
443 | 0 | size_t value_len; |
444 | |
|
445 | 0 | value_start = 0u; |
446 | 0 | for (name_start = i; i < uri_end; ++i) /* Processing parameter */ |
447 | 0 | { |
448 | 0 | if ('+' == buff[i]) |
449 | 0 | buff[i] = ' '; |
450 | 0 | else if ('=' == buff[i]) |
451 | 0 | { |
452 | | /* Found start of the value */ |
453 | 0 | for (value_start = ++i; i < uri_end; ++i) /* Processing parameter value */ |
454 | 0 | { |
455 | 0 | if ('+' == buff[i]) |
456 | 0 | buff[i] = ' '; |
457 | 0 | else if ('&' == buff[i]) /* delimiter for the next parameter */ |
458 | 0 | break; /* Next parameter */ |
459 | 0 | } |
460 | 0 | break; /* End of the current parameter */ |
461 | 0 | } |
462 | 0 | else if ('&' == buff[i]) |
463 | 0 | break; /* End of the name of the parameter without a value */ |
464 | 0 | } |
465 | | |
466 | | /* PCT-decode, zero-terminate and store the found parameter */ |
467 | |
|
468 | 0 | if (0u != value_start) /* Value cannot start at zero position */ |
469 | 0 | { /* Name with value */ |
470 | 0 | mhd_assert (name_start + 1u <= value_start); |
471 | 0 | name_len = value_start - name_start - 1u; |
472 | 0 | value_len = i - value_start; |
473 | 0 | } |
474 | 0 | else |
475 | 0 | { /* Name without value */ |
476 | 0 | name_len = i - name_start; |
477 | 0 | value_len = 0u; |
478 | 0 | } |
479 | 0 | name_len = mhd_str_pct_decode_lenient_n (buff + name_start, |
480 | 0 | name_len, |
481 | 0 | buff + name_start, |
482 | 0 | name_len, |
483 | 0 | NULL); // TODO: add support for broken encoding detection |
484 | 0 | buff[name_start + name_len] = 0; |
485 | |
|
486 | 0 | if (0u != value_start) |
487 | 0 | { |
488 | 0 | value_len = |
489 | 0 | mhd_str_pct_decode_lenient_n (buff + name_start + name_len + 1u, |
490 | 0 | value_len, |
491 | 0 | buff + value_start, |
492 | 0 | value_len, |
493 | 0 | NULL); // TODO: add support for broken encoding detection |
494 | 0 | buff[value_start + value_len] = 0; |
495 | 0 | } |
496 | |
|
497 | 0 | if (! mhd_h2_items_reserve_new_item (ib)) |
498 | 0 | break; // TODO: support reporting no space errors |
499 | | |
500 | 0 | mhd_h2_items_add_new_item_reserved (ib, |
501 | 0 | name_start, |
502 | 0 | name_len, |
503 | 0 | value_len, |
504 | 0 | (0u != value_start) |
505 | 0 | ? mhd_H2_RIK_URI_PARAM : |
506 | 0 | mhd_H2_RIK_URI_PARAM_NV); |
507 | |
|
508 | 0 | } while (uri_end > ++i); |
509 | |
|
510 | 0 | } |
511 | |
|
512 | 0 | return true; |
513 | 0 | } |
514 | | |
515 | | |
516 | | MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ |
517 | | MHD_FN_PAR_INOUT_ (1) bool |
518 | | mhd_h2_req_cookie_parse (struct mhd_H2Stream *restrict s) |
519 | 0 | { |
520 | | // TODO: handle cookie combining |
521 | | // TODO: Implement cookie parsing |
522 | 0 | return true; |
523 | 0 | } |