mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-07 18:48:54 +08:00
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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user