Return new stream ID from nghttp2_submit_{request, headers, push_promise}

Previously stream ID was assigned just before HEADERS or PUSH_PROMISE
was serialized and nghttp2_submit_{request, headers, push_promise} did
not return stream ID.  The application has to check assigned stream ID
using before_frame_send_callback.  Now it is apparent that priority is
meant to DATA transfer only.  Also application can reorder the
requests if it wants. Therefore we can assign stream ID in
nghttp2_submit_* functions and return stream ID from them.  With this
change, now application does not have to check stream ID using
before_frame_send_callback and its code will be simplified.
This commit is contained in:
Tatsuhiro Tsujikawa
2014-05-07 23:24:07 +09:00
parent 51e79c5a3d
commit e8de437d5c
13 changed files with 293 additions and 415 deletions

View File

@@ -2231,29 +2231,20 @@ int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec);
* arbitrary pointer, which can be retrieved later by
* `nghttp2_session_get_stream_user_data()`.
*
* Since the library reorders the frames and tries to send the highest
* prioritized one first and the HTTP/2 specification requires the
* stream ID must be strictly increasing, the stream ID of this
* request cannot be known until it is about to sent. To know the
* stream ID of the request, the application can use
* :member:`nghttp2_session_callbacks.before_frame_send_callback`.
* This callback is called just before the frame is sent. For HEADERS
* frame, the argument frame has the stream ID assigned. Also since
* the stream is already opened,
* `nghttp2_session_get_stream_user_data()` can be used to get
* |stream_user_data| to identify which HEADERS we are processing.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
* This function returns assigned stream ID if it succeeds, or one of
* the following negative error codes:
*
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
* :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE`
* No stream ID is available because maximum stream ID was
* reached.
*/
int nghttp2_submit_request(nghttp2_session *session,
const nghttp2_priority_spec *pri_spec,
const nghttp2_nv *nva, size_t nvlen,
const nghttp2_data_provider *data_prd,
void *stream_user_data);
int32_t nghttp2_submit_request(nghttp2_session *session,
const nghttp2_priority_spec *pri_spec,
const nghttp2_nv *nva, size_t nvlen,
const nghttp2_data_provider *data_prd,
void *stream_user_data);
/**
* @function
@@ -2314,8 +2305,8 @@ int nghttp2_submit_response(nghttp2_session *session,
*
* If the |stream_id| is -1, this frame is assumed as request (i.e.,
* request HEADERS frame which opens new stream). In this case, the
* actual stream ID is assigned just before the frame is sent. For
* response, specify stream ID in |stream_id|.
* assigned stream ID will be returned. Otherwise, specify stream ID
* in |stream_id|.
*
* The |pri_spec| is priority specification of this request. ``NULL``
* means the default priority (see
@@ -2348,17 +2339,21 @@ int nghttp2_submit_response(nghttp2_session *session,
* specify flags directly. For usual HTTP request,
* `nghttp2_submit_request()` is useful.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
* This function returns newly assigned stream ID if it succeeds and
* |stream_id| is -1. Otherwise, this function returns 0 if it
* succeeds, or one of the following negative error codes:
*
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
* :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE`
* No stream ID is available because maximum stream ID was
* reached.
*/
int nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const nghttp2_priority_spec *pri_spec,
const nghttp2_nv *nva, size_t nvlen,
void *stream_user_data);
int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const nghttp2_priority_spec *pri_spec,
const nghttp2_nv *nva, size_t nvlen,
void *stream_user_data);
/**
* @function
@@ -2500,31 +2495,24 @@ int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
* access it in :type:`nghttp2_before_frame_send_callback` and
* :type:`nghttp2_on_frame_send_callback` of this frame.
*
* Since the library reorders the frames and tries to send the highest
* prioritized one first and the HTTP/2 specification requires the
* stream ID must be strictly increasing, the promised stream ID
* cannot be known until it is about to sent. To know the promised
* stream ID, the application can use
* :member:`nghttp2_session_callbacks.before_frame_send_callback`.
* This callback is called just before the frame is sent. For
* PUSH_PROMISE frame, the argument frame has the promised stream ID
* assigned.
*
* The client side is not allowed to use this function.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
* This function returns assigned promised stream ID if it succeeds,
* or one of the following negative error codes:
*
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
* :enum:`NGHTTP2_ERR_PROTO`
* This function was invoked when |session| is initialized as
* client.
* :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE`
* No stream ID is available because maximum stream ID was
* reached.
*/
int nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const nghttp2_nv *nva, size_t nvlen,
void *promised_stream_user_data);
int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const nghttp2_nv *nva, size_t nvlen,
void *promised_stream_user_data);
/**
* @function

View File

@@ -66,6 +66,7 @@ static void nghttp2_frame_set_hd(nghttp2_frame_hd *hd, uint16_t length,
void nghttp2_frame_headers_init(nghttp2_headers *frame,
uint8_t flags, int32_t stream_id,
nghttp2_headers_category cat,
const nghttp2_priority_spec *pri_spec,
nghttp2_nv *nva, size_t nvlen)
{
@@ -73,7 +74,7 @@ void nghttp2_frame_headers_init(nghttp2_headers *frame,
frame->padlen = 0;
frame->nva = nva;
frame->nvlen = nvlen;
frame->cat = NGHTTP2_HCAT_REQUEST;
frame->cat = cat;
if(pri_spec) {
frame->pri_spec = *pri_spec;

View File

@@ -436,6 +436,7 @@ int nghttp2_frame_pack_blocked(nghttp2_bufs *bufs, nghttp2_blocked *frame);
*/
void nghttp2_frame_headers_init(nghttp2_headers *frame,
uint8_t flags, int32_t stream_id,
nghttp2_headers_category cat,
const nghttp2_priority_spec *pri_spec,
nghttp2_nv *nva, size_t nvlen);

