Skip to content

Commit 522ba23

Browse files
masaori335bryancall
authored andcommitted
Enhancement to h2 buffering
1 parent 15e749e commit 522ba23

File tree

9 files changed

+94
-28
lines changed

9 files changed

+94
-28
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3694,7 +3694,7 @@ HTTP/2 Configuration
36943694
:ts:cv:`proxy.config.http2.min_concurrent_streams_in`.
36953695
To disable, set to zero (``0``).
36963696

3697-
.. ts:cv:: CONFIG proxy.config.http2.initial_window_size_in INT 1048576
3697+
.. ts:cv:: CONFIG proxy.config.http2.initial_window_size_in INT 65535
36983698
:reloadable:
36993699

37003700
The initial window size for inbound connections.
@@ -3824,7 +3824,6 @@ HTTP/2 Configuration
38243824
Clients that send smaller window increments lower than this limit will be immediately disconnected with an error
38253825
code of ENHANCE_YOUR_CALM.
38263826

3827-
38283827
HTTP/3 Configuration
38293828
====================
38303829

mgmt/RecordsConfig.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1291,7 +1291,7 @@ static const RecordElement RecordsConfig[] =
12911291
,
12921292
{RECT_CONFIG, "proxy.config.http2.max_active_streams_in", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
12931293
,
1294-
{RECT_CONFIG, "proxy.config.http2.initial_window_size_in", RECD_INT, "1048576", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
1294+
{RECT_CONFIG, "proxy.config.http2.initial_window_size_in", RECD_INT, "65535", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
12951295
,
12961296
{RECT_CONFIG, "proxy.config.http2.max_frame_size", RECD_INT, "16384", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
12971297
,

proxy/http2/HTTP2.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,7 @@ uint32_t Http2::min_concurrent_streams_in = 10;
712712
uint32_t Http2::max_active_streams_in = 0;
713713
bool Http2::throttling = false;
714714
uint32_t Http2::stream_priority_enabled = 0;
715-
uint32_t Http2::initial_window_size = 1048576;
715+
uint32_t Http2::initial_window_size = 65535;
716716
uint32_t Http2::max_frame_size = 16384;
717717
uint32_t Http2::header_table_size = 4096;
718718
uint32_t Http2::max_header_list_size = 4294967295;

proxy/http2/Http2ClientSession.cc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -385,11 +385,9 @@ Http2ClientSession::main_event_handler(int event, void *edata)
385385
break;
386386

387387
case VC_EVENT_WRITE_READY:
388-
retval = 0;
389-
break;
390-
391388
case VC_EVENT_WRITE_COMPLETE:
392-
// Seems as this is being closed already
389+
this->connection_state.restart_streams();
390+
393391
retval = 0;
394392
break;
395393

@@ -678,6 +676,12 @@ Http2ClientSession::_should_do_something_else()
678676
return (this->_n_frame_read & 0x7F) == 0;
679677
}
680678

679+
int64_t
680+
Http2ClientSession::write_avail()
681+
{
682+
return this->write_buffer->write_avail();
683+
}
684+
681685
NetVConnection *
682686
Http2ClientSession::get_netvc() const
683687
{

proxy/http2/Http2ClientSession.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ class Http2ClientSession : public ProxySession
134134
// Record history from Http2ConnectionState
135135
void remember(const SourceLocation &location, int event, int reentrant = NO_REENTRANT);
136136

137+
int64_t write_avail();
138+
137139
// noncopyable
138140
Http2ClientSession(Http2ClientSession &) = delete;
139141
Http2ClientSession &operator=(const Http2ClientSession &) = delete;

proxy/http2/Http2ConnectionState.cc

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,12 @@ rcv_data_frame(Http2ConnectionState &cstate, const Http2Frame &frame)
166166
cstate.decrement_server_rwnd(payload_length);
167167
stream->decrement_server_rwnd(payload_length);
168168

169+
if (is_debug_tag_set("http2_con")) {
170+
uint32_t rwnd = cstate.server_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE);
171+
Http2StreamDebug(cstate.ua_session, id, "Received DATA frame: rwnd con=%zd/%" PRId32 " stream=%zd/%" PRId32,
172+
cstate.server_rwnd(), rwnd, stream->server_rwnd(), rwnd);
173+
}
174+
169175
const uint32_t unpadded_length = payload_length - pad_length;
170176
MIOBuffer *writer = stream->read_vio_writer();
171177
if (writer == nullptr) {
@@ -202,21 +208,6 @@ rcv_data_frame(Http2ConnectionState &cstate, const Http2Frame &frame)
202208
stream->signal_read_event(VC_EVENT_READ_READY);
203209
}
204210

205-
uint32_t initial_rwnd = cstate.server_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE);
206-
uint32_t min_rwnd = std::min(initial_rwnd, cstate.server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE));
207-
// Connection level WINDOW UPDATE
208-
if (cstate.server_rwnd() <= min_rwnd) {
209-
Http2WindowSize diff_size = initial_rwnd - cstate.server_rwnd();
210-
cstate.increment_server_rwnd(diff_size);
211-
cstate.send_window_update_frame(0, diff_size);
212-
}
213-
// Stream level WINDOW UPDATE
214-
if (stream->server_rwnd() <= min_rwnd) {
215-
Http2WindowSize diff_size = initial_rwnd - stream->server_rwnd();
216-
stream->increment_server_rwnd(diff_size);
217-
cstate.send_window_update_frame(stream->get_id(), diff_size);
218-
}
219-
220211
return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_NONE);
221212
}
222213

@@ -1288,6 +1279,35 @@ Http2ConnectionState::restart_streams()
12881279
}
12891280
}
12901281

1282+
void
1283+
Http2ConnectionState::restart_receiving(Http2Stream *stream)
1284+
{
1285+
uint32_t initial_rwnd = this->server_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE);
1286+
uint32_t min_rwnd = std::min(initial_rwnd, this->server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE));
1287+
1288+
// Connection level WINDOW UPDATE
1289+
if (this->server_rwnd() < min_rwnd) {
1290+
Http2WindowSize diff_size = initial_rwnd - this->server_rwnd();
1291+
this->increment_server_rwnd(diff_size);
1292+
this->send_window_update_frame(0, diff_size);
1293+
}
1294+
1295+
// Stream level WINDOW UPDATE
1296+
if (stream == nullptr || stream->server_rwnd() >= min_rwnd) {
1297+
return;
1298+
}
1299+
1300+
// If read_vio is buffering data, do not fully update window
1301+
int64_t data_size = stream->read_vio_read_avail();
1302+
if (data_size >= initial_rwnd) {
1303+
return;
1304+
}
1305+
1306+
Http2WindowSize diff_size = initial_rwnd - std::max(static_cast<int64_t>(stream->server_rwnd()), data_size);
1307+
stream->increment_server_rwnd(diff_size);
1308+
this->send_window_update_frame(stream->get_id(), diff_size);
1309+
}
1310+
12911311
void
12921312
Http2ConnectionState::cleanup_streams()
12931313
{
@@ -1497,6 +1517,12 @@ Http2ConnectionState::send_a_data_frame(Http2Stream *stream, size_t &payload_len
14971517
return Http2SendDataFrameResult::ERROR;
14981518
}
14991519

1520+
SCOPED_MUTEX_LOCK(lock, this->ua_session->mutex, this_ethread());
1521+
if (this->ua_session->write_avail() == 0) {
1522+
Http2StreamDebug(this->ua_session, stream->get_id(), "Not write avail");
1523+
return Http2SendDataFrameResult::NOT_WRITE_AVAIL;
1524+
}
1525+
15001526
// Select appropriate payload length
15011527
if (resp_reader->is_read_avail_more_than(0)) {
15021528
// We only need to check for window size when there is a payload
@@ -1872,7 +1898,7 @@ Http2ConnectionState::send_goaway_frame(Http2StreamId id, Http2ErrorCode ec)
18721898
void
18731899
Http2ConnectionState::send_window_update_frame(Http2StreamId id, uint32_t size)
18741900
{
1875-
Http2StreamDebug(ua_session, id, "Send WINDOW_UPDATE frame");
1901+
Http2StreamDebug(ua_session, id, "Send WINDOW_UPDATE frame: size=%" PRIu32, size);
18761902

18771903
// Create WINDOW_UPDATE frame
18781904
Http2WindowUpdateFrame window_update(id, size);

proxy/http2/Http2ConnectionState.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ enum class Http2SendDataFrameResult {
3939
NO_ERROR = 0,
4040
NO_WINDOW,
4141
NO_PAYLOAD,
42+
NOT_WRITE_AVAIL,
4243
ERROR,
4344
DONE,
4445
};
@@ -134,6 +135,8 @@ class Http2ConnectionState : public Continuation
134135
void
135136
init()
136137
{
138+
this->_server_rwnd = Http2::initial_window_size;
139+
137140
local_hpack_handle = new HpackHandle(HTTP2_HEADER_TABLE_SIZE);
138141
remote_hpack_handle = new HpackHandle(HTTP2_HEADER_TABLE_SIZE);
139142
if (Http2::stream_priority_enabled) {
@@ -190,7 +193,7 @@ class Http2ConnectionState : public Continuation
190193
bool delete_stream(Http2Stream *stream);
191194
void release_stream();
192195
void cleanup_streams();
193-
196+
void restart_receiving(Http2Stream *stream);
194197
void update_initial_rwnd(Http2WindowSize new_size);
195198

196199
Http2StreamId
@@ -377,7 +380,7 @@ class Http2ConnectionState : public Continuation
377380

378381
// Connection level window size
379382
ssize_t _client_rwnd = HTTP2_INITIAL_WINDOW_SIZE;
380-
ssize_t _server_rwnd = Http2::initial_window_size;
383+
ssize_t _server_rwnd = 0;
381384

382385
std::vector<size_t> _recent_rwnd_increment = {SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX};
383386
int _recent_rwnd_increment_index = 0;

proxy/http2/Http2Stream.cc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ Http2Stream::init(Http2StreamId sid, ssize_t initial_rwnd)
5252
this->_id = sid;
5353
this->_thread = this_ethread();
5454
this->_client_rwnd = initial_rwnd;
55+
this->_server_rwnd = Http2::initial_window_size;
5556

5657
this->_reader = this->_request_buffer.alloc_reader();
5758
// FIXME: Are you sure? every "stream" needs request_header?
@@ -519,6 +520,19 @@ Http2Stream::update_read_request(int64_t read_len, bool call_update, bool check_
519520
void
520521
Http2Stream::restart_sending()
521522
{
523+
if (!this->response_header_done) {
524+
return;
525+
}
526+
527+
IOBufferReader *reader = this->response_get_data_reader();
528+
if (reader && !reader->is_read_avail_more_than(0)) {
529+
return;
530+
}
531+
532+
if (this->write_vio.mutex && this->write_vio.ntodo() == 0) {
533+
return;
534+
}
535+
522536
this->send_response_body(true);
523537
}
524538

@@ -719,6 +733,12 @@ Http2Stream::reenable(VIO *vio)
719733
SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
720734
update_write_request(vio->get_reader(), INT64_MAX, true);
721735
} else if (vio->op == VIO::READ) {
736+
Http2ClientSession *h2_proxy_ssn = static_cast<Http2ClientSession *>(this->_proxy_ssn);
737+
{
738+
SCOPED_MUTEX_LOCK(ssn_lock, h2_proxy_ssn->connection_state.mutex, this_ethread());
739+
h2_proxy_ssn->connection_state.restart_receiving(this);
740+
}
741+
722742
SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
723743
update_read_request(INT64_MAX, true);
724744
}
@@ -979,3 +999,14 @@ Http2Stream::get_transaction_priority_dependence() const
979999
return priority_node->parent ? priority_node->parent->id : 0;
9801000
}
9811001
}
1002+
1003+
int64_t
1004+
Http2Stream::read_vio_read_avail()
1005+
{
1006+
MIOBuffer *writer = this->read_vio.get_writer();
1007+
if (writer) {
1008+
return writer->max_read_avail();
1009+
}
1010+
1011+
return 0;
1012+
}

proxy/http2/Http2Stream.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ class Http2Stream : public ProxyTransaction
128128
bool has_trailing_header() const;
129129
void set_request_headers(HTTPHdr &h2_headers);
130130
MIOBuffer *read_vio_writer() const;
131+
int64_t read_vio_read_avail();
131132

132133
//////////////////
133134
// Variables
@@ -204,8 +205,8 @@ class Http2Stream : public ProxyTransaction
204205
uint64_t data_length = 0;
205206
uint64_t bytes_sent = 0;
206207

207-
ssize_t _client_rwnd;
208-
ssize_t _server_rwnd = Http2::initial_window_size;
208+
ssize_t _client_rwnd = 0;
209+
ssize_t _server_rwnd = 0;
209210

210211
std::vector<size_t> _recent_rwnd_increment = {SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX};
211212
int _recent_rwnd_increment_index = 0;

0 commit comments

Comments
 (0)