net/quic: complete Fast Open response handling in OnIOComplete
With proxy Fast Open, Connect() can complete before the CONNECT response arrives; the response then ran through OnIOComplete() into the DoLoop() STATE_PROCESS_RESPONSE_CODE fastopen block, which force-returned ERR_IO_PENDING. An async header-read failure left the consumed connect callback to be run with an error (DCHECK in debug, null run in release), and a pending application read was never completed. Track the pending header read with std::optional<bool> read_headers_pending_ (nullopt until Fast Open engages, false once the read completes) and clear it in OnReadResponseHeadersComplete(). Remove the STATE_PROCESS_RESPONSE_CODE block and handle the response result in OnIOComplete(): on failure transition to STATE_DISCONNECTED and complete a pending application read with the actual error; on success leave the pending read to the data path.pull/826/head
parent
bfdd87ce08
commit
2e7fbdbb18
|
|
@ -47,7 +47,6 @@ QuicProxyClientSocket::QuicProxyClientSocket(
|
|||
proxy_delegate_(proxy_delegate),
|
||||
user_agent_(user_agent),
|
||||
use_fastopen_(false),
|
||||
read_headers_pending_(false),
|
||||
net_log_(net_log) {
|
||||
DCHECK(stream_->IsOpen());
|
||||
|
||||
|
|
@ -281,6 +280,13 @@ void QuicProxyClientSocket::OnIOComplete(int result) {
|
|||
DCHECK_NE(STATE_DISCONNECTED, next_state_);
|
||||
int rv = DoLoop(result);
|
||||
if (rv != ERR_IO_PENDING) {
|
||||
if (use_fastopen_ && read_headers_pending_ == false) {
|
||||
if (rv != OK)
|
||||
next_state_ = STATE_DISCONNECTED;
|
||||
if (read_callback_ && rv != OK)
|
||||
std::move(read_callback_).Run(rv);
|
||||
return;
|
||||
}
|
||||
// Connect() finished (successfully or unsuccessfully).
|
||||
DCHECK(!connect_callback_.is_null());
|
||||
std::move(connect_callback_).Run(rv);
|
||||
|
|
@ -326,23 +332,6 @@ int QuicProxyClientSocket::DoLoop(int last_io_result) {
|
|||
rv = DoReadReplyComplete(rv);
|
||||
net_log_.EndEventWithNetErrorCode(
|
||||
NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv);
|
||||
// If reading the response itself fails after Fast Open returned from
|
||||
// Connect(), there is no connect_callback_ left to invoke. Successful
|
||||
// responses continue through the normal response-processing states;
|
||||
// HTTP status failures are handled in STATE_PROCESS_RESPONSE_CODE.
|
||||
if (use_fastopen_ && read_headers_pending_ && rv < 0) {
|
||||
read_headers_pending_ = false;
|
||||
// The pending application read observes the closed stream. Any
|
||||
// subsequent data after this response must be ignored.
|
||||
next_state_ = STATE_DISCONNECTED;
|
||||
// A Fast Open Connect() can complete before the app issues a
|
||||
// Read(); a pending read must observe this failure instead of
|
||||
// waiting for a stream close that may never come.
|
||||
FailPendingReadOnFastOpenFailure(rv);
|
||||
// The Fast Open Connect() already completed; do not report this
|
||||
// response through connect_callback_.
|
||||
rv = ERR_IO_PENDING;
|
||||
}
|
||||
break;
|
||||
case STATE_PROCESS_RESPONSE_HEADERS:
|
||||
DCHECK_EQ(OK, rv);
|
||||
|
|
@ -354,21 +343,6 @@ int QuicProxyClientSocket::DoLoop(int last_io_result) {
|
|||
case STATE_PROCESS_RESPONSE_CODE:
|
||||
DCHECK_EQ(OK, rv);
|
||||
rv = DoProcessResponseCode();
|
||||
if (use_fastopen_ && read_headers_pending_) {
|
||||
read_headers_pending_ = false;
|
||||
if (rv < 0) {
|
||||
// The pending application read observes the closed stream. Any
|
||||
// subsequent data after this response must be ignored.
|
||||
next_state_ = STATE_DISCONNECTED;
|
||||
// A Fast Open Connect() can complete before the app issues a
|
||||
// Read(); a pending read must observe this failure instead of
|
||||
// waiting for a stream close that may never come.
|
||||
FailPendingReadOnFastOpenFailure(rv);
|
||||
}
|
||||
// Fast Open already completed Connect(); do not invoke the
|
||||
// consumed connect_callback_ for the later response.
|
||||
rv = ERR_IO_PENDING;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
NOTREACHED() << "bad state";
|
||||
|
|
@ -602,6 +576,7 @@ int QuicProxyClientSocket::DoProcessResponseCode() {
|
|||
void QuicProxyClientSocket::OnReadResponseHeadersComplete(int result) {
|
||||
// Convert the now-populated quiche::HttpHeaderBlock to HttpResponseInfo
|
||||
if (use_fastopen_ && read_headers_pending_) {
|
||||
read_headers_pending_ = false;
|
||||
if (next_state_ == STATE_DISCONNECTED)
|
||||
return;
|
||||
if (next_state_ == STATE_CONNECT_COMPLETE)
|
||||
|
|
@ -624,19 +599,6 @@ int QuicProxyClientSocket::ProcessResponseHeaders(
|
|||
return OK;
|
||||
}
|
||||
|
||||
void QuicProxyClientSocket::FailPendingReadOnFastOpenFailure(int error) {
|
||||
if (read_callback_.is_null()) {
|
||||
// No pending application read; still cancel the response stream so a
|
||||
// late body cannot be delivered after the failed CONNECT response.
|
||||
stream_->Reset(quic::QUIC_STREAM_CANCELLED);
|
||||
return;
|
||||
}
|
||||
read_buf_ = nullptr;
|
||||
stream_->Reset(quic::QUIC_STREAM_CANCELLED);
|
||||
// May destroy |this|; run last and use no members afterwards.
|
||||
std::move(read_callback_).Run(error);
|
||||
}
|
||||
|
||||
void QuicProxyClientSocket::OnBeforeTunnelRequestComplete(
|
||||
base::expected<HttpRequestHeaders, Error> result) {
|
||||
if (result.has_value()) {
|
||||
|
|
|
|||
|
|
@ -106,16 +106,6 @@ class NET_EXPORT_PRIVATE QuicProxyClientSocket : public ProxyClientSocket {
|
|||
void OnReadResponseHeadersComplete(int result);
|
||||
int ProcessResponseHeaders(const quiche::HttpHeaderBlock& headers);
|
||||
|
||||
// Fast Open async failure path: a Fast Open Connect() can complete before
|
||||
// the app has issued a Read(); when the CONNECT response then fails
|
||||
// asynchronously, a pending application Read() must observe the failure
|
||||
// instead of waiting indefinitely for a stream close that may never come.
|
||||
// Must be called after next_state_ is set to STATE_DISCONNECTED. The read
|
||||
// callback may destroy |this|, so DoLoop must not touch members afterwards
|
||||
// (its loop condition short-circuits on the ERR_IO_PENDING the caller sets
|
||||
// next).
|
||||
void FailPendingReadOnFastOpenFailure(int error);
|
||||
|
||||
// Callback for proxy_delegate_->OnBeforeTunnelRequest().
|
||||
void OnBeforeTunnelRequestComplete(
|
||||
base::expected<HttpRequestHeaders, Error> result);
|
||||
|
|
@ -179,7 +169,7 @@ class NET_EXPORT_PRIVATE QuicProxyClientSocket : public ProxyClientSocket {
|
|||
|
||||
bool use_fastopen_;
|
||||
std::optional<size_t> preamble_index_;
|
||||
bool read_headers_pending_;
|
||||
std::optional<bool> read_headers_pending_;
|
||||
|
||||
const NetLogWithSource net_log_;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue