From 1db21953894657b6225be50bd369fe9870f71687 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sun, 9 Feb 2014 00:35:21 +0900 Subject: [PATCH] Implement padding for HEADERS and CONTINUATION --- lib/includes/nghttp2/nghttp2.h | 31 +++- lib/nghttp2_frame.c | 83 +++++---- lib/nghttp2_frame.h | 22 ++- lib/nghttp2_session.c | 329 ++++++++++++++++++++++++--------- lib/nghttp2_session.h | 13 +- src/HttpServer.cc | 6 +- src/HttpServer.h | 2 +- src/app_helper.cc | 36 +++- src/nghttp.cc | 14 +- src/nghttpd.cc | 6 +- tests/nghttp2_frame_test.c | 28 ++- tests/nghttp2_session_test.c | 74 ++++---- tests/nghttp2_test_helper.c | 5 + tests/nghttp2_test_helper.h | 3 + 14 files changed, 453 insertions(+), 199 deletions(-) diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h index 4ef0f410..15b9173f 100644 --- a/lib/includes/nghttp2/nghttp2.h +++ b/lib/includes/nghttp2/nghttp2.h @@ -151,9 +151,9 @@ typedef struct { * @macro * * The default value of DATA padding alignment. See - * :member:`NGHTTP2_OPT_DATA_PAD_ALIGNMENT`. + * :member:`NGHTTP2_OPT_PAD_ALIGNMENT`. */ -#define NGHTTP2_DATA_PAD_ALIGNMENT 256 +#define NGHTTP2_PAD_ALIGNMENT 256 /** * @enum @@ -655,6 +655,11 @@ typedef struct { * The frame header. */ nghttp2_frame_hd hd; + /** + * The length of the padding in this frame. This includes PAD_HIGH + * and PAD_LOW. + */ + size_t padlen; /** * The name/value pairs. */ @@ -746,6 +751,11 @@ typedef struct { * The frame header. */ nghttp2_frame_hd hd; + /** + * The length of the padding in this frame. This includes PAD_HIGH + * and PAD_LOW. + */ + size_t padlen; /** * The name/value pairs. */ @@ -1324,13 +1334,14 @@ typedef enum { */ NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 2, /** - * This option specifies the alignment of padding in DATA frame. If - * this option is set to N, padding is added to DATA payload so that - * its payload length is divisible by N. Due to flow control, - * padding is not always added according to this alignment. The - * option value must be greater than or equal to 8. + * This option specifies the alignment of padding in frame + * payload. If this option is set to N, padding is added to frame + * payload so that its payload length is divisible by N. For DATA + * frame, due to flow control, padding is not always added according + * to this alignment. The option value must be greater than or equal + * to 8. */ - NGHTTP2_OPT_DATA_PAD_ALIGNMENT = 1 << 3 + NGHTTP2_OPT_PAD_ALIGNMENT = 1 << 3 } nghttp2_opt; /** @@ -1352,9 +1363,9 @@ typedef struct { */ uint8_t no_auto_connection_window_update; /** - * :enum:`NGHTTP2_OPT_DATA_PAD_ALIGNMENT` + * :enum:`NGHTTP2_OPT_PAD_ALIGNMENT` */ - uint16_t data_pad_alignment; + uint16_t pad_alignment; } nghttp2_opt_set; /** diff --git a/lib/nghttp2_frame.c b/lib/nghttp2_frame.c index be1fc177..9e7096a0 100644 --- a/lib/nghttp2_frame.c +++ b/lib/nghttp2_frame.c @@ -193,9 +193,9 @@ void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata) } } -size_t nghttp2_frame_data_trail_padlen(nghttp2_data *frame) +size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen) { - return frame->padlen + return padlen - ((frame->hd.flags & NGHTTP2_FLAG_PAD_HIGH) > 0) - ((frame->hd.flags & NGHTTP2_FLAG_PAD_LOW) > 0); } @@ -214,19 +214,6 @@ void nghttp2_frame_private_data_init(nghttp2_private_data *frame, void nghttp2_frame_private_data_free(nghttp2_private_data *frame) {} -/* - * Returns the offset of the name/header block in the HEADERS frame, - * including frame header length. - */ -static size_t headers_nv_offset(nghttp2_headers *frame) -{ - if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { - return NGHTTP2_FRAME_HEAD_LENGTH + 4; - } else { - return NGHTTP2_FRAME_HEAD_LENGTH; - } -} - size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame) { if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { @@ -238,23 +225,41 @@ size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame) ssize_t nghttp2_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr, + size_t *bufoff_ptr, nghttp2_headers *frame, - nghttp2_hd_deflater *deflater) + nghttp2_hd_deflater *deflater, + size_t align) { - ssize_t framelen; - size_t nv_offset = headers_nv_offset(frame); + size_t payloadoff = NGHTTP2_FRAME_HEAD_LENGTH + 2; + size_t nv_offset = + payloadoff + nghttp2_frame_headers_payload_nv_offset(frame); ssize_t rv; + size_t payloadlen; + rv = nghttp2_hd_deflate_hd(deflater, buf_ptr, buflen_ptr, nv_offset, frame->nva, frame->nvlen); if(rv < 0) { return rv; } - framelen = rv + nv_offset; - if(NGHTTP2_FRAME_HEAD_LENGTH + NGHTTP2_MAX_FRAME_LENGTH < rv + nv_offset) { - frame->hd.length = NGHTTP2_MAX_FRAME_LENGTH; - frame->hd.flags &= ~NGHTTP2_FLAG_END_HEADERS; + + payloadlen = nghttp2_frame_headers_payload_nv_offset(frame) + rv; + + if(align > 0) { + ssize_t padlen; + padlen = nghttp2_frame_add_pad(buf_ptr, buflen_ptr, bufoff_ptr, + &frame->hd.flags, + payloadlen, + payloadlen + align, + align); + if(padlen < 0) { + return padlen; + } + frame->padlen = padlen; + frame->hd.length = payloadlen + padlen; } else { - frame->hd.length = framelen - NGHTTP2_FRAME_HEAD_LENGTH; + *bufoff_ptr = 2; + frame->padlen = 0; + frame->hd.length = payloadlen; } /* If frame->nvlen == 0, *buflen_ptr may be smaller than nv_offset */ @@ -262,13 +267,21 @@ ssize_t nghttp2_frame_pack_headers(uint8_t **buf_ptr, if(rv < 0) { return rv; } - memset(*buf_ptr, 0, nv_offset); + memset(*buf_ptr + *bufoff_ptr, 0, NGHTTP2_FRAME_HEAD_LENGTH); /* pack ctrl header after length is determined */ - nghttp2_frame_pack_frame_hd(*buf_ptr, &frame->hd); - if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { - nghttp2_put_uint32be(&(*buf_ptr)[8], frame->pri); + if(NGHTTP2_FRAME_HEAD_LENGTH + NGHTTP2_MAX_FRAME_LENGTH < rv + nv_offset) { + /* Needs CONTINUATION */ + nghttp2_frame_hd hd = frame->hd; + hd.flags &= ~(NGHTTP2_FLAG_END_HEADERS | + NGHTTP2_FLAG_PAD_HIGH | NGHTTP2_FLAG_PAD_LOW); + nghttp2_frame_pack_frame_hd(*buf_ptr + *bufoff_ptr, &hd); + } else { + nghttp2_frame_pack_frame_hd(*buf_ptr + *bufoff_ptr, &frame->hd); } - return framelen; + if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { + nghttp2_put_uint32be(&(*buf_ptr)[payloadoff], frame->pri); + } + return frame->hd.length + NGHTTP2_FRAME_HEAD_LENGTH + *bufoff_ptr; } int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, @@ -276,7 +289,6 @@ int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, size_t payloadlen) { if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { - assert(payloadlen == 4); frame->pri = nghttp2_get_uint32(payload) & NGHTTP2_PRIORITY_MASK; } else { frame->pri = NGHTTP2_PRI_DEFAULT; @@ -660,19 +672,23 @@ ssize_t nghttp2_frame_add_pad(uint8_t **buf_ptr, size_t *buflen_ptr, payloadmax); size_t padlen = nextlen - payloadlen; size_t trail_padlen = 0; - size_t headoff = 2; - size_t trail_padoff = headoff + NGHTTP2_FRAME_HEAD_LENGTH + payloadlen; + /* extra 2 bytes for PAD_HIGH and PAD_LOW. */ + size_t trail_padoff = 2 + NGHTTP2_FRAME_HEAD_LENGTH + payloadlen; + if(padlen > 257) { - headoff = 0; + *bufoff_ptr = 0; trail_padlen = padlen - 2; *flags_ptr |= NGHTTP2_FLAG_PAD_HIGH | NGHTTP2_FLAG_PAD_LOW; (*buf_ptr)[NGHTTP2_FRAME_HEAD_LENGTH] = trail_padlen >> 8; (*buf_ptr)[NGHTTP2_FRAME_HEAD_LENGTH + 1] = trail_padlen & 0xff; } else if(padlen > 0) { - headoff = 1; + *bufoff_ptr = 1; trail_padlen = padlen - 1; *flags_ptr |= NGHTTP2_FLAG_PAD_LOW; (*buf_ptr)[NGHTTP2_FRAME_HEAD_LENGTH + 1] = trail_padlen; + } else { + *bufoff_ptr = 2; + return 0; } rv = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, @@ -682,7 +698,6 @@ ssize_t nghttp2_frame_add_pad(uint8_t **buf_ptr, size_t *buflen_ptr, } memset((*buf_ptr) + trail_padoff, 0, trail_padlen); - *bufoff_ptr = headoff; return padlen; } diff --git a/lib/nghttp2_frame.h b/lib/nghttp2_frame.h index b9224fd4..3496863c 100644 --- a/lib/nghttp2_frame.h +++ b/lib/nghttp2_frame.h @@ -104,14 +104,21 @@ size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame); * expansion occurred, memory previously pointed by |*buf_ptr| may * change. |*buf_ptr| and |*buflen_ptr| are updated accordingly. * + * The first byte the frame is serialized is returned in the + * |*bufoff_ptr|. + * + * The |align| is used as padding alignment. If the |align| is zero, + * no padding is added. + * * frame->hd.length is assigned after length is determined during * packing process. If payload length is strictly larger than * NGHTTP2_MAX_FRAME_LENGTH, payload data is still serialized as is, * but frame->hd.length is set to NGHTTP2_MAX_FRAME_LENGTH and * NGHTTP2_FLAG_END_HEADERS flag is cleared from frame->hd.flags. * - * This function returns the size of packed frame if it succeeds, or - * returns one of the following negative error codes: + * This function returns the size of packed frame (which includes + * |*bufoff_ptr| bytes) if it succeeds, or returns one of the + * following negative error codes: * * NGHTTP2_ERR_HEADER_COMP * The deflate operation failed. @@ -122,8 +129,10 @@ size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame); */ ssize_t nghttp2_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr, + size_t *bufoff_ptr, nghttp2_headers *frame, - nghttp2_hd_deflater *deflater); + nghttp2_hd_deflater *deflater, + size_t align); /* * Unpacks HEADERS frame byte sequence into |frame|. This function @@ -427,10 +436,11 @@ void nghttp2_frame_window_update_free(nghttp2_window_update *frame); void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata); /* - * Returns the number of padding data after application data - * payload. Thus this does not include the PAD_HIGH and PAD_LOW. + * Returns the number of padding bytes after payload. The total + * padding length is given in the |padlen|. The returned value does + * not include the PAD_HIGH and PAD_LOW. */ -size_t nghttp2_frame_data_trail_padlen(nghttp2_data *frame); +size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen); void nghttp2_frame_private_data_init(nghttp2_private_data *frame, uint8_t flags, diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index 6f77ff17..cba080ed 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -173,6 +173,7 @@ static void nghttp2_inbound_frame_reset(nghttp2_session *session) iframe->left = NGHTTP2_FRAME_HEAD_LENGTH; iframe->niv = 0; iframe->payloadleft = 0; + iframe->padlen = 0; iframe->error_code = 0; iframe->buflen = 0; } @@ -223,11 +224,11 @@ static int nghttp2_session_new(nghttp2_session **session_ptr, (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_CONNECTION_WINDOW_UPDATE; } - if((opt_set_mask & NGHTTP2_OPT_DATA_PAD_ALIGNMENT) && - opt_set->data_pad_alignment >= 8) { - (*session_ptr)->data_pad_alignment = opt_set->data_pad_alignment; + if((opt_set_mask & NGHTTP2_OPT_PAD_ALIGNMENT) && + opt_set->pad_alignment >= 8) { + (*session_ptr)->pad_alignment = opt_set->pad_alignment; } else { - (*session_ptr)->data_pad_alignment = NGHTTP2_DATA_PAD_ALIGNMENT; + (*session_ptr)->pad_alignment = NGHTTP2_PAD_ALIGNMENT; } (*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; @@ -1119,8 +1120,10 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, } framebuflen = nghttp2_frame_pack_headers(&session->aob.framebuf, &session->aob.framebufmax, + &session->aob.framebufoff, &frame->headers, - &session->hd_deflater); + &session->hd_deflater, + session->pad_alignment); if(framebuflen < 0) { return framebuflen; } @@ -1442,8 +1445,11 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session) session->aob.framebufmark, NGHTTP2_MAX_FRAME_LENGTH); cont_hd.type = NGHTTP2_CONTINUATION; - if(cont_hd.length < NGHTTP2_MAX_FRAME_LENGTH) { + if(cont_hd.length + session->aob.framebufmark == + session->aob.framebuflen) { cont_hd.flags = NGHTTP2_FLAG_END_HEADERS; + cont_hd.flags |= + (NGHTTP2_FLAG_PAD_HIGH | NGHTTP2_FLAG_PAD_LOW) & frame->hd.flags; } else { cont_hd.flags = NGHTTP2_FLAG_NONE; } @@ -1456,9 +1462,6 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session) session->aob.framebufoff -= NGHTTP2_FRAME_HEAD_LENGTH; return 0; } - /* Update frame payload length to include data sent in - CONTINUATION frame. */ - frame->hd.length = session->aob.framebuflen - NGHTTP2_FRAME_HEAD_LENGTH; } rv = session_call_on_frame_send(session, frame); if(nghttp2_is_fatal(rv)) { @@ -3318,24 +3321,73 @@ static int inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) * accordingly. If padding is set, this function returns 1. If no * padding is set, this function returns 0. On error, returns -1. */ -static int inbound_frame_handle_pad(nghttp2_inbound_frame *iframe) +static int inbound_frame_handle_pad(nghttp2_inbound_frame *iframe, + nghttp2_frame_hd *hd) { - if(iframe->frame.hd.flags & NGHTTP2_FLAG_PAD_HIGH) { - if((iframe->frame.hd.flags & NGHTTP2_FLAG_PAD_LOW) == 0) { + if(hd->flags & NGHTTP2_FLAG_PAD_HIGH) { + if((hd->flags & NGHTTP2_FLAG_PAD_LOW) == 0) { return -1; } iframe->state = NGHTTP2_IB_READ_NBYTE; iframe->left = 2; return 1; } - if(iframe->frame.hd.flags & NGHTTP2_FLAG_PAD_LOW) { + if(hd->flags & NGHTTP2_FLAG_PAD_LOW) { iframe->state = NGHTTP2_IB_READ_NBYTE; iframe->left = 1; return 1; } + DEBUGF(fprintf(stderr, "no padding\n")); return 0; } +/* + * Computes number of padding based on flags. This function returns + * the calculated length if it succeeds, or -1. + */ +static ssize_t inbound_frame_compute_pad(nghttp2_inbound_frame *iframe) +{ + size_t padlen; + padlen = iframe->buf[0]; + if(iframe->frame.hd.flags & NGHTTP2_FLAG_PAD_HIGH) { + padlen <<= 8; + padlen |= iframe->buf[1]; + ++padlen; + } + ++padlen; + DEBUGF(fprintf(stderr, "padlen=%zu\n", padlen)); + if(padlen > iframe->frame.hd.length) { + return -1; + } + iframe->padlen = padlen; + return padlen; +} + +/* + * This function returns the effective payload length in the data of + * length |readlen| when the remaning payload is |payloadleft|. The + * |payloadleft| does not include |readlen|. If padding was started + * strictly before this data chunk, this function returns -1. + */ +static ssize_t inbound_frame_effective_readlen(nghttp2_inbound_frame *iframe, + size_t payloadleft, + size_t readlen) +{ + size_t trail_padlen = nghttp2_frame_trail_padlen(&iframe->frame, + iframe->padlen); + + if(trail_padlen > payloadleft) { + size_t padlen; + padlen = trail_padlen - payloadleft; + if(readlen < padlen) { + return -1; + } else { + return readlen - padlen; + } + } + return readlen; +} + ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, size_t inlen) { @@ -3363,7 +3415,6 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, switch(iframe->frame.hd.type) { case NGHTTP2_DATA: { DEBUGF(fprintf(stderr, "DATA\n")); - iframe->frame.data.padlen = 0; /* Check stream is open. If it is not open or closing, ignore payload. */ busy = 1; @@ -3377,7 +3428,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, if(nghttp2_is_fatal(rv)) { return rv; } - rv = inbound_frame_handle_pad(iframe); + rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); if(rv < 0) { iframe->state = NGHTTP2_IB_IGN_DATA; rv = nghttp2_session_terminate_session(session, @@ -3395,6 +3446,20 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, } case NGHTTP2_HEADERS: DEBUGF(fprintf(stderr, "HEADERS\n")); + rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); + if(rv < 0) { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + rv = nghttp2_session_terminate_session(session, + NGHTTP2_PROTOCOL_ERROR); + if(nghttp2_is_fatal(rv)) { + return rv; + } + break; + } + if(rv == 1) { + break; + } if(iframe->frame.hd.flags & NGHTTP2_FLAG_PRIORITY) { if(iframe->payloadleft < 4) { busy = 1; @@ -3497,16 +3562,8 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, switch(iframe->frame.hd.type) { case NGHTTP2_DATA: busy = 1; - iframe->frame.data.padlen = iframe->buf[0]; - if(iframe->frame.hd.flags & NGHTTP2_FLAG_PAD_HIGH) { - iframe->frame.data.padlen <<= 8; - iframe->frame.data.padlen |= iframe->buf[1]; - ++iframe->frame.data.padlen; - } - ++iframe->frame.data.padlen; - - DEBUGF(fprintf(stderr, "padlen=%zu\n", iframe->frame.data.padlen)); - if(iframe->frame.data.padlen > iframe->frame.hd.length) { + rv = inbound_frame_compute_pad(iframe); + if(rv < 0) { rv = nghttp2_session_terminate_session(session, NGHTTP2_PROTOCOL_ERROR); if(nghttp2_is_fatal(rv)) { @@ -3515,9 +3572,36 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, iframe->state = NGHTTP2_IB_IGN_DATA; break; } + iframe->frame.data.padlen = rv; iframe->state = NGHTTP2_IB_READ_DATA; break; case NGHTTP2_HEADERS: + if(iframe->padlen == 0 && + iframe->frame.hd.flags & NGHTTP2_FLAG_PAD_LOW) { + rv = inbound_frame_compute_pad(iframe); + if(rv < 0) { + busy = 1; + rv = nghttp2_session_terminate_session(session, + NGHTTP2_PROTOCOL_ERROR); + if(nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; + } + iframe->frame.headers.padlen = rv; + if(iframe->frame.hd.flags & NGHTTP2_FLAG_PRIORITY) { + if(iframe->payloadleft < 4) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + iframe->state = NGHTTP2_IB_READ_NBYTE; + iframe->left = 4; + iframe->buflen = 0; + break; + } + } rv = session_process_headers_frame(session); if(nghttp2_is_fatal(rv)) { return rv; @@ -3580,7 +3664,8 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, } break; case NGHTTP2_IB_READ_HEADER_BLOCK: - case NGHTTP2_IB_IGN_HEADER_BLOCK: + case NGHTTP2_IB_IGN_HEADER_BLOCK: { + ssize_t data_readlen; #ifdef DEBUGBUILD if(iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { fprintf(stderr, "[IB_READ_HEADER_BLOCK]\n"); @@ -3591,68 +3676,94 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, readlen = inbound_frame_payload_readlen(iframe, in, last); DEBUGF(fprintf(stderr, "readlen=%zu, payloadleft=%zu\n", readlen, iframe->payloadleft - readlen)); - DEBUGF(fprintf(stderr, "block final=%d\n", - (iframe->frame.hd.flags & - NGHTTP2_FLAG_END_HEADERS) && - iframe->payloadleft == readlen)); - rv = inflate_header_block(session, &iframe->frame, &readlen, - (uint8_t*)in, readlen, - (iframe->frame.hd.flags & - NGHTTP2_FLAG_END_HEADERS) && - iframe->payloadleft == readlen, - iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK); - if(nghttp2_is_fatal(rv)) { - return rv; - } - in += readlen; - iframe->payloadleft -= readlen; - if(rv == NGHTTP2_ERR_PAUSE) { - return in - first; - } - if(rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - /* The application says no more headers. We decompress the - rest of the header block but not invoke on_header_callback - and on_frame_recv_callback. */ - rv = nghttp2_session_add_rst_stream(session, - iframe->frame.hd.stream_id, - NGHTTP2_INTERNAL_ERROR); + + data_readlen = inbound_frame_effective_readlen + (iframe, iframe->payloadleft - readlen, readlen); + if(data_readlen >= 0) { + size_t trail_padlen; + size_t hd_proclen = 0; + trail_padlen = nghttp2_frame_trail_padlen(&iframe->frame, + iframe->padlen); + DEBUGF(fprintf(stderr, "block final=%d\n", + (iframe->frame.hd.flags & + NGHTTP2_FLAG_END_HEADERS) && + iframe->payloadleft - data_readlen == trail_padlen)); + + rv = inflate_header_block + (session, &iframe->frame, &hd_proclen, + (uint8_t*)in, data_readlen, + (iframe->frame.hd.flags & + NGHTTP2_FLAG_END_HEADERS) && + iframe->payloadleft - data_readlen == trail_padlen, + iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK); + if(nghttp2_is_fatal(rv)) { return rv; } - iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; - break; - } - if(rv == NGHTTP2_ERR_HEADER_COMP) { - /* GOAWAY is already issued */ - if(iframe->payloadleft == 0) { - nghttp2_inbound_frame_reset(session); - } else { - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + if(rv == NGHTTP2_ERR_PAUSE) { + in += hd_proclen; + iframe->payloadleft -= hd_proclen; + + return in - first; } - break; + + in += readlen; + iframe->payloadleft -= readlen; + + if(rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + /* The application says no more headers. We decompress the + rest of the header block but not invoke on_header_callback + and on_frame_recv_callback. */ + rv = nghttp2_session_add_rst_stream(session, + iframe->frame.hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + if(nghttp2_is_fatal(rv)) { + return rv; + } + busy = 1; + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + if(rv == NGHTTP2_ERR_HEADER_COMP) { + /* GOAWAY is already issued */ + if(iframe->payloadleft == 0) { + nghttp2_inbound_frame_reset(session); + } else { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + } + break; + } + } else { + in += readlen; + iframe->payloadleft -= readlen; } + if(iframe->payloadleft) { break; } - if(iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { - rv = session_after_header_block_received(session); - if(nghttp2_is_fatal(rv)) { - return rv; - } - } if((iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) { iframe->left = NGHTTP2_FRAME_HEAD_LENGTH; iframe->error_code = 0; iframe->buflen = 0; + iframe->padlen = 0; if(iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { iframe->state = NGHTTP2_IB_EXPECT_CONTINUATION; } else { iframe->state = NGHTTP2_IB_IGN_CONTINUATION; } } else { + if(iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { + rv = session_after_header_block_received(session); + if(nghttp2_is_fatal(rv)) { + return rv; + } + } nghttp2_inbound_frame_reset(session); } break; + } case NGHTTP2_IB_IGN_PAYLOAD: DEBUGF(fprintf(stderr, "[IB_IGN_PAYLOAD]\n")); readlen = inbound_frame_payload_readlen(iframe, in, last); @@ -3761,9 +3872,30 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, iframe->state = NGHTTP2_IB_IGN_PAYLOAD; break; } - if(cont_hd.flags & NGHTTP2_FLAG_END_HEADERS) { - iframe->frame.hd.flags |= NGHTTP2_FLAG_END_HEADERS; + iframe->frame.hd.flags |= cont_hd.flags & + (NGHTTP2_FLAG_END_HEADERS | + NGHTTP2_FLAG_PAD_HIGH | NGHTTP2_FLAG_PAD_LOW); + + rv = inbound_frame_handle_pad(iframe, &cont_hd); + if(rv < 0) { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + rv = nghttp2_session_terminate_session(session, + NGHTTP2_PROTOCOL_ERROR); + if(nghttp2_is_fatal(rv)) { + return rv; + } + break; } + if(rv == 1) { + if(iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) { + iframe->state = NGHTTP2_IB_READ_PAD_CONTINUATION; + } else { + iframe->state = NGHTTP2_IB_IGN_PAD_CONTINUATION; + } + break; + } + busy = 1; if(iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) { iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; @@ -3771,6 +3903,46 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; } break; + case NGHTTP2_IB_READ_PAD_CONTINUATION: + case NGHTTP2_IB_IGN_PAD_CONTINUATION: +#ifdef DEBUGBUILD + if(iframe->state == NGHTTP2_IB_READ_PAD_CONTINUATION) { + fprintf(stderr, "[IB_READ_PAD_CONTINUATION]\n"); + } else { + fprintf(stderr, "[IB_IGN_PAD_CONTINUATION]\n"); + } +#endif /* DEBUGBUILD */ + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + iframe->payloadleft -= readlen; + DEBUGF(fprintf(stderr, "readlen=%zu, payloadleft=%zu, left=%zu\n", + readlen, iframe->payloadleft, iframe->left)); + if(iframe->left) { + return in - first; + } + busy = 1; + rv = inbound_frame_compute_pad(iframe); + if(rv < 0) { + rv = nghttp2_session_terminate_session(session, + NGHTTP2_PROTOCOL_ERROR); + if(nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; + } + iframe->padlen = rv; + if(iframe->frame.hd.type == NGHTTP2_HEADERS) { + iframe->frame.headers.padlen += rv; + } else { + iframe->frame.push_promise.padlen += rv; + } + if(iframe->state == NGHTTP2_IB_READ_PAD_CONTINUATION) { + iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; + } else { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + } + break; case NGHTTP2_IB_READ_DATA: DEBUGF(fprintf(stderr, "[IB_READ_DATA]\n")); readlen = inbound_frame_payload_readlen(iframe, in, last); @@ -3779,7 +3951,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, DEBUGF(fprintf(stderr, "readlen=%zu, payloadleft=%zu\n", readlen, iframe->payloadleft)); if(readlen > 0) { - size_t data_readlen = readlen; + ssize_t data_readlen; rv = nghttp2_session_update_recv_connection_window_size (session, readlen); if(nghttp2_is_fatal(rv)) { @@ -3798,17 +3970,8 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, } } } - if(nghttp2_frame_data_trail_padlen(&iframe->frame.data) > - iframe->payloadleft) { - size_t trail_padlen; - trail_padlen = nghttp2_frame_data_trail_padlen(&iframe->frame.data) - - iframe->payloadleft; - if(readlen < trail_padlen) { - data_readlen = 0; - } else { - data_readlen -= trail_padlen; - } - } + data_readlen = inbound_frame_effective_readlen + (iframe, iframe->payloadleft, readlen); DEBUGF(fprintf(stderr, "data_readlen=%zu\n", data_readlen)); if(data_readlen > 0 && session->callbacks.on_data_chunk_recv_callback) { rv = session->callbacks.on_data_chunk_recv_callback @@ -4066,11 +4229,11 @@ ssize_t nghttp2_session_pack_data(nghttp2_session *session, frame->hd.flags &= ~(NGHTTP2_FLAG_PAD_HIGH | NGHTTP2_FLAG_PAD_LOW); flags = 0; - if((session->opt_flags & NGHTTP2_OPTMASK_NO_DATA_PADDING) == 0 && + if(session->pad_alignment && payloadlen > 0 && (size_t)payloadlen < datamax) { rv = nghttp2_frame_add_pad(buf_ptr, buflen_ptr, bufoff_ptr, &flags, payloadlen, datamax, - session->data_pad_alignment); + session->pad_alignment); if(rv < 0) { return rv; } diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h index 9e4fb484..a205cb60 100644 --- a/lib/nghttp2_session.h +++ b/lib/nghttp2_session.h @@ -44,10 +44,7 @@ */ typedef enum { NGHTTP2_OPTMASK_NO_AUTO_STREAM_WINDOW_UPDATE = 1 << 0, - NGHTTP2_OPTMASK_NO_AUTO_CONNECTION_WINDOW_UPDATE = 1 << 1, - /* Option to disable DATA frame padding, which is currently hidden - from outside, but provided for ease of testing */ - NGHTTP2_OPTMASK_NO_DATA_PADDING = 1 << 2, + NGHTTP2_OPTMASK_NO_AUTO_CONNECTION_WINDOW_UPDATE = 1 << 1 } nghttp2_optmask; typedef struct { @@ -89,6 +86,8 @@ typedef enum { NGHTTP2_IB_READ_GOAWAY_DEBUG, NGHTTP2_IB_EXPECT_CONTINUATION, NGHTTP2_IB_IGN_CONTINUATION, + NGHTTP2_IB_READ_PAD_CONTINUATION, + NGHTTP2_IB_IGN_PAD_CONTINUATION, NGHTTP2_IB_READ_DATA, NGHTTP2_IB_IGN_DATA } nghttp2_inbound_state; @@ -105,6 +104,8 @@ typedef struct { size_t left; /* How many bytes we still need to receive for current frame */ size_t payloadleft; + /* padding length for the current frame */ + size_t padlen; nghttp2_inbound_state state; /* TODO, remove this. Error code */ int error_code; @@ -152,8 +153,8 @@ struct nghttp2_session { size_t num_incoming_streams; /* The number of bytes allocated for nvbuf */ size_t nvbuflen; - /* DATA padding alignemnt. See NGHTTP2_OPT_DATA_PAD_ALIGNMENT. */ - size_t data_pad_alignment; + /* padding alignemnt. See NGHTTP2_OPT_PAD_ALIGNMENT. */ + size_t pad_alignment; /* Next Stream ID. Made unsigned int to detect >= (1 << 31). */ uint32_t next_stream_id; /* The largest stream ID received so far */ diff --git a/src/HttpServer.cc b/src/HttpServer.cc index 6e7374e0..df09f4e4 100644 --- a/src/HttpServer.cc +++ b/src/HttpServer.cc @@ -66,7 +66,7 @@ const std::string NGHTTPD_SERVER = "nghttpd nghttp2/" NGHTTP2_VERSION; Config::Config() : data_ptr(nullptr), output_upper_thres(1024*1024), - data_pad_alignment(NGHTTP2_DATA_PAD_ALIGNMENT), + pad_alignment(NGHTTP2_PAD_ALIGNMENT), header_table_size(-1), port(0), verbose(false), @@ -365,11 +365,11 @@ int Http2Handler::on_connect() nghttp2_opt_set opt_set; memset(&opt_set, 0, sizeof(opt_set)); - opt_set.data_pad_alignment = sessions_->get_config()->data_pad_alignment; + opt_set.pad_alignment = sessions_->get_config()->pad_alignment; fill_callback(callbacks, sessions_->get_config()); r = nghttp2_session_server_new2(&session_, &callbacks, this, - NGHTTP2_OPT_DATA_PAD_ALIGNMENT, &opt_set); + NGHTTP2_OPT_PAD_ALIGNMENT, &opt_set); if(r != 0) { return r; } diff --git a/src/HttpServer.h b/src/HttpServer.h index aeb6f43d..ad5feefa 100644 --- a/src/HttpServer.h +++ b/src/HttpServer.h @@ -56,7 +56,7 @@ struct Config { std::string cert_file; void *data_ptr; size_t output_upper_thres; - size_t data_pad_alignment; + size_t pad_alignment; ssize_t header_table_size; uint16_t port; bool verbose; diff --git a/src/app_helper.cc b/src/app_helper.cc index 46a231ba..ed19172e 100644 --- a/src/app_helper.cc +++ b/src/app_helper.cc @@ -244,6 +244,18 @@ void print_flags(const nghttp2_frame_hd& hd) } s += "PRIORITY"; } + if(hd.flags & NGHTTP2_FLAG_PAD_LOW) { + if(!s.empty()) { + s += " | "; + } + s += "PAD_LOW"; + } + if(hd.flags & NGHTTP2_FLAG_PAD_HIGH) { + if(!s.empty()) { + s += " | "; + } + s += "PAD_HIGH"; + } break; case NGHTTP2_SETTINGS: if(hd.flags & NGHTTP2_FLAG_ACK) { @@ -254,6 +266,18 @@ void print_flags(const nghttp2_frame_hd& hd) if(hd.flags & NGHTTP2_FLAG_END_PUSH_PROMISE) { s += "END_PUSH_PROMISE"; } + if(hd.flags & NGHTTP2_FLAG_PAD_LOW) { + if(!s.empty()) { + s += " | "; + } + s += "PAD_LOW"; + } + if(hd.flags & NGHTTP2_FLAG_PAD_HIGH) { + if(!s.empty()) { + s += " | "; + } + s += "PAD_HIGH"; + } break; case NGHTTP2_PING: if(hd.flags & NGHTTP2_FLAG_ACK) { @@ -297,10 +321,9 @@ void print_frame(print_type ptype, const nghttp2_frame *frame) } break; case NGHTTP2_HEADERS: - if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { - print_frame_attr_indent(); - printf("(pri=%d)\n", frame->headers.pri); - } + print_frame_attr_indent(); + printf("(pri=%d, padlen=%zu)\n", + frame->headers.pri, frame->headers.padlen); switch(frame->headers.cat) { case NGHTTP2_HCAT_REQUEST: print_frame_attr_indent(); @@ -342,8 +365,9 @@ void print_frame(print_type ptype, const nghttp2_frame *frame) break; case NGHTTP2_PUSH_PROMISE: print_frame_attr_indent(); - printf("(promised_stream_id=%d)\n", - frame->push_promise.promised_stream_id); + printf("(promised_stream_id=%d, padlen=%zu)\n", + frame->push_promise.promised_stream_id, + frame->push_promise.padlen); print_nv(frame->push_promise.nva, frame->push_promise.nvlen); break; case NGHTTP2_PING: diff --git a/src/nghttp.cc b/src/nghttp.cc index 8ee76756..27837404 100644 --- a/src/nghttp.cc +++ b/src/nghttp.cc @@ -82,7 +82,7 @@ struct Config { std::string keyfile; std::string datafile; size_t output_upper_thres; - size_t data_pad_alignment; + size_t pad_alignment; ssize_t peer_max_concurrent_streams; ssize_t header_table_size; int32_t pri; @@ -100,7 +100,7 @@ struct Config { bool continuation; Config() : output_upper_thres(1024*1024), - data_pad_alignment(NGHTTP2_DATA_PAD_ALIGNMENT), + pad_alignment(NGHTTP2_PAD_ALIGNMENT), peer_max_concurrent_streams(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS), header_table_size(-1), pri(NGHTTP2_PRI_DEFAULT), @@ -716,10 +716,10 @@ struct HttpClient { } nghttp2_opt_set opt_set; opt_set.peer_max_concurrent_streams = config.peer_max_concurrent_streams; - opt_set.data_pad_alignment = config.data_pad_alignment; + opt_set.pad_alignment = config.pad_alignment; rv = nghttp2_session_client_new2(&session, callbacks, this, NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS | - NGHTTP2_OPT_DATA_PAD_ALIGNMENT, + NGHTTP2_OPT_PAD_ALIGNMENT, &opt_set); if(rv != 0) { return -1; @@ -1710,8 +1710,8 @@ void print_help(std::ostream& out) << " is large enough as it is seen as unlimited.\n" << " -c, --header-table-size=\n" << " Specify decoder header table size.\n" - << " -b, --data-pad=\n" - << " Alignment of DATA frame padding.\n" + << " -b, --pad=\n" + << " Alignment of frame payload padding.\n" << " --color Force colored log output.\n" << " --continuation Send large header to test CONTINUATION.\n" << std::endl; @@ -1766,7 +1766,7 @@ int main(int argc, char **argv) print_help(std::cout); exit(EXIT_SUCCESS); case 'b': - config.data_pad_alignment = strtol(optarg, nullptr, 10); + config.pad_alignment = strtol(optarg, nullptr, 10); break; case 'n': config.null_out = true; diff --git a/src/nghttpd.cc b/src/nghttpd.cc index 87db6143..741475d0 100644 --- a/src/nghttpd.cc +++ b/src/nghttpd.cc @@ -115,8 +115,8 @@ void print_help(std::ostream& out) << " -p/=/foo.png -p/doc=/bar.css\n" << " PATH and PUSH_PATHs are relative to document\n" << " root. See --htdocs option.\n" - << " -b, --data-pad=\n" - << " Alignment of DATA frame padding.\n" + << " -b, --pad=\n" + << " Alignment of frame payload padding.\n" << " -h, --help Print this help.\n" << std::endl; } @@ -155,7 +155,7 @@ int main(int argc, char **argv) config.verify_client = true; break; case 'b': - config.data_pad_alignment = strtol(optarg, nullptr, 10); + config.pad_alignment = strtol(optarg, nullptr, 10); break; case 'd': config.htdocs = optarg; diff --git a/tests/nghttp2_frame_test.c b/tests/nghttp2_frame_test.c index 74d01563..2188d208 100644 --- a/tests/nghttp2_frame_test.c +++ b/tests/nghttp2_frame_test.c @@ -74,6 +74,7 @@ void test_nghttp2_frame_pack_headers() nghttp2_headers frame, oframe; uint8_t *buf = NULL; size_t buflen = 0; + size_t bufoff; ssize_t framelen; nghttp2_nv *nva; ssize_t nvlen; @@ -89,17 +90,21 @@ void test_nghttp2_frame_pack_headers() NGHTTP2_FLAG_END_STREAM|NGHTTP2_FLAG_END_HEADERS, 1000000007, 1 << 20, nva, nvlen); - framelen = nghttp2_frame_pack_headers(&buf, &buflen, &frame, &deflater); + framelen = nghttp2_frame_pack_headers(&buf, &buflen, &bufoff, &frame, + &deflater, 0); - CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf, framelen)); - check_frame_header(framelen - NGHTTP2_FRAME_HEAD_LENGTH, NGHTTP2_HEADERS, + CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf + bufoff, + framelen - bufoff)); + check_frame_header(framelen - bufoff - NGHTTP2_FRAME_HEAD_LENGTH, + NGHTTP2_HEADERS, NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS, 1000000007, &oframe.hd); /* We didn't include PRIORITY flag so priority is not packed */ CU_ASSERT(1 << 30 == oframe.pri); - CU_ASSERT(framelen - 8 == - inflate_hd(&inflater, &out, buf + 8, framelen - 8)); + CU_ASSERT(framelen - (ssize_t)bufoff - 8 == + inflate_hd(&inflater, &out, + buf + bufoff + 8, framelen - bufoff - 8)); CU_ASSERT(7 == out.nvlen); CU_ASSERT(nvnameeq("method", &out.nva[0])); @@ -111,10 +116,13 @@ void test_nghttp2_frame_pack_headers() memset(&oframe, 0, sizeof(oframe)); /* Next, include PRIORITY flag */ frame.hd.flags |= NGHTTP2_FLAG_PRIORITY; - framelen = nghttp2_frame_pack_headers(&buf, &buflen, &frame, &deflater); + framelen = nghttp2_frame_pack_headers(&buf, &buflen, &bufoff, &frame, + &deflater, 0); - CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf, framelen)); - check_frame_header(framelen - NGHTTP2_FRAME_HEAD_LENGTH, NGHTTP2_HEADERS, + CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf + bufoff, + framelen - bufoff)); + check_frame_header(framelen - bufoff - NGHTTP2_FRAME_HEAD_LENGTH, + NGHTTP2_HEADERS, NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, 1000000007, &oframe.hd); @@ -140,6 +148,7 @@ void test_nghttp2_frame_pack_headers_frame_too_large(void) nghttp2_headers frame; uint8_t *buf = NULL; size_t buflen = 0; + size_t bufoff; ssize_t framelen; nghttp2_nv *nva; ssize_t nvlen; @@ -163,7 +172,8 @@ void test_nghttp2_frame_pack_headers_frame_too_large(void) NGHTTP2_FLAG_END_STREAM|NGHTTP2_FLAG_END_HEADERS, 1000000007, 0, nva, nvlen); - framelen = nghttp2_frame_pack_headers(&buf, &buflen, &frame, &deflater); + framelen = nghttp2_frame_pack_headers(&buf, &buflen, &bufoff, &frame, + &deflater, 0); CU_ASSERT_EQUAL(NGHTTP2_ERR_HEADER_COMP, framelen); nghttp2_frame_headers_free(&frame); diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c index 98df0836..ff3e699b 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -336,9 +336,10 @@ void test_nghttp2_session_recv(void) }; uint8_t *framedata = NULL; size_t framedatalen = 0; + size_t bufoff; ssize_t framelen; nghttp2_frame frame; - int i; + size_t i; nghttp2_outbound_item *item; nghttp2_nv *nva; ssize_t nvlen; @@ -355,13 +356,13 @@ void test_nghttp2_session_recv(void) nvlen = nghttp2_nv_array_copy(&nva, nv, ARRLEN(nv)); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, NGHTTP2_PRI_DEFAULT, nva, nvlen); - framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, + framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, &bufoff, &frame.headers, - &deflater); + &deflater, 0); - scripted_data_feed_init(&df, framedata, framelen); + scripted_data_feed_init(&df, framedata + bufoff, framelen - bufoff); /* Send 1 byte per each read */ - for(i = 0; i < framelen; ++i) { + for(i = 0; i < framelen - bufoff; ++i) { df.feedseq[i] = 1; } nghttp2_frame_headers_free(&frame.headers); @@ -375,13 +376,13 @@ void test_nghttp2_session_recv(void) /* Received HEADERS without header block, which is valid */ nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 5, NGHTTP2_PRI_DEFAULT, NULL, 0); - framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, + framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, &bufoff, &frame.headers, - &deflater); + &deflater, 0); nghttp2_frame_headers_free(&frame.headers); - scripted_data_feed_init(&df, framedata, framelen); + scripted_data_feed_init(&df, framedata + bufoff, framelen - bufoff); user_data.frame_recv_cb_called = 0; CU_ASSERT(0 == nghttp2_session_recv(session)); CU_ASSERT(1 == user_data.frame_recv_cb_called); @@ -421,6 +422,7 @@ void test_nghttp2_session_recv_invalid_stream_id(void) my_user_data user_data; uint8_t *framedata = NULL; size_t framedatalen = 0; + size_t bufoff; ssize_t framelen; nghttp2_frame frame; nghttp2_hd_deflater deflater; @@ -436,11 +438,11 @@ void test_nghttp2_session_recv_invalid_stream_id(void) nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, NGHTTP2_PRI_DEFAULT, NULL, 0); - framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, + framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, &bufoff, &frame.headers, - &deflater); + &deflater, 0); - scripted_data_feed_init(&df, framedata, framelen); + scripted_data_feed_init(&df, framedata + bufoff, framelen - bufoff); nghttp2_frame_headers_free(&frame.headers); CU_ASSERT(0 == nghttp2_session_recv(session)); @@ -462,6 +464,7 @@ void test_nghttp2_session_recv_invalid_frame(void) }; uint8_t *framedata = NULL; size_t framedatalen = 0; + size_t bufoff; ssize_t framelen; nghttp2_frame frame; nghttp2_nv *nva; @@ -480,11 +483,11 @@ void test_nghttp2_session_recv_invalid_frame(void) nvlen = nghttp2_nv_array_copy(&nva, nv, ARRLEN(nv)); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, NGHTTP2_PRI_DEFAULT, nva, nvlen); - framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, + framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, &bufoff, &frame.headers, - &deflater); + &deflater, 0); - scripted_data_feed_init(&df, framedata, framelen); + scripted_data_feed_init(&df, framedata + bufoff, framelen - bufoff); CU_ASSERT(0 == nghttp2_session_recv(session)); CU_ASSERT(0 == nghttp2_session_send(session)); @@ -492,7 +495,7 @@ void test_nghttp2_session_recv_invalid_frame(void) /* Receive exactly same bytes of HEADERS is treated as subsequent HEADERS (e.g., trailers */ - scripted_data_feed_init(&df, framedata, framelen); + scripted_data_feed_init(&df, framedata + bufoff, framelen - bufoff); CU_ASSERT(0 == nghttp2_session_recv(session)); CU_ASSERT(0 == nghttp2_session_send(session)); @@ -660,6 +663,7 @@ void test_nghttp2_session_recv_continuation(void) uint8_t *framedata = NULL; size_t framedatacap = 0; size_t framedatalen; + size_t bufoff; size_t framedataoff; ssize_t rv; my_user_data ud; @@ -681,13 +685,14 @@ void test_nghttp2_session_recv_continuation(void) nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE, 1, NGHTTP2_PRI_DEFAULT, nva, nvlen); framedatalen = nghttp2_frame_pack_headers(&framedata, &framedatacap, + &bufoff, &frame.headers, - &deflater); + &deflater, 0); nghttp2_frame_headers_free(&frame.headers); - memcpy(data, framedata, 9); + memcpy(data, framedata + bufoff, 9); datalen = 9; - framedataoff = NGHTTP2_FRAME_HEAD_LENGTH + 1; + framedataoff = bufoff + NGHTTP2_FRAME_HEAD_LENGTH + 1; nghttp2_put_uint16be(data, 1); @@ -735,12 +740,12 @@ void test_nghttp2_session_recv_continuation(void) nvlen = nghttp2_nv_array_copy(&nva, nv1, ARRLEN(nv1)); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE, 1, NGHTTP2_PRI_DEFAULT, nva, nvlen); - framedatalen = nghttp2_frame_pack_headers(&framedata, &framedatacap, + framedatalen = nghttp2_frame_pack_headers(&framedata, &framedatacap, &bufoff, &frame.headers, - &deflater); + &deflater, 0); nghttp2_frame_headers_free(&frame.headers); - memcpy(data, framedata, framedatalen); - datalen = framedatalen; + memcpy(data, framedata + bufoff, framedatalen - bufoff); + datalen = framedatalen - bufoff; /* Followed by PRIORITY */ nghttp2_frame_priority_init(&frame.priority, 1, 0); @@ -782,6 +787,7 @@ void test_nghttp2_session_continue(void) uint8_t buffer[4096]; uint8_t *bufp = buffer; size_t buflen; + size_t bufoff; nghttp2_frame frame; nghttp2_nv *nva; ssize_t nvlen; @@ -804,22 +810,24 @@ void test_nghttp2_session_continue(void) nvlen = nghttp2_nv_array_copy(&nva, nv1, ARRLEN(nv1)); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, NGHTTP2_PRI_DEFAULT, nva, nvlen); - framelen1 = nghttp2_frame_pack_headers(&framedata, &framedatalen, + framelen1 = nghttp2_frame_pack_headers(&framedata, &framedatalen, &bufoff, &frame.headers, - &deflater); + &deflater, 0); nghttp2_frame_headers_free(&frame.headers); - memcpy(buffer, framedata, framelen1); + memcpy(buffer, framedata + bufoff, framelen1 - bufoff); + framelen1 -= bufoff; nvlen = nghttp2_nv_array_copy(&nva, nv2, ARRLEN(nv2)); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, NGHTTP2_PRI_DEFAULT, nva, nvlen); - framelen2 = nghttp2_frame_pack_headers(&framedata, &framedatalen, + framelen2 = nghttp2_frame_pack_headers(&framedata, &framedatalen, &bufoff, &frame.headers, - &deflater); + &deflater, 0); nghttp2_frame_headers_free(&frame.headers); - memcpy(buffer + framelen1, framedata, framelen2); + memcpy(buffer + framelen1, framedata + bufoff, framelen2 - bufoff); + framelen2 -= bufoff; buflen = framelen1 + framelen2; /* Receive 1st HEADERS and pause */ @@ -955,6 +963,7 @@ void test_nghttp2_session_add_frame(void) acc.length = 0; user_data.acc = &acc; CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &user_data)); + session_disable_pad(session); frame = malloc(sizeof(nghttp2_frame)); nvlen = nghttp2_nv_array_copy(&nva, nv, ARRLEN(nv)); @@ -2173,6 +2182,7 @@ void test_nghttp2_submit_request_without_data(void) memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback = accumulator_send_callback; CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + session_disable_pad(session); nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST); CU_ASSERT(0 == nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT, nva, ARRLEN(nva), &data_prd, NULL)); @@ -2244,6 +2254,7 @@ void test_nghttp2_submit_response_without_data(void) memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback = accumulator_send_callback; CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + session_disable_pad(session); nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_RESPONSE); nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_END_STREAM, NGHTTP2_PRI_DEFAULT, @@ -2415,6 +2426,7 @@ void test_nghttp2_submit_headers(void) callbacks.on_frame_send_callback = on_frame_send_callback; CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + session_disable_pad(session); nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST); CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, @@ -3858,7 +3870,7 @@ void test_nghttp2_session_pack_data_with_padding(void) data_prd.read_callback = fixed_length_data_source_read_callback; nghttp2_session_client_new(&session, &callbacks, &ud); - session->data_pad_alignment = 512; + session->pad_alignment = 512; nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT, NULL, 0, &data_prd, NULL); @@ -3869,7 +3881,7 @@ void test_nghttp2_session_pack_data_with_padding(void) CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); frame = OB_DATA(session->aob.item); - CU_ASSERT(session->data_pad_alignment - datalen == frame->padlen); + CU_ASSERT(session->pad_alignment - datalen == frame->padlen); CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PAD_LOW); CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PAD_HIGH); @@ -3893,7 +3905,7 @@ void test_nghttp2_session_pack_data_with_padding(void) CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); frame = OB_DATA(session->aob.item); - CU_ASSERT(session->data_pad_alignment - datalen == frame->padlen); + CU_ASSERT(session->pad_alignment - datalen == frame->padlen); CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PAD_LOW); CU_ASSERT(0 == (frame->hd.flags & NGHTTP2_FLAG_PAD_HIGH)); diff --git a/tests/nghttp2_test_helper.c b/tests/nghttp2_test_helper.c index aec305bd..ef40e4ac 100644 --- a/tests/nghttp2_test_helper.c +++ b/tests/nghttp2_test_helper.c @@ -159,3 +159,8 @@ ssize_t inflate_hd(nghttp2_hd_inflater *inflater, nva_out *out, nghttp2_hd_inflate_end_headers(inflater); return initial - buflen; } + +void session_disable_pad(nghttp2_session *session) +{ + session->pad_alignment = 0; +} diff --git a/tests/nghttp2_test_helper.h b/tests/nghttp2_test_helper.h index 4ad2a672..e881bf1f 100644 --- a/tests/nghttp2_test_helper.h +++ b/tests/nghttp2_test_helper.h @@ -29,6 +29,7 @@ # include #endif /* HAVE_CONFIG_H */ +#include "nghttp2_session.h" #include "nghttp2_frame.h" #include "nghttp2_hd.h" @@ -57,4 +58,6 @@ void add_out(nva_out *out, nghttp2_nv *nv); ssize_t inflate_hd(nghttp2_hd_inflater *inflater, nva_out *out, uint8_t *buf, size_t buflen); +void session_disable_pad(nghttp2_session *session); + #endif /* NGHTTP2_TEST_HELPER_H */