View File

@@ -560,40 +560,13 @@ int nghttp2_session_add_frame(nghttp2_session *session,
if(frame_cat == NGHTTP2_CAT_CTRL) {
nghttp2_frame *frame = (nghttp2_frame*)abs_frame;
nghttp2_stream *stream;
nghttp2_stream *dep_stream;
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
switch(frame->hd.type) {
case NGHTTP2_HEADERS:
if(frame->hd.stream_id == -1) {
/* Initial HEADERS, which will open stream */
item->weight = NGHTTP2_MAX_WEIGHT;
/* TODO If we always frame.headers.pri_spec filled in, we
don't have to check flags */
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
if(frame->headers.pri_spec.stream_id == 0) {
item->weight = frame->headers.pri_spec.weight;
} else {
dep_stream = nghttp2_session_get_stream
(session, frame->headers.pri_spec.stream_id);
if(dep_stream) {
item->weight = nghttp2_stream_dep_distributed_effective_weight
(dep_stream, frame->headers.pri_spec.weight);
} else {
item->weight = frame->headers.pri_spec.weight;
}
}
} else {
item->weight = NGHTTP2_DEFAULT_WEIGHT;
}
} else if(stream) {
/* Otherwise, the frame must have stream ID. We use its
effective_weight. */
item->weight = stream->effective_weight;
}
break;
case NGHTTP2_PRIORITY:
break;
@@ -607,18 +580,13 @@ int nghttp2_session_add_frame(nghttp2_session *session,
stream->state = NGHTTP2_STREAM_CLOSING;
}
}
break;
case NGHTTP2_SETTINGS:
item->weight = NGHTTP2_OB_SETTINGS_WEIGHT;
break;
case NGHTTP2_PUSH_PROMISE:
/* Use priority of associated stream */
if(stream) {
item->weight = stream->effective_weight;
}
item->weight = NGHTTP2_MAX_WEIGHT;
break;
case NGHTTP2_PING:
/* Ping has highest priority. */
@@ -637,7 +605,7 @@ int nghttp2_session_add_frame(nghttp2_session *session,
}
if(frame->hd.type == NGHTTP2_HEADERS &&
(frame->hd.stream_id == -1 ||
(frame->headers.cat == NGHTTP2_HCAT_REQUEST ||
(stream && stream->state == NGHTTP2_STREAM_RESERVED))) {
/* We push request HEADERS and push response HEADERS to
dedicated queue because their transmission is affected by
@@ -996,9 +964,6 @@ static int nghttp2_predicate_stream_for_send(nghttp2_stream *stream)
* NGHTTP2_ERR_START_STREAM_NOT_ALLOWED
* New stream cannot be created because GOAWAY is already sent or
* received.
* NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE
* Stream ID has reached the maximum value. Therefore no stream ID
* is available.
*/
static int nghttp2_session_predicate_request_headers_send
(nghttp2_session *session, nghttp2_headers *frame)
@@ -1008,10 +973,6 @@ static int nghttp2_session_predicate_request_headers_send
HEADERS. */
return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
}
/* All 32bit signed stream IDs are spent. */
if(session->next_stream_id > INT32_MAX) {
return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
}
return 0;
}
@@ -1176,9 +1137,6 @@ static int nghttp2_session_predicate_priority_send
* NGHTTP2_ERR_START_STREAM_NOT_ALLOWED
* New stream cannot be created because GOAWAY is already sent or
* received.
* NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE
* Stream ID has reached the maximum value. Therefore no stream ID
* is available.
* NGHTTP2_ERR_PROTO
* The client side attempts to send PUSH_PROMISE, or the server
* sends PUSH_PROMISE for the stream not initiated by the client.
@@ -1220,10 +1178,6 @@ static int nghttp2_session_predicate_push_promise_send
stream ID */
return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
}
/* All 32bit signed stream IDs are spent. */
if(session->next_stream_id > INT32_MAX) {
return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
}
return 0;
}
@@ -1598,19 +1552,16 @@ static int nghttp2_session_prep_frame(nghttp2_session *session,
aux_data = (nghttp2_headers_aux_data*)item->aux_data;
if(frame->hd.stream_id == -1) {
if(frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
nghttp2_priority_spec pri_spec_default;
nghttp2_stream *stream;
/* initial HEADERS, which opens stream */
frame->headers.cat = NGHTTP2_HCAT_REQUEST;
rv = nghttp2_session_predicate_request_headers_send(session,
&frame->headers);
if(rv != 0) {
return rv;
}
frame->hd.stream_id = session->next_stream_id;
session->next_stream_id += 2;
/* We first open strea with default priority. This is because
priority may be adjusted in callback. */
@@ -1733,8 +1684,7 @@ static int nghttp2_session_prep_frame(nghttp2_session *session,
if(rv != 0) {
return rv;
}
frame->push_promise.promised_stream_id = session->next_stream_id;
session->next_stream_id += 2;
framerv = nghttp2_frame_pack_push_promise(&session->aob.framebufs,
&frame->push_promise,
&session->hd_deflater);

View File

@@ -35,7 +35,7 @@
/* This function takes ownership of |nva_copy|. Regardless of the
return value, the caller must not free |nva_copy| after this
function returns. */
static int nghttp2_submit_headers_shared
static int32_t nghttp2_submit_headers_shared
(nghttp2_session *session,
uint8_t flags,
int32_t stream_id,
@@ -50,6 +50,7 @@ static int nghttp2_submit_headers_shared
nghttp2_frame *frame = NULL;
nghttp2_data_provider *data_prd_copy = NULL;
nghttp2_headers_aux_data *aux_data = NULL;
nghttp2_headers_category hcat;
if(data_prd != NULL && data_prd->read_callback != NULL) {
data_prd_copy = malloc(sizeof(nghttp2_data_provider));
@@ -80,8 +81,24 @@ static int nghttp2_submit_headers_shared
NGHTTP2_FLAG_PRIORITY)) |
NGHTTP2_FLAG_END_HEADERS;
nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, pri_spec,
nva_copy, nvlen);
if(stream_id == -1) {
if(session->next_stream_id > INT32_MAX) {
rv = NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
goto fail;
}
stream_id = session->next_stream_id;
session->next_stream_id += 2;
hcat = NGHTTP2_HCAT_REQUEST;
} else {
/* More specific categorization will be done later. */
hcat = NGHTTP2_HCAT_HEADERS;
}
nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id,
hcat, pri_spec, nva_copy, nvlen);
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame,
aux_data);
@@ -90,7 +107,13 @@ static int nghttp2_submit_headers_shared
nghttp2_frame_headers_free(&frame->headers);
goto fail2;
}
if(hcat == NGHTTP2_HCAT_REQUEST) {
return stream_id;
}
return 0;
fail:
/* nghttp2_frame_headers_init() takes ownership of nva_copy. */
nghttp2_nv_array_del(nva_copy);
@@ -110,7 +133,7 @@ static void adjust_priority_spec_weight(nghttp2_priority_spec *pri_spec)
}
}
static int nghttp2_submit_headers_shared_nva
static int32_t nghttp2_submit_headers_shared_nva
(nghttp2_session *session,
uint8_t flags,
int32_t stream_id,
@@ -141,11 +164,11 @@ static int nghttp2_submit_headers_shared_nva
stream_user_data);
}
int nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const nghttp2_priority_spec *pri_spec,
const nghttp2_nv *nva, size_t nvlen,
void *stream_user_data)
int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const nghttp2_priority_spec *pri_spec,
const nghttp2_nv *nva, size_t nvlen,
void *stream_user_data)
{
flags &= NGHTTP2_FLAG_END_STREAM;
@@ -227,15 +250,16 @@ int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv);
}
int nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const nghttp2_nv *nva, size_t nvlen,
void *promised_stream_user_data)
int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const nghttp2_nv *nva, size_t nvlen,
void *promised_stream_user_data)
{
nghttp2_frame *frame;
nghttp2_nv *nva_copy;
uint8_t flags_copy;
nghttp2_headers_aux_data *aux_data = NULL;
int32_t promised_stream_id;
int rv;
if(!session->server) {
@@ -261,16 +285,35 @@ int nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
free(frame);
return rv;
}
flags_copy = NGHTTP2_FLAG_END_HEADERS;
/* All 32bit signed stream IDs are spent. */
if(session->next_stream_id > INT32_MAX) {
free(aux_data);
free(frame);
return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
}
promised_stream_id = session->next_stream_id;
session->next_stream_id += 2;
nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy,
stream_id, -1, nva_copy, nvlen);
stream_id, promised_stream_id,
nva_copy, nvlen);
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, aux_data);
if(rv != 0) {
nghttp2_frame_push_promise_free(&frame->push_promise);
free(aux_data);
free(frame);
return rv;
}
return 0;
return promised_stream_id;
}
int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
@@ -404,11 +447,11 @@ static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
return flags;
}
int nghttp2_submit_request(nghttp2_session *session,
const nghttp2_priority_spec *pri_spec,
const nghttp2_nv *nva, size_t nvlen,
const nghttp2_data_provider *data_prd,
void *stream_user_data)
int32_t nghttp2_submit_request(nghttp2_session *session,
const nghttp2_priority_spec *pri_spec,
const nghttp2_nv *nva, size_t nvlen,
const nghttp2_data_provider *data_prd,
void *stream_user_data)
{
uint8_t flags;