Create stream object for pushed resource during nghttp2_submit_push_promise()
Previously, stream object for pushed resource was not created during nghttp2_submit_push_promise(). It was created just before nghttp2_before_frame_send_callback was called for that PUSH_PROMISE frame. This means that application could not call nghttp2_submit_response for the pushed resource before nghttp2_before_frame_send_callback was called. This could be solved by callback chaining, but for web server with back pressure from backend stream, it is a bit unnecessarily hard to use. This commit changes nghttp2_submit_push_promise() behaviour so that stream object is created during that call. It makes application call nghttp2_submit_response right after successful nghttp2_submit_push_promise call.
This commit is contained in:
parent
6beaf4d9f3
commit
2288ee8060
|
@ -3573,14 +3573,20 @@ NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session,
|
||||||
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
|
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
|
||||||
* The |stream_id| is 0; The |stream_id| does not designate stream
|
* The |stream_id| is 0; The |stream_id| does not designate stream
|
||||||
* that peer initiated.
|
* that peer initiated.
|
||||||
|
* :enum:`NGHTTP2_ERR_STREAM_CLOSED`
|
||||||
|
* The stream was alreay closed; or the |stream_id| is invalid.
|
||||||
*
|
*
|
||||||
* .. warning::
|
* .. warning::
|
||||||
*
|
*
|
||||||
* This function returns assigned promised stream ID if it succeeds.
|
* This function returns assigned promised stream ID if it succeeds.
|
||||||
* But that stream is not opened yet. The application must not
|
* As of 1.16.0, stream object for pushed resource is created when
|
||||||
* submit frame to that stream ID before
|
* this function succeeds. In that case, the application can submit
|
||||||
* :type:`nghttp2_before_frame_send_callback` is called for this
|
* push response for the promised frame.
|
||||||
* frame.
|
*
|
||||||
|
* In 1.15.0 or prior versions, pushed stream is not opened yet when
|
||||||
|
* this function succeeds. The application must not submit frame to
|
||||||
|
* that stream ID before :type:`nghttp2_before_frame_send_callback`
|
||||||
|
* is called for this frame.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN int32_t
|
NGHTTP2_EXTERN int32_t
|
||||||
|
|
|
@ -750,6 +750,31 @@ int nghttp2_session_add_item(nghttp2_session *session,
|
||||||
nghttp2_outbound_queue_push(&session->ob_reg, item);
|
nghttp2_outbound_queue_push(&session->ob_reg, item);
|
||||||
item->queued = 1;
|
item->queued = 1;
|
||||||
break;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
nghttp2_outbound_queue_push(&session->ob_reg, item);
|
||||||
|
item->queued = 1;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
case NGHTTP2_WINDOW_UPDATE:
|
case NGHTTP2_WINDOW_UPDATE:
|
||||||
if (stream) {
|
if (stream) {
|
||||||
stream->window_update_queued = 1;
|
stream->window_update_queued = 1;
|
||||||
|
@ -1903,30 +1928,8 @@ static int session_prep_frame(nghttp2_session *session,
|
||||||
}
|
}
|
||||||
case NGHTTP2_PUSH_PROMISE: {
|
case NGHTTP2_PUSH_PROMISE: {
|
||||||
nghttp2_stream *stream;
|
nghttp2_stream *stream;
|
||||||
nghttp2_headers_aux_data *aux_data;
|
|
||||||
nghttp2_priority_spec pri_spec;
|
|
||||||
size_t estimated_payloadlen;
|
size_t estimated_payloadlen;
|
||||||
|
|
||||||
aux_data = &item->aux_data.headers;
|
|
||||||
|
|
||||||
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
|
|
||||||
|
|
||||||
/* stream could be NULL if associated stream was already
|
|
||||||
closed. */
|
|
||||||
if (stream) {
|
|
||||||
nghttp2_priority_spec_init(&pri_spec, stream->stream_id,
|
|
||||||
NGHTTP2_DEFAULT_WEIGHT, 0);
|
|
||||||
} else {
|
|
||||||
nghttp2_priority_spec_default_init(&pri_spec);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
estimated_payloadlen = session_estimate_headers_payload(
|
estimated_payloadlen = session_estimate_headers_payload(
|
||||||
session, frame->push_promise.nva, frame->push_promise.nvlen, 0);
|
session, frame->push_promise.nva, frame->push_promise.nvlen, 0);
|
||||||
|
|
||||||
|
@ -1934,6 +1937,10 @@ static int session_prep_frame(nghttp2_session *session,
|
||||||
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
|
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. */
|
/* predicte should fail if stream is NULL. */
|
||||||
rv = session_predicate_push_promise_send(session, stream);
|
rv = session_predicate_push_promise_send(session, stream);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
|
|
|
@ -334,7 +334,7 @@ int nghttp2_session_is_my_stream_id(nghttp2_session *session,
|
||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory.
|
* Out of memory.
|
||||||
* NGHTTP2_ERR_STREAM_CLOSED
|
* NGHTTP2_ERR_STREAM_CLOSED
|
||||||
* Stream already closed (DATA frame only)
|
* Stream already closed (DATA and PUSH_PROMISE frame only)
|
||||||
*/
|
*/
|
||||||
int nghttp2_session_add_item(nghttp2_session *session,
|
int nghttp2_session_add_item(nghttp2_session *session,
|
||||||
nghttp2_outbound_item *item);
|
nghttp2_outbound_item *item);
|
||||||
|
|
|
@ -4554,28 +4554,28 @@ void test_nghttp2_submit_push_promise(void) {
|
||||||
CU_ASSERT(2 == nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 1,
|
CU_ASSERT(2 == nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 1,
|
||||||
reqnv, ARRLEN(reqnv), &ud));
|
reqnv, ARRLEN(reqnv), &ud));
|
||||||
|
|
||||||
|
stream = nghttp2_session_get_stream(session, 2);
|
||||||
|
|
||||||
|
CU_ASSERT(NULL != stream);
|
||||||
|
CU_ASSERT(NGHTTP2_STREAM_RESERVED == stream->state);
|
||||||
|
CU_ASSERT(&ud == nghttp2_session_get_stream_user_data(session, 2));
|
||||||
|
|
||||||
ud.frame_send_cb_called = 0;
|
ud.frame_send_cb_called = 0;
|
||||||
ud.sent_frame_type = 0;
|
ud.sent_frame_type = 0;
|
||||||
|
|
||||||
CU_ASSERT(0 == nghttp2_session_send(session));
|
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||||
CU_ASSERT(1 == ud.frame_send_cb_called);
|
CU_ASSERT(1 == ud.frame_send_cb_called);
|
||||||
CU_ASSERT(NGHTTP2_PUSH_PROMISE == ud.sent_frame_type);
|
CU_ASSERT(NGHTTP2_PUSH_PROMISE == ud.sent_frame_type);
|
||||||
|
|
||||||
stream = nghttp2_session_get_stream(session, 2);
|
stream = nghttp2_session_get_stream(session, 2);
|
||||||
|
|
||||||
CU_ASSERT(NGHTTP2_STREAM_RESERVED == stream->state);
|
CU_ASSERT(NGHTTP2_STREAM_RESERVED == stream->state);
|
||||||
CU_ASSERT(&ud == nghttp2_session_get_stream_user_data(session, 2));
|
CU_ASSERT(&ud == nghttp2_session_get_stream_user_data(session, 2));
|
||||||
|
|
||||||
/* submit PUSH_PROMISE while associated stream is not opened */
|
/* submit PUSH_PROMISE while associated stream is not opened */
|
||||||
CU_ASSERT(4 == nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 3,
|
CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSED ==
|
||||||
reqnv, ARRLEN(reqnv), &ud));
|
nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 3, reqnv,
|
||||||
|
ARRLEN(reqnv), &ud));
|
||||||
ud.frame_not_send_cb_called = 0;
|
|
||||||
|
|
||||||
CU_ASSERT(0 == nghttp2_session_send(session));
|
|
||||||
CU_ASSERT(1 == ud.frame_not_send_cb_called);
|
|
||||||
CU_ASSERT(NGHTTP2_PUSH_PROMISE == ud.not_sent_frame_type);
|
|
||||||
|
|
||||||
stream = nghttp2_session_get_stream(session, 4);
|
|
||||||
|
|
||||||
CU_ASSERT(NULL == stream);
|
|
||||||
|
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue