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); }