From d1c1deaf03af8c0af0ff0b931cbec1927c5d48b2 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Tue, 25 Feb 2014 00:26:12 +0900 Subject: [PATCH] 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. --- lib/includes/nghttp2/nghttp2.h | 10 +++++++++- lib/nghttp2_frame.c | 4 ---- lib/nghttp2_frame.h | 3 --- lib/nghttp2_session.c | 8 ++++++-- lib/nghttp2_submit.c | 17 +++++++++++++++-- src/HttpServer.cc | 3 ++- tests/nghttp2_frame_test.c | 3 ++- tests/nghttp2_session_test.c | 10 ++++++---- 8 files changed, 40 insertions(+), 18 deletions(-) diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h index 2a994fd2..e2e427da 100644 --- a/lib/includes/nghttp2/nghttp2.h +++ b/lib/includes/nghttp2/nghttp2.h @@ -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 * 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 * prioritized one first and the HTTP/2.0 specification requires the * 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, int32_t stream_id, - const nghttp2_nv *nva, size_t nvlen); + const nghttp2_nv *nva, size_t nvlen, + void *promised_stream_user_data); /** * @function diff --git a/lib/nghttp2_frame.c b/lib/nghttp2_frame.c index e9b39c8a..1d73af84 100644 --- a/lib/nghttp2_frame.c +++ b/lib/nghttp2_frame.c @@ -598,10 +598,6 @@ ssize_t nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, size_t buflen = 0; nghttp2_nv *p; 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; } /* If all name/value pair is 0-length, remove them */ diff --git a/lib/nghttp2_frame.h b/lib/nghttp2_frame.h index 16754d09..5826d7e2 100644 --- a/lib/nghttp2_frame.h +++ b/lib/nghttp2_frame.h @@ -481,9 +481,6 @@ void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen); * * NGHTTP2_ERR_NOMEM * 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, const nghttp2_nv *nva, size_t nvlen); diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index 4709ddbc..d5d09a58 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -1303,6 +1303,9 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, } case NGHTTP2_PUSH_PROMISE: { 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, frame->hd.stream_id); 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); assert(stream); - if(nghttp2_session_open_stream + if(!nghttp2_session_open_stream (session, frame->push_promise.promised_stream_id, NGHTTP2_STREAM_FLAG_PUSH, nghttp2_pushed_stream_pri(stream), NGHTTP2_STREAM_RESERVED, - NULL) == NULL) { + aux_data ? + aux_data->stream_user_data : NULL)) { return NGHTTP2_ERR_NOMEM; } break; diff --git a/lib/nghttp2_submit.c b/lib/nghttp2_submit.c index c33c74d0..f9dfd464 100644 --- a/lib/nghttp2_submit.c +++ b/lib/nghttp2_submit.c @@ -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, 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_nv *nva_copy; uint8_t flags_copy; + nghttp2_headers_aux_data *aux_data = NULL; int rv; frame = malloc(sizeof(nghttp2_frame)); if(frame == NULL) { 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); if(rv < 0) { + free(aux_data); free(frame); return rv; } flags_copy = NGHTTP2_FLAG_END_HEADERS; nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, 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) { nghttp2_frame_push_promise_free(&frame->push_promise); + free(aux_data); free(frame); } return 0; diff --git a/src/HttpServer.cc b/src/HttpServer.cc index 1b678761..81fe9629 100644 --- a/src/HttpServer.cc +++ b/src/HttpServer.cc @@ -515,7 +515,8 @@ int Http2Handler::submit_push_promise(Request *req, http2::make_nv_ls(":authority", (*itr).second) }; 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 req) diff --git a/tests/nghttp2_frame_test.c b/tests/nghttp2_frame_test.c index c3dcb895..6eefe085 100644 --- a/tests/nghttp2_frame_test.c +++ b/tests/nghttp2_frame_test.c @@ -406,8 +406,9 @@ void test_nghttp2_nv_array_copy(void) nghttp2_nv_array_del(nva); + /* Large header field is acceptable */ rv = nghttp2_nv_array_copy(&nva, &bignv, 1); - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv); + CU_ASSERT(1 == rv); free(bignv.value); } diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c index 3b1afc12..6253be28 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -2201,7 +2201,7 @@ void test_nghttp2_session_reprioritize_stream(void) CU_ASSERT(0 == nghttp2_submit_push_promise(session, NGHTTP2_FLAG_END_HEADERS, - 3, NULL, 0)); + 3, NULL, 0, NULL)); ud.block_count = 0; CU_ASSERT(0 == nghttp2_session_send(session)); /* Now PUSH_PROMISE is in aob */ @@ -2846,7 +2846,7 @@ void test_nghttp2_submit_push_promise(void) NGHTTP2_PRI_DEFAULT, NGHTTP2_STREAM_OPENING, NULL); 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.sent_frame_type = 0; @@ -2855,6 +2855,7 @@ void test_nghttp2_submit_push_promise(void) CU_ASSERT(NGHTTP2_PUSH_PROMISE == ud.sent_frame_type); stream = nghttp2_session_get_stream(session, 2); CU_ASSERT(NGHTTP2_STREAM_RESERVED == stream->state); + CU_ASSERT(&ud == nghttp2_session_get_stream_user_data(session, 2)); nghttp2_session_del(session); } @@ -3039,7 +3040,8 @@ void test_nghttp2_submit_invalid_nv(void) /* nghttp2_submit_push_promise */ CU_ASSERT(0 == 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); } @@ -4128,7 +4130,7 @@ void test_nghttp2_session_pack_headers_with_padding(void) /* Check PUSH_PROMISE */ CU_ASSERT(0 == nghttp2_submit_push_promise(sv_session, NGHTTP2_FLAG_NONE, 1, - nva, ARRLEN(nva))); + nva, ARRLEN(nva), NULL)); acc.length = 0; CU_ASSERT(0 == nghttp2_session_send(sv_session));