Skip to content

Commit 1889fe9

Browse files
shinrichGitHub Enterprise
authored andcommitted
Merge pull request apache#41 from shinrich/fix-stale-stream
Avoid using deleted stream after send data frames
2 parents 6b979e3 + ac4cf2e commit 1889fe9

File tree

4 files changed

+40
-28
lines changed

4 files changed

+40
-28
lines changed

proxy/http2/Http2ConnectionState.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1438,14 +1438,16 @@ Http2ConnectionState::send_a_data_frame(Http2Stream *stream, size_t &payload_len
14381438
}
14391439

14401440
void
1441-
Http2ConnectionState::send_data_frames(Http2Stream *stream)
1441+
Http2ConnectionState::send_data_frames(Http2Stream *stream, bool &stream_deleted)
14421442
{
1443+
stream_deleted = false;
14431444
// To follow RFC 7540 must not send more frames other than priority on
14441445
// a closed stream. So we return without sending
14451446
if (stream->get_state() == Http2StreamState::HTTP2_STREAM_STATE_HALF_CLOSED_LOCAL ||
14461447
stream->get_state() == Http2StreamState::HTTP2_STREAM_STATE_CLOSED) {
14471448
Http2StreamDebug(this->ua_session, stream->get_id(), "Shutdown half closed local stream");
14481449
this->delete_stream(stream);
1450+
stream_deleted = true;
14491451
return;
14501452
}
14511453

@@ -1461,6 +1463,7 @@ Http2ConnectionState::send_data_frames(Http2Stream *stream)
14611463
// See 'closed' state written at [RFC 7540] 5.1.
14621464
Http2StreamDebug(this->ua_session, stream->get_id(), "Shutdown stream");
14631465
this->delete_stream(stream);
1466+
stream_deleted = true;
14641467
}
14651468
}
14661469

proxy/http2/Http2ConnectionState.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ class Http2ConnectionState : public Continuation
257257
// HTTP/2 frame sender
258258
void schedule_stream(Http2Stream *stream);
259259
void send_data_frames_depends_on_priority();
260-
void send_data_frames(Http2Stream *stream);
260+
void send_data_frames(Http2Stream *stream, bool &stream_deleted);
261261
Http2SendDataFrameResult send_a_data_frame(Http2Stream *stream, size_t &payload_length);
262262
void send_headers_frame(Http2Stream *stream);
263263
void send_push_promise_frame(Http2Stream *stream, URL &url, const MIMEField *accept_encoding);

proxy/http2/Http2Stream.cc

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -328,15 +328,16 @@ Http2Stream::do_io_close(int /* flags */)
328328
// by the time this is called from transaction_done.
329329
closed = true;
330330

331+
clear_timers();
332+
clear_io_events();
333+
334+
bool stream_deleted = false;
331335
if (parent && this->is_client_state_writeable()) {
332336
// Make sure any trailing end of stream frames are sent
333337
// Wee will be removed at send_data_frames or closing connection phase
334-
static_cast<Http2ClientSession *>(parent)->connection_state.send_data_frames(this);
338+
static_cast<Http2ClientSession *>(parent)->connection_state.send_data_frames(this, stream_deleted);
335339
}
336340

