From e4537596372b2a7a9582a18676b0df5cc48953ae Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Mon, 29 Feb 2016 23:39:50 +0900 Subject: [PATCH] Add nghttp2_option_set_no_auto_ping_ack() option This option prevents the nghttp2 library from sending PING frame with ACK flag set in the reply to incoming PING frame. To allow the application to send PING with ACK flag set, nghttp2_submit_ping() now recognizes NGHTTP2_FLAG_PING in its flags parameter. --- doc/Makefile.am | 1 + lib/includes/nghttp2/nghttp2.h | 22 ++++++++++++++++++++-- lib/nghttp2_option.c | 5 +++++ lib/nghttp2_option.h | 7 ++++++- lib/nghttp2_session.c | 8 +++++++- lib/nghttp2_session.h | 3 ++- lib/nghttp2_submit.c | 3 ++- tests/nghttp2_session_test.c | 27 +++++++++++++++++++++++++++ 8 files changed, 70 insertions(+), 6 deletions(-) diff --git a/doc/Makefile.am b/doc/Makefile.am index 1052f957..35f1390b 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -55,6 +55,7 @@ APIDOCS= \ nghttp2_option_del.rst \ nghttp2_option_new.rst \ nghttp2_option_set_max_reserved_remote_streams.rst \ + nghttp2_option_set_no_auto_ping_ack.rst \ nghttp2_option_set_no_auto_window_update.rst \ nghttp2_option_set_no_http_messaging.rst \ nghttp2_option_set_no_recv_client_magic.rst \ diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h index f4c53fa6..f6133055 100644 --- a/lib/includes/nghttp2/nghttp2.h +++ b/lib/includes/nghttp2/nghttp2.h @@ -2251,6 +2251,20 @@ NGHTTP2_EXTERN void nghttp2_option_set_user_recv_extension_type(nghttp2_option *option, uint8_t type); +/** + * @function + * + * This option prevents the library from sending PING frame with ACK + * flag set automatically when PING frame without ACK flag set is + * received. If this option is set to nonzero, the library won't send + * PING frame with ACK flag set in the response for incoming PING + * frame. The application can send PING frame with ACK flag set using + * `nghttp2_submit_ping()` with :enum:`NGHTTP2_FLAG_ACK` as flags + * parameter. + */ +NGHTTP2_EXTERN void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option, + int val); + /** * @function * @@ -3783,8 +3797,12 @@ nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, * received PING frame. The library automatically submits PING frame * in this case. * - * The |flags| is currently ignored and should be - * :enum:`NGHTTP2_FLAG_NONE`. + * The |flags| is bitwise OR of 0 or more of the following value. + * + * * :enum:`NGHTTP2_FLAG_ACK` + * + * Unless `nghttp2_option_set_no_auto_ping_ack()` is used, the |flags| + * should be :enum:`NGHTTP2_FLAG_NONE`. * * If the |opaque_data| is non ``NULL``, then it should point to the 8 * bytes array of memory to specify opaque data to send with PING diff --git a/lib/nghttp2_option.c b/lib/nghttp2_option.c index fd665112..e3e8717b 100644 --- a/lib/nghttp2_option.c +++ b/lib/nghttp2_option.c @@ -73,3 +73,8 @@ void nghttp2_option_set_user_recv_extension_type(nghttp2_option *option, option->user_recv_ext_types[type / 8] = (uint8_t)(option->user_recv_ext_types[type / 8] | (1 << (type & 0x7))); } + +void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_PING_ACK; + option->no_auto_ping_ack = val; +} diff --git a/lib/nghttp2_option.h b/lib/nghttp2_option.h index a2d090fb..f859b4eb 100644 --- a/lib/nghttp2_option.h +++ b/lib/nghttp2_option.h @@ -60,7 +60,8 @@ typedef enum { NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2, NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3, NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4, - NGHTTP2_OPT_USER_RECV_EXT_TYPES = 1 << 5 + NGHTTP2_OPT_USER_RECV_EXT_TYPES = 1 << 5, + NGHTTP2_OPT_NO_AUTO_PING_ACK = 1 << 6 } nghttp2_option_flag; /** @@ -92,6 +93,10 @@ struct nghttp2_option { * NGHTTP2_OPT_NO_HTTP_MESSAGING */ int no_http_messaging; + /** + * NGHTTP2_OPT_NO_AUTO_PING_ACK + */ + int no_auto_ping_ack; /** * NGHTTP2_OPT_USER_RECV_EXT_TYPES */ diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index fffdf45d..cbc1bac6 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -416,6 +416,11 @@ static int session_new(nghttp2_session **session_ptr, memcpy((*session_ptr)->user_recv_ext_types, option->user_recv_ext_types, sizeof((*session_ptr)->user_recv_ext_types)); } + + if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_PING_ACK) && + option->no_auto_ping_ack) { + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_PING_ACK; + } } (*session_ptr)->callbacks = *callbacks; @@ -4364,7 +4369,8 @@ int nghttp2_session_on_ping_received(nghttp2_session *session, return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, "PING: stream_id != 0"); } - if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 && + if ((session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_PING_ACK) == 0 && + (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 && !session_is_closing(session)) { /* Peer sent ping, so ping it back */ rv = nghttp2_session_add_ping(session, NGHTTP2_FLAG_ACK, diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h index c319c15e..3c3764de 100644 --- a/lib/nghttp2_session.h +++ b/lib/nghttp2_session.h @@ -50,7 +50,8 @@ extern int nghttp2_enable_strict_preface; typedef enum { NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0, NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC = 1 << 1, - NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2 + NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2, + NGHTTP2_OPTMASK_NO_AUTO_PING_ACK = 1 << 3 } nghttp2_optmask; typedef enum { diff --git a/lib/nghttp2_submit.c b/lib/nghttp2_submit.c index 19f2a316..9c11e210 100644 --- a/lib/nghttp2_submit.c +++ b/lib/nghttp2_submit.c @@ -211,8 +211,9 @@ int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, nvlen, NULL, stream_user_data); } -int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags _U_, +int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, const uint8_t *opaque_data) { + flags &= NGHTTP2_FLAG_ACK; return nghttp2_session_add_ping(session, NGHTTP2_FLAG_NONE, opaque_data); } diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c index 54da8670..2e7dafee 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -3095,6 +3095,7 @@ void test_nghttp2_session_on_ping_received(void) { nghttp2_frame frame; nghttp2_outbound_item *top; const uint8_t opaque_data[] = "01234567"; + nghttp2_option *option; user_data.frame_recv_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; @@ -3125,6 +3126,23 @@ void test_nghttp2_session_on_ping_received(void) { nghttp2_frame_ping_free(&frame.ping); nghttp2_session_del(session); + + /* Use nghttp2_option_set_no_auto_ping_ack() */ + nghttp2_option_new(&option); + nghttp2_option_set_no_auto_ping_ack(option, 1); + + nghttp2_session_server_new2(&session, &callbacks, &user_data, option); + nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL); + + user_data.frame_recv_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame)); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + CU_ASSERT(NULL == nghttp2_outbound_queue_top(&session->ob_urgent)); + + nghttp2_frame_ping_free(&frame.ping); + nghttp2_session_del(session); + nghttp2_option_del(option); } void test_nghttp2_session_on_goaway_received(void) { @@ -6153,6 +6171,15 @@ void test_nghttp2_session_set_option(void) { CU_ASSERT(99 == session->max_incoming_reserved_streams); nghttp2_session_del(session); + /* Test for nghttp2_option_set_no_auto_ping_ack */ + nghttp2_option_set_no_auto_ping_ack(option, 1); + + nghttp2_session_client_new2(&session, &callbacks, NULL, option); + + CU_ASSERT(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_PING_ACK); + + nghttp2_session_del(session); + nghttp2_option_del(option); }