Simplify code

Move DATA frame handling code to switch-case of frame type.
This commit is contained in:
Tatsuhiro Tsujikawa 2016-09-11 16:55:15 +09:00
parent 8099dd9558
commit 9d4e8eeb12
1 changed files with 329 additions and 365 deletions

View File

@ -816,99 +816,96 @@ int nghttp2_session_add_item(nghttp2_session *session,
frame = &item->frame; frame = &item->frame;
stream = nghttp2_session_get_stream(session, frame->hd.stream_id); stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
if (frame->hd.type != NGHTTP2_DATA) { switch (frame->hd.type) {
case NGHTTP2_DATA:
switch (frame->hd.type) { if (!stream) {
case NGHTTP2_HEADERS: return NGHTTP2_ERR_STREAM_CLOSED;
/* 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;
} }
case NGHTTP2_WINDOW_UPDATE:
if (stream) { if (stream->item) {
stream->window_update_queued = 1; return NGHTTP2_ERR_DATA_EXIST;
} else if (frame->hd.stream_id == 0) { }
session->window_update_queued = 1;
} rv = nghttp2_stream_attach_item(stream, item);
nghttp2_outbound_queue_push(&session->ob_reg, item);
item->queued = 1; if (rv != 0) {
break; return rv;
default:
nghttp2_outbound_queue_push(&session->ob_reg, item);
item->queued = 1;
} }
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; return 0;
} }
case NGHTTP2_WINDOW_UPDATE:
if (!stream) { if (stream) {
return NGHTTP2_ERR_STREAM_CLOSED; 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, 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; mem = &session->mem;
frame = &item->frame; frame = &item->frame;
if (frame->hd.type != NGHTTP2_DATA) { switch (frame->hd.type) {
switch (frame->hd.type) { case NGHTTP2_DATA: {
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 {
size_t next_readmax; size_t next_readmax;
nghttp2_stream *stream; nghttp2_stream *stream;
@ -2304,6 +2033,249 @@ static int session_prep_frame(nghttp2_session *session,
} }
return 0; 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 * nghttp2_outbound_item *
@ -2538,14 +2510,6 @@ static int session_after_frame_sent1(nghttp2_session *session) {
break; break;
} }
if (stream->item == item) {
rv = nghttp2_stream_detach_item(stream);
if (nghttp2_is_fatal(rv)) {
return rv;
}
}
switch (frame->headers.cat) { switch (frame->headers.cat) {
case NGHTTP2_HCAT_REQUEST: { case NGHTTP2_HCAT_REQUEST: {
stream->state = NGHTTP2_STREAM_OPENING; stream->state = NGHTTP2_STREAM_OPENING;