337-
clear_timers();
338-
clear_io_events();
339-
340341
// Wait until transaction_done is called from HttpSM to signal that the TXN_CLOSE hook has been executed
341342
}
342343
}
@@ -517,8 +518,9 @@ Http2Stream::update_read_request(int64_t read_len, bool call_update, bool check_
517518
void
518519
Http2Stream::restart_sending()
519520
{
520-
send_response_body();
521-
if (this->write_vio.ntodo() > 0 && this->write_vio.get_writer()->write_avail() > 0) {
521+
bool stream_deleted;
522+
send_response_body(stream_deleted);
523+
if (!stream_deleted && this->write_vio.ntodo() > 0 && this->write_vio.get_writer()->write_avail() > 0) {
522524
write_vio._cont->handleEvent(VC_EVENT_WRITE_READY, &write_vio);
523525
}
524526
}
@@ -569,6 +571,7 @@ Http2Stream::update_write_request(IOBufferReader *buf_reader, int64_t write_len,
569571
write_vio.nbytes, write_vio.ndone, write_vio.get_writer()->write_avail(), bytes_avail);
570572

571573
if (bytes_avail > 0 || is_done) {
574+
bool stream_deleted;
572575
int send_event = (write_vio.ntodo() == bytes_avail || is_done) ? VC_EVENT_WRITE_COMPLETE : VC_EVENT_WRITE_READY;
573576

574577
// Process the new data
@@ -613,18 +616,20 @@ Http2Stream::update_write_request(IOBufferReader *buf_reader, int64_t write_len,
613616
// make sure to send the end of stream
614617
if (this->response_is_data_available() || send_event == VC_EVENT_WRITE_COMPLETE) {
615618
if (send_event != VC_EVENT_WRITE_COMPLETE) {
616-
send_response_body();
617-
// As with update_read_request, should be safe to call handler directly here if
618-
// call_update is true. Commented out for now while tracking a performance regression
619-
if (call_update) { // Coming from reenable. Safe to call the handler directly
620-
if (write_vio._cont && this->current_reader)
621-
write_vio._cont->handleEvent(send_event, &write_vio);
622-
} else { // Called from do_io_write. Might still be setting up state. Send an event to let the dust settle
623-
write_event = send_tracked_event(write_event, send_event, &write_vio);
619+
send_response_body(stream_deleted);
620+
if (!stream_deleted) {
621+
// As with update_read_request, should be safe to call handler directly here if
622+
// call_update is true. Commented out for now while tracking a performance regression
623+
if (call_update) { // Coming from reenable. Safe to call the handler directly
624+
if (write_vio._cont && this->current_reader)
625+
write_vio._cont->handleEvent(send_event, &write_vio);
626+
} else { // Called from do_io_write. Might still be setting up state. Send an event to let the dust settle
627+
write_event = send_tracked_event(write_event, send_event, &write_vio);
628+
}
624629
}
625630
} else {
626631
this->mark_body_done();
627-
send_response_body();
632+
send_response_body(stream_deleted);
628633
}
629634
}
630635
break;
@@ -640,15 +645,17 @@ Http2Stream::update_write_request(IOBufferReader *buf_reader, int64_t write_len,
640645
// Defer sending the write complete until the send_data_frame has sent it all
641646
// this_ethread()->schedule_imm(this, send_event, &write_vio);
642647
this->mark_body_done();
643-
send_response_body();
648+
send_response_body(stream_deleted);
644649
retval = false;
645650
} else {
646-
send_response_body();
647-
if (call_update) { // Coming from reenable. Safe to call the handler directly
648-
if (write_vio._cont && this->current_reader)
649-
write_vio._cont->handleEvent(send_event, &write_vio);
650-
} else { // Called from do_io_write. Might still be setting up state. Send an event to let the dust settle
651-
write_event = send_tracked_event(write_event, send_event, &write_vio);
651+
send_response_body(stream_deleted);
652+
if (!stream_deleted) {
653+
if (call_update) { // Coming from reenable. Safe to call the handler directly
654+
if (write_vio._cont && this->current_reader)
655+
write_vio._cont->handleEvent(send_event, &write_vio);
656+
} else { // Called from do_io_write. Might still be setting up state. Send an event to let the dust settle
657+
write_event = send_tracked_event(write_event, send_event, &write_vio);
658+
}
652659
}
653660
}
654661
}
@@ -667,18 +674,20 @@ Http2Stream::push_promise(URL &url, const MIMEField *accept_encoding)
667674
}
668675

669676
void
670-
Http2Stream::send_response_body()
677+
Http2Stream::send_response_body(bool &stream_deleted)
671678
{
679+
stream_deleted = false;
672680
Http2ClientSession *parent = static_cast<Http2ClientSession *>(this->get_parent());
673681

682+
inactive_timeout_at = Thread::get_hrtime() + inactive_timeout;
683+
674684
if (Http2::stream_priority_enabled) {
675685
SCOPED_MUTEX_LOCK(lock, parent->connection_state.mutex, this_ethread());
676686
parent->connection_state.schedule_stream(this);
677687
} else {
678688
SCOPED_MUTEX_LOCK(lock, parent->connection_state.mutex, this_ethread());
679-
parent->connection_state.send_data_frames(this);
689+
parent->connection_state.send_data_frames(this, stream_deleted);
680690
}
681-
inactive_timeout_at = Thread::get_hrtime() + inactive_timeout;
682691
}
683692

684693
void

proxy/http2/Http2Stream.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ class Http2Stream : public ProxyClientTransaction
189189
void transaction_done() override;
190190

191191
void restart_sending();
192-
void send_response_body();
192+
void send_response_body(bool &stream_deleted);
193193
void push_promise(URL &url, const MIMEField *accept_encoding);
194194

195195
// Stream level window size

0 commit comments

Comments
 (0)