Add promised_stream_user_data parameter to nghttp2_submit_push_promise

This is very useful to associate application specific data to promised
stream.

nghttp2_nv_array_copy now does not complain the header field is large.
This commit is contained in:
Tatsuhiro Tsujikawa 2014-02-25 00:26:12 +09:00
parent 86aa905c10
commit d1c1deaf03
8 changed files with 40 additions and 18 deletions

View File

@ -2140,6 +2140,13 @@ int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
* This function creates copies of all name/value pairs in |nva|. It * This function creates copies of all name/value pairs in |nva|. It
* also lower-cases all names in |nva|. * also lower-cases all names in |nva|.
* *
* The |promised_stream_user_data| is a pointer to an arbitrary data
* which is associated to the promised stream this frame will open and
* make it in reserved state. It is available using
* `nghttp2_session_get_stream_user_data()`. The application can
* 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 * Since the library reorders the frames and tries to send the highest
* prioritized one first and the HTTP/2.0 specification requires the * prioritized one first and the HTTP/2.0 specification requires the
* stream ID must be strictly increasing, the promised stream ID * stream ID must be strictly increasing, the promised stream ID
@ -2160,7 +2167,8 @@ int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
*/ */
int nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, int nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
int32_t stream_id, int32_t stream_id,
const nghttp2_nv *nva, size_t nvlen); const nghttp2_nv *nva, size_t nvlen,
void *promised_stream_user_data);
/** /**
* @function * @function

View File

@ -598,10 +598,6 @@ ssize_t nghttp2_nv_array_copy(nghttp2_nv **nva_ptr,
size_t buflen = 0; size_t buflen = 0;
nghttp2_nv *p; nghttp2_nv *p;
for(i = 0; i < nvlen; ++i) { for(i = 0; i < nvlen; ++i) {
if(nva[i].namelen > NGHTTP2_HD_MAX_NAME ||
nva[i].valuelen > NGHTTP2_HD_MAX_VALUE) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
buflen += nva[i].namelen + nva[i].valuelen; buflen += nva[i].namelen + nva[i].valuelen;
} }
/* If all name/value pair is 0-length, remove them */ /* If all name/value pair is 0-length, remove them */

View File

@ -481,9 +481,6 @@ void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen);
* *
* NGHTTP2_ERR_NOMEM * NGHTTP2_ERR_NOMEM
* Out of memory. * Out of memory.
* NGHTTP2_ERR_INVALID_ARGUMENT
* The length of name or value in |nva| is strictly larger than
* NGHTTP2_MAX_HD_VALUE_LENGTH.
*/ */
ssize_t nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, ssize_t nghttp2_nv_array_copy(nghttp2_nv **nva_ptr,
const nghttp2_nv *nva, size_t nvlen); const nghttp2_nv *nva, size_t nvlen);

View File

@ -1303,6 +1303,9 @@ static ssize_t nghttp2_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;
aux_data = (nghttp2_headers_aux_data*)item->aux_data;
rv = nghttp2_session_predicate_push_promise_send(session, rv = nghttp2_session_predicate_push_promise_send(session,
frame->hd.stream_id); frame->hd.stream_id);
if(rv != 0) { if(rv != 0) {
@ -1325,12 +1328,13 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session,
stream = nghttp2_session_get_stream(session, frame->hd.stream_id); stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
assert(stream); assert(stream);
if(nghttp2_session_open_stream if(!nghttp2_session_open_stream
(session, frame->push_promise.promised_stream_id, (session, frame->push_promise.promised_stream_id,
NGHTTP2_STREAM_FLAG_PUSH, NGHTTP2_STREAM_FLAG_PUSH,
nghttp2_pushed_stream_pri(stream), nghttp2_pushed_stream_pri(stream),
NGHTTP2_STREAM_RESERVED, NGHTTP2_STREAM_RESERVED,
NULL) == NULL) { aux_data ?
aux_data->stream_user_data : NULL)) {
return NGHTTP2_ERR_NOMEM; return NGHTTP2_ERR_NOMEM;
} }
break; break;

View File

@ -181,28 +181,41 @@ int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
int nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, int nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
int32_t stream_id, int32_t stream_id,
const nghttp2_nv *nva, size_t nvlen) const nghttp2_nv *nva, size_t nvlen,
void *promised_stream_user_data)
{ {
nghttp2_frame *frame; nghttp2_frame *frame;
nghttp2_nv *nva_copy; nghttp2_nv *nva_copy;
uint8_t flags_copy; uint8_t flags_copy;
nghttp2_headers_aux_data *aux_data = NULL;
int rv; int rv;
frame = malloc(sizeof(nghttp2_frame)); frame = malloc(sizeof(nghttp2_frame));
if(frame == NULL) { if(frame == NULL) {
return NGHTTP2_ERR_NOMEM; return NGHTTP2_ERR_NOMEM;
} }
if(promised_stream_user_data) {
aux_data = malloc(sizeof(nghttp2_headers_aux_data));
if(aux_data == NULL) {
free(frame);
return NGHTTP2_ERR_NOMEM;
}
aux_data->data_prd = NULL;
aux_data->stream_user_data = promised_stream_user_data;
}
rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen); rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen);
if(rv < 0) { if(rv < 0) {
free(aux_data);
free(frame); free(frame);
return rv; return rv;
} }
flags_copy = NGHTTP2_FLAG_END_HEADERS; flags_copy = NGHTTP2_FLAG_END_HEADERS;
nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy,
stream_id, -1, nva_copy, nvlen); stream_id, -1, nva_copy, nvlen);
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL); rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, aux_data);
if(rv != 0) { if(rv != 0) {
nghttp2_frame_push_promise_free(&frame->push_promise); nghttp2_frame_push_promise_free(&frame->push_promise);
free(aux_data);
free(frame); free(frame);
} }
return 0; return 0;

