diff --git a/lib/nghttp2_frame.c b/lib/nghttp2_frame.c index 7794df70..81c760b9 100644 --- a/lib/nghttp2_frame.c +++ b/lib/nghttp2_frame.c @@ -562,13 +562,44 @@ int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame) void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, const uint8_t *payload, - size_t payloadlen) + size_t payloadlen, + uint8_t *var_gift_payload, + size_t var_gift_payloadlen) { frame->last_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; frame->error_code = nghttp2_get_uint32(payload+4); - /* TODO Currently we don't buffer debug data */ - frame->opaque_data = NULL; - frame->opaque_data_len = 0; + + frame->opaque_data = var_gift_payload; + frame->opaque_data_len = var_gift_payloadlen; +} + +int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, + const uint8_t *payload, + size_t payloadlen) +{ + uint8_t *var_gift_payload; + size_t var_gift_payloadlen; + + if(payloadlen > 8) { + var_gift_payloadlen = payloadlen - 8; + } else { + var_gift_payloadlen = 0; + } + + payloadlen -= var_gift_payloadlen; + + var_gift_payload = malloc(var_gift_payloadlen); + + if(var_gift_payload == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + memcpy(var_gift_payload, payload + 8, var_gift_payloadlen); + + nghttp2_frame_unpack_goaway_payload(frame, payload, payloadlen, + var_gift_payload, var_gift_payloadlen); + + return 0; } int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, diff --git a/lib/nghttp2_frame.h b/lib/nghttp2_frame.h index 983cad8f..04321720 100644 --- a/lib/nghttp2_frame.h +++ b/lib/nghttp2_frame.h @@ -330,9 +330,31 @@ void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame); /* - * Unpacks GOAWAY wire format into |frame|. + * Unpacks GOAWAY wire format into |frame|. The |payload| of length + * |payloadlen| contains first 8 bytes of payload. The + * |var_gift_payload| of length |var_gift_payloadlen| contains + * remaining payload and its buffer is gifted to the function and then + * |frame|. The |var_gift_payloadlen| must be freed by + * nghttp2_frame_goaway_free(). */ void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, + const uint8_t *payload, + size_t payloadlen, + uint8_t *var_gift_payload, + size_t var_gift_payloadlen); + +/* + * Unpacks GOAWAY wire format into |frame|. This function only exists + * for unit test. After allocating buffer for debug data, this + * function internally calls nghttp2_frame_unpack_goaway_payload(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, const uint8_t *payload, size_t payloadlen); diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index cd8fce9f..6f81466c 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -3099,7 +3099,11 @@ static int session_process_goaway_frame(nghttp2_session *session) nghttp2_frame_unpack_goaway_payload(&frame->goaway, iframe->sbuf.pos, - nghttp2_buf_len(&iframe->sbuf)); + nghttp2_buf_len(&iframe->sbuf), + iframe->lbuf.pos, + nghttp2_buf_len(&iframe->lbuf)); + + nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); return nghttp2_session_on_goaway_received(session, frame); } @@ -4009,12 +4013,28 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, nghttp2_inbound_frame_reset(session); break; - case NGHTTP2_GOAWAY: + case NGHTTP2_GOAWAY: { + size_t debuglen; + + /* 8 is Last-stream-ID + Error Code */ + debuglen = iframe->frame.hd.length - 8; + + if(debuglen > 0) { + iframe->raw_lbuf = malloc(debuglen); + + if(iframe->raw_lbuf == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, debuglen); + } + busy = 1; iframe->state = NGHTTP2_IB_READ_GOAWAY_DEBUG; break; + } case NGHTTP2_WINDOW_UPDATE: rv = session_process_window_update_frame(session); if(nghttp2_is_fatal(rv)) { @@ -4224,6 +4244,9 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, DEBUGF(fprintf(stderr, "recv: [IB_READ_GOAWAY_DEBUG]\n")); readlen = inbound_frame_payload_readlen(iframe, in, last); + + iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen); + iframe->payloadleft -= readlen; in += readlen; @@ -4231,6 +4254,8 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, readlen, iframe->payloadleft)); if(iframe->payloadleft) { + assert(nghttp2_buf_avail(&iframe->lbuf) > 0); + break; } diff --git a/tests/nghttp2_frame_test.c b/tests/nghttp2_frame_test.c index bf294b5e..476268e8 100644 --- a/tests/nghttp2_frame_test.c +++ b/tests/nghttp2_frame_test.c @@ -373,11 +373,9 @@ void test_nghttp2_frame_pack_goaway() check_frame_header(24, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0, &oframe.hd); CU_ASSERT(1000000007 == oframe.last_stream_id); CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == oframe.error_code); - /* TODO Currently, opaque data is discarded */ - CU_ASSERT(0 == oframe.opaque_data_len); - CU_ASSERT(NULL == oframe.opaque_data); - /* CU_ASSERT(opaque_data_len == oframe.opaque_data_len); */ - /* CU_ASSERT(memcmp(opaque_data, oframe.opaque_data, opaque_data_len) == 0); */ + + CU_ASSERT(opaque_data_len == oframe.opaque_data_len); + CU_ASSERT(memcmp(opaque_data, oframe.opaque_data, opaque_data_len) == 0); nghttp2_bufs_free(&bufs); nghttp2_frame_goaway_free(&oframe); diff --git a/tests/nghttp2_test_helper.c b/tests/nghttp2_test_helper.c index 006b210e..fee34ad1 100644 --- a/tests/nghttp2_test_helper.c +++ b/tests/nghttp2_test_helper.c @@ -77,7 +77,7 @@ int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len) (&frame->ping, payload, payloadlen); break; case NGHTTP2_GOAWAY: - nghttp2_frame_unpack_goaway_payload + nghttp2_frame_unpack_goaway_payload2 (&frame->goaway, payload, payloadlen); break; case NGHTTP2_WINDOW_UPDATE: