Skip to content

Commit be35466

Browse files
committed
Add HTTP/2 flow control configurability
Issue #8199 outlines the issue. This PR adds the ability to specify different flow control mechanisms. Closes #8199
1 parent 6d9652e commit be35466

File tree

11 files changed

+648
-81
lines changed

11 files changed

+648
-81
lines changed

doc/admin-guide/files/records.config.en.rst

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4152,7 +4152,34 @@ HTTP/2 Configuration
41524152
:reloadable:
41534153
:units: bytes
41544154

4155-
The initial window size for inbound connections.
4155+
The initial HTTP/2 stream window size for inbound connections that |TS| as a
4156+
receiver advertises to the peer. See IETF RFC 9113 section 5.2 for details
4157+
concerning HTTP/2 flow control. See
4158+
:ts:cv:`proxy.config.http2.flow_control.in.policy` for how HTTP/2 stream and
4159+
session windows are maintained over the lifetime of HTTP/2 connections.
4160+
4161+
.. ts:cv:: CONFIG proxy.config.http2.flow_control.in.policy INT 0
4162+
:reloadable:
4163+
4164+
Specifies the mechanism |TS| uses to maintian flow control via the HTTP/2
4165+
stream and session windows for inbound connections. See IETF RFC 9113
4166+
section 5.2 for details concerning HTTP/2 flow control.
4167+
4168+
===== ===========================================================================================
4169+
Value Description
4170+
===== ===========================================================================================
4171+
``0`` Session and stream receive windows are initialized and maintained at the value as specified
4172+
in :ts:cv:`proxy.config.http2.initial_window_size_in` over the lifetime of HTTP/2
4173+
connections.
4174+
``1`` Session receive windows are initialized to the value of the product of
4175+
:ts:cv:`proxy.config.http2.initial_window_size_in` and
4176+
:ts:cv:`proxy.config.http2.max_concurrent_streams_in` and are maintained as such over the
4177+
lifetime of HTTP/2 connections. Stream windows are initialized to the value of
4178+
:ts:cv:`proxy.config.http2.initial_window_size_in` but are dynamically adjusted to the
4179+
session window size divided by the number of concurrent streams over the lifetime of HTTP/2
4180+
connections. That is, stream window sizes dynamically adjust to fill the session window in
4181+
a way that shares the window equally among all concurrent streams.
4182+
===== ===========================================================================================
41564183

41574184
.. ts:cv:: CONFIG proxy.config.http2.max_frame_size INT 16384
41584185
:reloadable:

