@@ -36,6 +36,9 @@ static const size_t s_scratch_space_size = 9;
3636/* Stream ids & dependencies should only write the bottom 31 bits */
3737static const uint32_t s_31_bit_mask = UINT32_MAX >> 1 ;
3838
39+ /* initial size for cookie buffer, buffer will grow if needed */
40+ static const size_t s_decoder_cookie_buffer_initial_size = 512 ;
41+
3942#define DECODER_LOGF (level , decoder , text , ...) \
4043 AWS_LOGF_##level(AWS_LS_HTTP_DECODER, "id=%p " text, (decoder)->logging_id, __VA_ARGS__)
4144#define DECODER_LOG (level , decoder , text ) DECODER_LOGF(level, decoder, "%s", text)
@@ -245,6 +248,12 @@ struct aws_h2_decoder {
245248 * A malformed header-block is not a connection error, it's a Stream Error (RFC-7540 5.4.2).
246249 * We continue decoding and report that it's malformed in on_headers_end(). */
247250 bool malformed ;
251+
252+ /* Buffer up cookie header fields to concatenate separate ones */
253+ struct aws_byte_buf cookies ;
254+ /* If separate cookie fields have different compression types, the concatenated cookie uses the strictest type.
255+ */
256+ enum aws_http_header_compression cookie_header_compression_type ;
248257 } header_block_in_progress ;
249258
250259 /* Settings for decoder, which is based on the settings sent to the peer and ACKed by peer */
@@ -280,7 +289,7 @@ struct aws_h2_decoder *aws_h2_decoder_new(struct aws_h2_decoder_params *params)
280289 void * allocation = aws_mem_acquire_many (
281290 params -> alloc , 2 , & decoder , sizeof (struct aws_h2_decoder ), & scratch_buf , s_scratch_space_size );
282291 if (!allocation ) {
283- goto failed_alloc ;
292+ goto error ;
284293 }
285294
286295 AWS_ZERO_STRUCT (* decoder );
@@ -295,7 +304,7 @@ struct aws_h2_decoder *aws_h2_decoder_new(struct aws_h2_decoder_params *params)
295304
296305 decoder -> hpack = aws_hpack_context_new (params -> alloc , AWS_LS_HTTP_DECODER , decoder );
297306 if (!decoder -> hpack ) {
298- goto failed_new_hpack ;
307+ goto error ;
299308 }
300309
301310 if (decoder -> is_server && !params -> skip_connection_preface ) {
@@ -311,23 +320,34 @@ struct aws_h2_decoder *aws_h2_decoder_new(struct aws_h2_decoder_params *params)
311320
312321 if (aws_array_list_init_dynamic (
313322 & decoder -> settings_buffer_list , decoder -> alloc , 0 , sizeof (struct aws_h2_frame_setting ))) {
314- goto array_list_failed ;
323+ goto error ;
324+ }
325+
326+ if (aws_byte_buf_init (
327+ & decoder -> header_block_in_progress .cookies , decoder -> alloc , s_decoder_cookie_buffer_initial_size )) {
328+ goto error ;
315329 }
316330
317331 return decoder ;
318332
319- failed_new_hpack :
320- array_list_failed :
333+ error :
334+ if (decoder ) {
335+ aws_hpack_context_destroy (decoder -> hpack );
336+ aws_array_list_clean_up (& decoder -> settings_buffer_list );
337+ aws_byte_buf_clean_up (& decoder -> header_block_in_progress .cookies );
338+ }
321339 aws_mem_release (params -> alloc , allocation );
322- failed_alloc :
323340 return NULL ;
324341}
325342
326343static void s_reset_header_block_in_progress (struct aws_h2_decoder * decoder ) {
327344 for (size_t i = 0 ; i < PSEUDOHEADER_COUNT ; ++ i ) {
328345 aws_string_destroy (decoder -> header_block_in_progress .pseudoheader_values [i ]);
329346 }
347+ struct aws_byte_buf cookie_backup = decoder -> header_block_in_progress .cookies ;
330348 AWS_ZERO_STRUCT (decoder -> header_block_in_progress );
349+ decoder -> header_block_in_progress .cookies = cookie_backup ;
350+ aws_byte_buf_reset (& decoder -> header_block_in_progress .cookies , false);
331351}
332352
333353void aws_h2_decoder_destroy (struct aws_h2_decoder * decoder ) {
@@ -337,6 +357,7 @@ void aws_h2_decoder_destroy(struct aws_h2_decoder *decoder) {
337357 aws_array_list_clean_up (& decoder -> settings_buffer_list );
338358 aws_hpack_context_destroy (decoder -> hpack );
339359 s_reset_header_block_in_progress (decoder );
360+ aws_byte_buf_clean_up (& decoder -> header_block_in_progress .cookies );
340361 aws_mem_release (decoder -> alloc , decoder );
341362}
342363
@@ -1263,11 +1284,35 @@ static int s_process_header_field(struct aws_h2_decoder *decoder, const struct a
12631284
12641285 /* #TODO Validate characters used in header_field->value */
12651286
1266- /* Deliver header-field via callback */
1267- if (current_block -> is_push_promise ) {
1268- DECODER_CALL_VTABLE_STREAM_ARGS (decoder , on_push_promise_i , header_field , name_enum );
1269- } else {
1270- DECODER_CALL_VTABLE_STREAM_ARGS (decoder , on_headers_i , header_field , name_enum , current_block -> block_type );
1287+ switch (name_enum ) {
1288+ case AWS_HTTP_HEADER_COOKIE :
1289+ /* for a header cookie, we will not fire callback until we concatenate them all, let's store it at the
1290+ * buffer */
1291+ if (header_field -> compression > current_block -> cookie_header_compression_type ) {
1292+ current_block -> cookie_header_compression_type = header_field -> compression ;
1293+ }
1294+
1295+ if (current_block -> cookies .len ) {
1296+ /* add a delimiter */
1297+ struct aws_byte_cursor delimiter = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL ("; " );
1298+ if (aws_byte_buf_append_dynamic (& current_block -> cookies , & delimiter )) {
1299+ return AWS_OP_ERR ;
1300+ }
1301+ }
1302+ if (aws_byte_buf_append_dynamic (& current_block -> cookies , & header_field -> value )) {
1303+ return AWS_OP_ERR ;
1304+ }
1305+ break ;
1306+
1307+ default :
1308+ /* Deliver header-field via callback */
1309+ if (current_block -> is_push_promise ) {
1310+ DECODER_CALL_VTABLE_STREAM_ARGS (decoder , on_push_promise_i , header_field , name_enum );
1311+ } else {
1312+ DECODER_CALL_VTABLE_STREAM_ARGS (
1313+ decoder , on_headers_i , header_field , name_enum , current_block -> block_type );
1314+ }
1315+ break ;
12711316 }
12721317 }
12731318
@@ -1282,6 +1327,29 @@ static int s_process_header_field(struct aws_h2_decoder *decoder, const struct a
12821327 return AWS_OP_SUCCESS ;
12831328}
12841329
1330+ static int s_flush_cookie_header (struct aws_h2_decoder * decoder ) {
1331+ struct aws_header_block_in_progress * current_block = & decoder -> header_block_in_progress ;
1332+ if (current_block -> malformed ) {
1333+ return AWS_OP_SUCCESS ;
1334+ }
1335+ if (current_block -> cookies .len == 0 ) {
1336+ /* Nothing to flush */
1337+ return AWS_OP_SUCCESS ;
1338+ }
1339+ struct aws_http_header concatenated_cookie ;
1340+ struct aws_byte_cursor header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL ("cookie" );
1341+ concatenated_cookie .name = header_name ;
1342+ concatenated_cookie .value = aws_byte_cursor_from_buf (& current_block -> cookies );
1343+ concatenated_cookie .compression = current_block -> cookie_header_compression_type ;
1344+ if (current_block -> is_push_promise ) {
1345+ DECODER_CALL_VTABLE_STREAM_ARGS (decoder , on_push_promise_i , & concatenated_cookie , AWS_HTTP_HEADER_COOKIE );
1346+ } else {
1347+ DECODER_CALL_VTABLE_STREAM_ARGS (
1348+ decoder , on_headers_i , & concatenated_cookie , AWS_HTTP_HEADER_COOKIE , current_block -> block_type );
1349+ }
1350+ return AWS_OP_SUCCESS ;
1351+ }
1352+
12851353/* This state checks whether we've consumed the current frame's entire header-block fragment.
12861354 * We revisit this state after each entry is decoded.
12871355 * This state consumes no data. */
@@ -1297,6 +1365,10 @@ static int s_state_fn_header_block_loop(struct aws_h2_decoder *decoder, struct a
12971365 if (s_flush_pseudoheaders (decoder )) {
12981366 return AWS_OP_ERR ;
12991367 }
1368+ /* flush the concatenated cookie header */
1369+ if (s_flush_cookie_header (decoder )) {
1370+ return AWS_OP_ERR ;
1371+ }
13001372
13011373 bool malformed = decoder -> header_block_in_progress .malformed ;
13021374 DECODER_LOGF (TRACE , decoder , "Done decoding header-block, malformed=%d" , malformed );
@@ -1389,8 +1461,6 @@ static int s_state_fn_header_block_entry(struct aws_h2_decoder *decoder, struct
13891461 * If dynamic table size changed via SETTINGS frame, next header-block must start with DYNAMIC_TABLE_RESIZE entry.
13901462 * Is it illegal to receive a resize entry at other times? */
13911463
1392- /* #TODO Cookie headers must be concatenated into single delivery RFC-7540 8.1.2.5 */
1393-
13941464 /* #TODO The TE header field ... MUST NOT contain any value other than "trailers" */
13951465
13961466 if (result .type == AWS_HPACK_DECODE_T_HEADER_FIELD ) {
0 commit comments