View File

@ -515,7 +515,8 @@ int Http2Handler::submit_push_promise(Request *req,
http2::make_nv_ls(":authority", (*itr).second) http2::make_nv_ls(":authority", (*itr).second)
}; };
return nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_END_HEADERS, return nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_END_HEADERS,
req->stream_id, nva.data(), nva.size()); req->stream_id, nva.data(), nva.size(),
nullptr);
} }
void Http2Handler::add_stream(int32_t stream_id, std::unique_ptr<Request> req) void Http2Handler::add_stream(int32_t stream_id, std::unique_ptr<Request> req)

View File

@ -406,8 +406,9 @@ void test_nghttp2_nv_array_copy(void)
nghttp2_nv_array_del(nva); nghttp2_nv_array_del(nva);
/* Large header field is acceptable */
rv = nghttp2_nv_array_copy(&nva, &bignv, 1); rv = nghttp2_nv_array_copy(&nva, &bignv, 1);
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv); CU_ASSERT(1 == rv);
free(bignv.value); free(bignv.value);
} }

View File

@ -2201,7 +2201,7 @@ void test_nghttp2_session_reprioritize_stream(void)
CU_ASSERT(0 == nghttp2_submit_push_promise(session, CU_ASSERT(0 == nghttp2_submit_push_promise(session,
NGHTTP2_FLAG_END_HEADERS, NGHTTP2_FLAG_END_HEADERS,
3, NULL, 0)); 3, NULL, 0, NULL));
ud.block_count = 0; ud.block_count = 0;
CU_ASSERT(0 == nghttp2_session_send(session)); CU_ASSERT(0 == nghttp2_session_send(session));
/* Now PUSH_PROMISE is in aob */ /* Now PUSH_PROMISE is in aob */
@ -2846,7 +2846,7 @@ void test_nghttp2_submit_push_promise(void)
NGHTTP2_PRI_DEFAULT, NGHTTP2_PRI_DEFAULT,
NGHTTP2_STREAM_OPENING, NULL); NGHTTP2_STREAM_OPENING, NULL);
CU_ASSERT(0 == nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 1, CU_ASSERT(0 == nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 1,
nv, ARRLEN(nv))); nv, ARRLEN(nv), &ud));
ud.frame_send_cb_called = 0; ud.frame_send_cb_called = 0;
ud.sent_frame_type = 0; ud.sent_frame_type = 0;
@ -2855,6 +2855,7 @@ void test_nghttp2_submit_push_promise(void)
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));
nghttp2_session_del(session); nghttp2_session_del(session);
} }
@ -3039,7 +3040,8 @@ void test_nghttp2_submit_invalid_nv(void)
/* nghttp2_submit_push_promise */ /* nghttp2_submit_push_promise */
CU_ASSERT(0 == CU_ASSERT(0 ==
nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 2, nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 2,
empty_name_nv, ARRLEN(empty_name_nv))); empty_name_nv, ARRLEN(empty_name_nv),
NULL));
nghttp2_session_del(session); nghttp2_session_del(session);
} }
@ -4128,7 +4130,7 @@ void test_nghttp2_session_pack_headers_with_padding(void)
/* Check PUSH_PROMISE */ /* Check PUSH_PROMISE */
CU_ASSERT(0 == CU_ASSERT(0 ==
nghttp2_submit_push_promise(sv_session, NGHTTP2_FLAG_NONE, 1, nghttp2_submit_push_promise(sv_session, NGHTTP2_FLAG_NONE, 1,
nva, ARRLEN(nva))); nva, ARRLEN(nva), NULL));
acc.length = 0; acc.length = 0;
CU_ASSERT(0 == nghttp2_session_send(sv_session)); CU_ASSERT(0 == nghttp2_session_send(sv_session));