mgmt/RecordsConfig.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,6 +1301,8 @@ static const RecordElement RecordsConfig[] =
13011301
,
13021302
{RECT_CONFIG, "proxy.config.http2.initial_window_size_in", RECD_INT, "65535", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
13031303
,
1304+
{RECT_CONFIG, "proxy.config.http2.flow_control.in.policy", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_STR, "[0-1]", RECA_NULL}
1305+
,
13041306
{RECT_CONFIG, "proxy.config.http2.max_frame_size", RECD_INT, "16384", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
13051307
,
13061308
{RECT_CONFIG, "proxy.config.http2.header_table_size", RECD_INT, "4096", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}

proxy/http2/HTTP2.cc

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -567,35 +567,36 @@ http2_decode_header_blocks(HTTPHdr *hdr, const uint8_t *buf_start, const uint32_
567567
}
568568

569569
// Initialize this subsystem with librecords configs (for now)
570-
uint32_t Http2::max_concurrent_streams_in = 100;
571-
uint32_t Http2::min_concurrent_streams_in = 10;
572-
uint32_t Http2::max_active_streams_in = 0;
573-
bool Http2::throttling = false;
574-
uint32_t Http2::stream_priority_enabled = 0;
575-
uint32_t Http2::initial_window_size = 65535;
576-
uint32_t Http2::max_frame_size = 16384;
577-
uint32_t Http2::header_table_size = 4096;
578-
uint32_t Http2::max_header_list_size = 4294967295;
579-
uint32_t Http2::accept_no_activity_timeout = 120;
580-
uint32_t Http2::no_activity_timeout_in = 120;
581-
uint32_t Http2::active_timeout_in = 0;
582-
uint32_t Http2::push_diary_size = 256;
583-
uint32_t Http2::zombie_timeout_in = 0;
584-
float Http2::stream_error_rate_threshold = 0.1;
585-
uint32_t Http2::stream_error_sampling_threshold = 10;
586-
uint32_t Http2::max_settings_per_frame = 7;
587-
uint32_t Http2::max_settings_per_minute = 14;
588-
uint32_t Http2::max_settings_frames_per_minute = 14;
589-
uint32_t Http2::max_ping_frames_per_minute = 60;
590-
uint32_t Http2::max_priority_frames_per_minute = 120;
591-
float Http2::min_avg_window_update = 2560.0;
592-
uint32_t Http2::con_slow_log_threshold = 0;
593-
uint32_t Http2::stream_slow_log_threshold = 0;
594-
uint32_t Http2::header_table_size_limit = 65536;
595-
uint32_t Http2::write_buffer_block_size = 262144;
596-
float Http2::write_size_threshold = 0.5;
597-
uint32_t Http2::write_time_threshold = 100;
598-
uint32_t Http2::buffer_water_mark = 0;
570+
uint32_t Http2::max_concurrent_streams_in = 100;
571+
uint32_t Http2::min_concurrent_streams_in = 10;
572+
uint32_t Http2::max_active_streams_in = 0;
573+
bool Http2::throttling = false;
574+
uint32_t Http2::stream_priority_enabled = 0;
575+
uint32_t Http2::initial_window_size_in = 65535;
576+
Http2FlowControlPolicy Http2::flow_control_policy_in = Http2FlowControlPolicy::STATIC_SESSION_AND_STATIC_STREAM;
577+
uint32_t Http2::max_frame_size = 16384;
578+
uint32_t Http2::header_table_size = 4096;
579+
uint32_t Http2::max_header_list_size = 4294967295;
580+
uint32_t Http2::accept_no_activity_timeout = 120;
581+
uint32_t Http2::no_activity_timeout_in = 120;
582+
uint32_t Http2::active_timeout_in = 0;
583+
uint32_t Http2::push_diary_size = 256;
584+
uint32_t Http2::zombie_timeout_in = 0;
585+
float Http2::stream_error_rate_threshold = 0.1;
586+
uint32_t Http2::stream_error_sampling_threshold = 10;
587+
uint32_t Http2::max_settings_per_frame = 7;
588+
uint32_t Http2::max_settings_per_minute = 14;
589+
uint32_t Http2::max_settings_frames_per_minute = 14;
590+
uint32_t Http2::max_ping_frames_per_minute = 60;
591+
uint32_t Http2::max_priority_frames_per_minute = 120;
592+
float Http2::min_avg_window_update = 2560.0;
593+
uint32_t Http2::con_slow_log_threshold = 0;
594+
uint32_t Http2::stream_slow_log_threshold = 0;
595+
uint32_t Http2::header_table_size_limit = 65536;
596+
uint32_t Http2::write_buffer_block_size = 262144;
597+
float Http2::write_size_threshold = 0.5;
598+
uint32_t Http2::write_time_threshold = 100;
599+
uint32_t Http2::buffer_water_mark = 0;
599600

600601
void
601602
Http2::init()
@@ -604,7 +605,16 @@ Http2::init()
604605
REC_EstablishStaticConfigInt32U(min_concurrent_streams_in, "proxy.config.http2.min_concurrent_streams_in");
605606
REC_EstablishStaticConfigInt32U(max_active_streams_in, "proxy.config.http2.max_active_streams_in");
606607
REC_EstablishStaticConfigInt32U(stream_priority_enabled, "proxy.config.http2.stream_priority_enabled");
607-
REC_EstablishStaticConfigInt32U(initial_window_size, "proxy.config.http2.initial_window_size_in");
608+
REC_EstablishStaticConfigInt32U(initial_window_size_in, "proxy.config.http2.initial_window_size_in");
609+
610+
uint32_t flow_control_policy_in_int = 0;
611+
REC_EstablishStaticConfigInt32U(flow_control_policy_in_int, "proxy.config.http2.flow_control.in.policy");
612+
if (flow_control_policy_in_int > 1) {
613+
Error("Invalid value for proxy.config.http2.flow_control.in.policy: %d", flow_control_policy_in_int);
614+
flow_control_policy_in_int = 0;
615+
}
616+
flow_control_policy_in = static_cast<Http2FlowControlPolicy>(flow_control_policy_in_int);
617+
608618
REC_EstablishStaticConfigInt32U(max_frame_size, "proxy.config.http2.max_frame_size");
609619
REC_EstablishStaticConfigInt32U(header_table_size, "proxy.config.http2.header_table_size");
610620
REC_EstablishStaticConfigInt32U(max_header_list_size, "proxy.config.http2.max_header_list_size");
@@ -632,7 +642,7 @@ Http2::init()
632642
// If any settings is broken, ATS should not start
633643
ink_release_assert(http2_settings_parameter_is_valid({HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, max_concurrent_streams_in}));
634644
ink_release_assert(http2_settings_parameter_is_valid({HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, min_concurrent_streams_in}));
635-
ink_release_assert(http2_settings_parameter_is_valid({HTTP2_SETTINGS_INITIAL_WINDOW_SIZE, initial_window_size}));
645+
ink_release_assert(http2_settings_parameter_is_valid({HTTP2_SETTINGS_INITIAL_WINDOW_SIZE, initial_window_size_in}));
636646
ink_release_assert(http2_settings_parameter_is_valid({HTTP2_SETTINGS_MAX_FRAME_SIZE, max_frame_size}));
637647
ink_release_assert(http2_settings_parameter_is_valid({HTTP2_SETTINGS_HEADER_TABLE_SIZE, header_table_size}));
638648
ink_release_assert(http2_settings_parameter_is_valid({HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, max_header_list_size}));

proxy/http2/HTTP2.h

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,15 @@
3131

3232
class HTTPHdr;
3333

34-
typedef unsigned Http2StreamId;
34+
// [RFC 9113] 5.1.1 Stream identifiers.
35+
using Http2StreamId = uint32_t;
3536

36-
constexpr Http2StreamId HTTP2_CONNECTION_CONTROL_STRTEAM = 0;
37-
constexpr uint8_t HTTP2_FRAME_NO_FLAG = 0;
37+
constexpr Http2StreamId HTTP2_CONNECTION_CONTROL_STREAM = 0;
38+
constexpr uint8_t HTTP2_FRAME_NO_FLAG = 0;
3839

3940
// [RFC 7540] 6.9.2. Initial Flow Control Window Size
4041
// the flow control window can be come negative so we need to track it with a signed type.
41-
typedef int32_t Http2WindowSize;
42+
using Http2WindowSize = int32_t;
4243

4344
extern const char *const HTTP2_CONNECTION_PREFACE;
4445
const size_t HTTP2_CONNECTION_PREFACE_LEN = 24;
@@ -360,6 +361,16 @@ ParseResult http2_convert_header_from_2_to_1_1(HTTPHdr *);
360361
ParseResult http2_convert_header_from_1_1_to_2(HTTPHdr *);
361362
void http2_init();
362363

364+
enum class Http2FlowControlPolicy {
365+
/** Both session and stream windows are maintained at their initial values
366+
* over the lifetime of the connection. */
367+
STATIC_SESSION_AND_STATIC_STREAM,
368+
369+
/** The session window is initialized to a large value while the stream
370+
* windows are dynamically adjusted to fill the session window. */
371+
LARGE_SESSION_DYNAMIC_STREAMS,
372+
};
373+
363374
// Not sure where else to put this, but figure this is as good of a start as
364375
// anything else.
365376
// Right now, only the static init() is available, which sets up some basic
@@ -373,7 +384,8 @@ class Http2
373384
static uint32_t max_active_streams_in;
374385
static bool throttling;
375386
static uint32_t stream_priority_enabled;
376-
static uint32_t initial_window_size;
387+
static uint32_t initial_window_size_in;
388+
static Http2FlowControlPolicy flow_control_policy_in;
377389
static uint32_t max_frame_size;
378390
static uint32_t header_table_size;
379391
static uint32_t max_header_list_size;

0 commit comments

Comments
 (0)