From 9d4e8eeb122ab03de4898c2ab727f503163544ed Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sun, 11 Sep 2016 16:55:15 +0900 Subject: [PATCH] Simplify code Move DATA frame handling code to switch-case of frame type. --- lib/nghttp2_session.c | 694 ++++++++++++++++++++---------------------- 1 file changed, 329 insertions(+), 365 deletions(-) diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index c401aabb..adce16af 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -816,99 +816,96 @@ int nghttp2_session_add_item(nghttp2_session *session, frame = &item->frame; stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (frame->hd.type != NGHTTP2_DATA) { - - switch (frame->hd.type) { - case NGHTTP2_HEADERS: - /* We push request HEADERS and push response HEADERS to - dedicated queue because their transmission is affected by - SETTINGS_MAX_CONCURRENT_STREAMS */ - /* TODO If 2 HEADERS are submitted for reserved stream, then - both of them are queued into ob_syn, which is not - desirable. */ - if (frame->headers.cat == NGHTTP2_HCAT_REQUEST || - (stream && stream->state == NGHTTP2_STREAM_RESERVED)) { - nghttp2_outbound_queue_push(&session->ob_syn, item); - item->queued = 1; - break; - } - - nghttp2_outbound_queue_push(&session->ob_reg, item); - item->queued = 1; - break; - case NGHTTP2_SETTINGS: - case NGHTTP2_PING: - nghttp2_outbound_queue_push(&session->ob_urgent, item); - item->queued = 1; - break; - case NGHTTP2_RST_STREAM: - if (stream) { - stream->state = NGHTTP2_STREAM_CLOSING; - } - nghttp2_outbound_queue_push(&session->ob_reg, item); - item->queued = 1; - break; - case NGHTTP2_PUSH_PROMISE: { - nghttp2_headers_aux_data *aux_data; - nghttp2_priority_spec pri_spec; - - aux_data = &item->aux_data.headers; - - if (!stream) { - return NGHTTP2_ERR_STREAM_CLOSED; - } - - nghttp2_priority_spec_init(&pri_spec, stream->stream_id, - NGHTTP2_DEFAULT_WEIGHT, 0); - - if (!nghttp2_session_open_stream( - session, frame->push_promise.promised_stream_id, - NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_RESERVED, - aux_data->stream_user_data)) { - return NGHTTP2_ERR_NOMEM; - } - - /* We don't have to call nghttp2_session_adjust_closed_stream() - here, since stream->stream_id is local stream_id, and it does - not affect closed stream count. */ - - nghttp2_outbound_queue_push(&session->ob_reg, item); - item->queued = 1; - - break; + switch (frame->hd.type) { + case NGHTTP2_DATA: + if (!stream) { + return NGHTTP2_ERR_STREAM_CLOSED; } - case NGHTTP2_WINDOW_UPDATE: - if (stream) { - stream->window_update_queued = 1; - } else if (frame->hd.stream_id == 0) { - session->window_update_queued = 1; - } - nghttp2_outbound_queue_push(&session->ob_reg, item); - item->queued = 1; - break; - default: - nghttp2_outbound_queue_push(&session->ob_reg, item); - item->queued = 1; + + if (stream->item) { + return NGHTTP2_ERR_DATA_EXIST; + } + + rv = nghttp2_stream_attach_item(stream, item); + + if (rv != 0) { + return rv; } + return 0; + case NGHTTP2_HEADERS: + /* We push request HEADERS and push response HEADERS to + dedicated queue because their transmission is affected by + SETTINGS_MAX_CONCURRENT_STREAMS */ + /* TODO If 2 HEADERS are submitted for reserved stream, then + both of them are queued into ob_syn, which is not + desirable. */ + if (frame->headers.cat == NGHTTP2_HCAT_REQUEST || + (stream && stream->state == NGHTTP2_STREAM_RESERVED)) { + nghttp2_outbound_queue_push(&session->ob_syn, item); + item->queued = 1; + return 0; + ; + } + + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + return 0; + case NGHTTP2_SETTINGS: + case NGHTTP2_PING: + nghttp2_outbound_queue_push(&session->ob_urgent, item); + item->queued = 1; + return 0; + case NGHTTP2_RST_STREAM: + if (stream) { + stream->state = NGHTTP2_STREAM_CLOSING; + } + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + return 0; + case NGHTTP2_PUSH_PROMISE: { + nghttp2_headers_aux_data *aux_data; + nghttp2_priority_spec pri_spec; + + aux_data = &item->aux_data.headers; + + if (!stream) { + return NGHTTP2_ERR_STREAM_CLOSED; + } + + nghttp2_priority_spec_init(&pri_spec, stream->stream_id, + NGHTTP2_DEFAULT_WEIGHT, 0); + + if (!nghttp2_session_open_stream( + session, frame->push_promise.promised_stream_id, + NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_RESERVED, + aux_data->stream_user_data)) { + return NGHTTP2_ERR_NOMEM; + } + + /* We don't have to call nghttp2_session_adjust_closed_stream() + here, since stream->stream_id is local stream_id, and it does + not affect closed stream count. */ + + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + return 0; } - - if (!stream) { - return NGHTTP2_ERR_STREAM_CLOSED; + case NGHTTP2_WINDOW_UPDATE: + if (stream) { + stream->window_update_queued = 1; + } else if (frame->hd.stream_id == 0) { + session->window_update_queued = 1; + } + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + return 0; + default: + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + return 0; } - - if (stream->item) { - return NGHTTP2_ERR_DATA_EXIST; - } - - rv = nghttp2_stream_attach_item(stream, item); - - if (rv != 0) { - return rv; - } - - return 0; } int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, @@ -1941,276 +1938,8 @@ static int session_prep_frame(nghttp2_session *session, mem = &session->mem; frame = &item->frame; - if (frame->hd.type != NGHTTP2_DATA) { - switch (frame->hd.type) { - case NGHTTP2_HEADERS: { - nghttp2_headers_aux_data *aux_data; - size_t estimated_payloadlen; - - aux_data = &item->aux_data.headers; - - if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { - /* initial HEADERS, which opens stream */ - nghttp2_stream *stream; - - stream = nghttp2_session_open_stream( - session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, - &frame->headers.pri_spec, NGHTTP2_STREAM_INITIAL, - aux_data->stream_user_data); - - if (stream == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - /* We don't call nghttp2_session_adjust_closed_stream() here, - since we don't keep closed stream in client side */ - - estimated_payloadlen = session_estimate_headers_payload( - session, frame->headers.nva, frame->headers.nvlen, - NGHTTP2_PRIORITY_SPECLEN); - - if (estimated_payloadlen > session->max_send_header_block_length) { - return NGHTTP2_ERR_FRAME_SIZE_ERROR; - } - - rv = session_predicate_request_headers_send(session, item); - if (rv != 0) { - return rv; - } - - if (session_enforce_http_messaging(session)) { - nghttp2_http_record_request_method(stream, frame); - } - } else { - nghttp2_stream *stream; - - estimated_payloadlen = session_estimate_headers_payload( - session, frame->headers.nva, frame->headers.nvlen, - NGHTTP2_PRIORITY_SPECLEN); - - if (estimated_payloadlen > session->max_send_header_block_length) { - return NGHTTP2_ERR_FRAME_SIZE_ERROR; - } - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - - if (stream && stream->state == NGHTTP2_STREAM_RESERVED) { - rv = session_predicate_push_response_headers_send(session, stream); - if (rv == 0) { - frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; - - if (aux_data->stream_user_data) { - stream->stream_user_data = aux_data->stream_user_data; - } - } - } else if (session_predicate_response_headers_send(session, stream) == - 0) { - frame->headers.cat = NGHTTP2_HCAT_RESPONSE; - rv = 0; - } else { - frame->headers.cat = NGHTTP2_HCAT_HEADERS; - - rv = session_predicate_headers_send(session, stream); - } - - if (rv != 0) { - // If stream was already closed, nghttp2_session_get_stream() - // returns NULL, but item is still attached to the stream. - // Search stream including closed again. - stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); - if (stream && stream->item == item) { - int rv2; - - rv2 = nghttp2_stream_detach_item(stream); - - if (nghttp2_is_fatal(rv2)) { - return rv2; - } - } - - return rv; - } - } - - rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers, - &session->hd_deflater); - - if (rv != 0) { - return rv; - } - - DEBUGF(fprintf(stderr, - "send: before padding, HEADERS serialized in %zd bytes\n", - nghttp2_bufs_len(&session->aob.framebufs))); - - rv = session_headers_add_pad(session, frame); - - if (rv != 0) { - return rv; - } - - DEBUGF(fprintf(stderr, "send: HEADERS finally serialized in %zd bytes\n", - nghttp2_bufs_len(&session->aob.framebufs))); - - if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { - assert(session->last_sent_stream_id < frame->hd.stream_id); - session->last_sent_stream_id = frame->hd.stream_id; - } - - break; - } - case NGHTTP2_PRIORITY: { - if (session_is_closing(session)) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - /* PRIORITY frame can be sent at any time and to any stream - ID. */ - nghttp2_frame_pack_priority(&session->aob.framebufs, &frame->priority); - - /* Peer can send PRIORITY frame against idle stream to create - "anchor" in dependency tree. Only client can do this in - nghttp2. In nghttp2, only server retains non-active (closed - or idle) streams in memory, so we don't open stream here. */ - break; - } - case NGHTTP2_RST_STREAM: - if (session_is_closing(session)) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - nghttp2_frame_pack_rst_stream(&session->aob.framebufs, - &frame->rst_stream); - break; - case NGHTTP2_SETTINGS: { - if (frame->hd.flags & NGHTTP2_FLAG_ACK) { - assert(session->obq_flood_counter_ > 0); - --session->obq_flood_counter_; - /* When session is about to close, don't send SETTINGS ACK. - We are required to send SETTINGS without ACK though; for - example, we have to send SETTINGS as a part of connection - preface. */ - if (session_is_closing(session)) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - } - - rv = nghttp2_frame_pack_settings(&session->aob.framebufs, - &frame->settings); - if (rv != 0) { - return rv; - } - break; - } - case NGHTTP2_PUSH_PROMISE: { - nghttp2_stream *stream; - size_t estimated_payloadlen; - - estimated_payloadlen = session_estimate_headers_payload( - session, frame->push_promise.nva, frame->push_promise.nvlen, 0); - - if (estimated_payloadlen > session->max_send_header_block_length) { - return NGHTTP2_ERR_FRAME_SIZE_ERROR; - } - - /* stream could be NULL if associated stream was already - closed. */ - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - - /* predicte should fail if stream is NULL. */ - rv = session_predicate_push_promise_send(session, stream); - if (rv != 0) { - return rv; - } - - assert(stream); - - rv = nghttp2_frame_pack_push_promise( - &session->aob.framebufs, &frame->push_promise, &session->hd_deflater); - if (rv != 0) { - return rv; - } - rv = session_headers_add_pad(session, frame); - if (rv != 0) { - return rv; - } - - assert(session->last_sent_stream_id + 2 <= - frame->push_promise.promised_stream_id); - session->last_sent_stream_id = frame->push_promise.promised_stream_id; - - break; - } - case NGHTTP2_PING: - if (frame->hd.flags & NGHTTP2_FLAG_ACK) { - assert(session->obq_flood_counter_ > 0); - --session->obq_flood_counter_; - } - - if (session_is_closing(session)) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping); - break; - case NGHTTP2_GOAWAY: - rv = nghttp2_frame_pack_goaway(&session->aob.framebufs, &frame->goaway); - if (rv != 0) { - return rv; - } - session->local_last_stream_id = frame->goaway.last_stream_id; - - break; - case NGHTTP2_WINDOW_UPDATE: - rv = session_predicate_window_update_send(session, frame->hd.stream_id); - if (rv != 0) { - return rv; - } - nghttp2_frame_pack_window_update(&session->aob.framebufs, - &frame->window_update); - break; - case NGHTTP2_CONTINUATION: - /* We never handle CONTINUATION here. */ - assert(0); - break; - default: { - nghttp2_ext_aux_data *aux_data; - - /* extension frame */ - - aux_data = &item->aux_data.ext; - - if (aux_data->builtin == 0) { - if (session_is_closing(session)) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - - rv = session_pack_extension(session, &session->aob.framebufs, frame); - if (rv != 0) { - return rv; - } - - break; - } - - switch (frame->hd.type) { - case NGHTTP2_ALTSVC: - rv = session_predicate_altsvc_send(session, frame->hd.stream_id); - if (rv != 0) { - return rv; - } - - nghttp2_frame_pack_altsvc(&session->aob.framebufs, &frame->ext); - - break; - default: - /* Unreachable here */ - assert(0); - break; - } - - break; - } - } - return 0; - } else { + switch (frame->hd.type) { + case NGHTTP2_DATA: { size_t next_readmax; nghttp2_stream *stream; @@ -2304,6 +2033,249 @@ static int session_prep_frame(nghttp2_session *session, } return 0; } + case NGHTTP2_HEADERS: { + nghttp2_headers_aux_data *aux_data; + size_t estimated_payloadlen; + + aux_data = &item->aux_data.headers; + + if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { + /* initial HEADERS, which opens stream */ + nghttp2_stream *stream; + + stream = nghttp2_session_open_stream( + session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, + &frame->headers.pri_spec, NGHTTP2_STREAM_INITIAL, + aux_data->stream_user_data); + + if (stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + /* We don't call nghttp2_session_adjust_closed_stream() here, + since we don't keep closed stream in client side */ + + estimated_payloadlen = session_estimate_headers_payload( + session, frame->headers.nva, frame->headers.nvlen, + NGHTTP2_PRIORITY_SPECLEN); + + if (estimated_payloadlen > session->max_send_header_block_length) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + rv = session_predicate_request_headers_send(session, item); + if (rv != 0) { + return rv; + } + + if (session_enforce_http_messaging(session)) { + nghttp2_http_record_request_method(stream, frame); + } + } else { + nghttp2_stream *stream; + + estimated_payloadlen = session_estimate_headers_payload( + session, frame->headers.nva, frame->headers.nvlen, + NGHTTP2_PRIORITY_SPECLEN); + + if (estimated_payloadlen > session->max_send_header_block_length) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + if (stream && stream->state == NGHTTP2_STREAM_RESERVED) { + rv = session_predicate_push_response_headers_send(session, stream); + if (rv == 0) { + frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; + + if (aux_data->stream_user_data) { + stream->stream_user_data = aux_data->stream_user_data; + } + } + } else if (session_predicate_response_headers_send(session, stream) == + 0) { + frame->headers.cat = NGHTTP2_HCAT_RESPONSE; + rv = 0; + } else { + frame->headers.cat = NGHTTP2_HCAT_HEADERS; + + rv = session_predicate_headers_send(session, stream); + } + + if (rv != 0) { + return rv; + } + } + + rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers, + &session->hd_deflater); + + if (rv != 0) { + return rv; + } + + DEBUGF(fprintf(stderr, + "send: before padding, HEADERS serialized in %zd bytes\n", + nghttp2_bufs_len(&session->aob.framebufs))); + + rv = session_headers_add_pad(session, frame); + + if (rv != 0) { + return rv; + } + + DEBUGF(fprintf(stderr, "send: HEADERS finally serialized in %zd bytes\n", + nghttp2_bufs_len(&session->aob.framebufs))); + + if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { + assert(session->last_sent_stream_id < frame->hd.stream_id); + session->last_sent_stream_id = frame->hd.stream_id; + } + + return 0; + } + case NGHTTP2_PRIORITY: { + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + /* PRIORITY frame can be sent at any time and to any stream + ID. */ + nghttp2_frame_pack_priority(&session->aob.framebufs, &frame->priority); + + /* Peer can send PRIORITY frame against idle stream to create + "anchor" in dependency tree. Only client can do this in + nghttp2. In nghttp2, only server retains non-active (closed + or idle) streams in memory, so we don't open stream here. */ + return 0; + } + case NGHTTP2_RST_STREAM: + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + nghttp2_frame_pack_rst_stream(&session->aob.framebufs, &frame->rst_stream); + return 0; + case NGHTTP2_SETTINGS: { + if (frame->hd.flags & NGHTTP2_FLAG_ACK) { + assert(session->obq_flood_counter_ > 0); + --session->obq_flood_counter_; + /* When session is about to close, don't send SETTINGS ACK. + We are required to send SETTINGS without ACK though; for + example, we have to send SETTINGS as a part of connection + preface. */ + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + } + + rv = nghttp2_frame_pack_settings(&session->aob.framebufs, &frame->settings); + if (rv != 0) { + return rv; + } + return 0; + } + case NGHTTP2_PUSH_PROMISE: { + nghttp2_stream *stream; + size_t estimated_payloadlen; + + estimated_payloadlen = session_estimate_headers_payload( + session, frame->push_promise.nva, frame->push_promise.nvlen, 0); + + if (estimated_payloadlen > session->max_send_header_block_length) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + /* stream could be NULL if associated stream was already + closed. */ + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + /* predicte should fail if stream is NULL. */ + rv = session_predicate_push_promise_send(session, stream); + if (rv != 0) { + return rv; + } + + assert(stream); + + rv = nghttp2_frame_pack_push_promise( + &session->aob.framebufs, &frame->push_promise, &session->hd_deflater); + if (rv != 0) { + return rv; + } + rv = session_headers_add_pad(session, frame); + if (rv != 0) { + return rv; + } + + assert(session->last_sent_stream_id + 2 <= + frame->push_promise.promised_stream_id); + session->last_sent_stream_id = frame->push_promise.promised_stream_id; + + return 0; + } + case NGHTTP2_PING: + if (frame->hd.flags & NGHTTP2_FLAG_ACK) { + assert(session->obq_flood_counter_ > 0); + --session->obq_flood_counter_; + } + + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping); + return 0; + case NGHTTP2_GOAWAY: + rv = nghttp2_frame_pack_goaway(&session->aob.framebufs, &frame->goaway); + if (rv != 0) { + return rv; + } + session->local_last_stream_id = frame->goaway.last_stream_id; + + return 0; + case NGHTTP2_WINDOW_UPDATE: + rv = session_predicate_window_update_send(session, frame->hd.stream_id); + if (rv != 0) { + return rv; + } + nghttp2_frame_pack_window_update(&session->aob.framebufs, + &frame->window_update); + return 0; + case NGHTTP2_CONTINUATION: + /* We never handle CONTINUATION here. */ + assert(0); + return 0; + default: { + nghttp2_ext_aux_data *aux_data; + + /* extension frame */ + + aux_data = &item->aux_data.ext; + + if (aux_data->builtin == 0) { + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + + return session_pack_extension(session, &session->aob.framebufs, frame); + } + + switch (frame->hd.type) { + case NGHTTP2_ALTSVC: + rv = session_predicate_altsvc_send(session, frame->hd.stream_id); + if (rv != 0) { + return rv; + } + + nghttp2_frame_pack_altsvc(&session->aob.framebufs, &frame->ext); + + return 0; + default: + /* Unreachable here */ + assert(0); + return 0; + } + } + } } nghttp2_outbound_item * @@ -2538,14 +2510,6 @@ static int session_after_frame_sent1(nghttp2_session *session) { break; } - if (stream->item == item) { - rv = nghttp2_stream_detach_item(stream); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - switch (frame->headers.cat) { case NGHTTP2_HCAT_REQUEST: { stream->state = NGHTTP2_STREAM_OPENING;