diff --git a/net/spdy/spdy_http_stream.cc b/net/spdy/spdy_http_stream.cc index 14a3188dd0628..80dd1be401981 100644 --- a/net/spdy/spdy_http_stream.cc +++ b/net/spdy/spdy_http_stream.cc @@ -542,11 +542,21 @@ void SpdyHttpStream::DoBufferedReadCallback() { return; if (!response_body_queue_.IsEmpty()) { + // SpdyReadQueue::Dequeue fires SpdyBuffer ConsumeCallbacks which can + // re-enter the network stack and synchronously destroy |this| (e.g. via + // SpdySession::DoDrainSession -> OnClose -> DoResponseCallback -> + // ~HttpNetworkTransaction). + base::WeakPtr self = weak_factory_.GetWeakPtr(); int rv = response_body_queue_.Dequeue(user_buffer_->first(user_buffer_len_)); + if (!self) { + return; + } user_buffer_ = nullptr; user_buffer_len_ = 0; - DoResponseCallback(rv); + if (response_callback_) { + DoResponseCallback(rv); + } return; } diff --git a/net/spdy/spdy_read_queue.cc b/net/spdy/spdy_read_queue.cc index 9e97b0a72e4d5..209b9a93ab000 100644 --- a/net/spdy/spdy_read_queue.cc +++ b/net/spdy/spdy_read_queue.cc @@ -36,18 +36,32 @@ void SpdyReadQueue::Enqueue(std::unique_ptr buffer) { size_t SpdyReadQueue::Dequeue(base::span out) { DCHECK_GT(out.size(), 0u); size_t bytes_copied = 0; + // ~SpdyBuffer / SpdyBuffer::Consume run ConsumeCallbacks that can re-enter + // arbitrary network-stack code (session WINDOW_UPDATE -> DoDrainSession) and + // synchronously destroy the SpdyHttpStream that owns |this|. Defer firing + // them until after we are done touching member state. + std::vector> to_discard; + size_t to_consume = 0; while (!queue_.empty() && !out.empty()) { SpdyBuffer* buffer = queue_.front().get(); size_t bytes_to_copy = std::min(out.size(), buffer->GetRemainingSize()); out.take_first(bytes_to_copy) .copy_from(buffer->GetRemaining().first(bytes_to_copy)); bytes_copied += bytes_to_copy; - if (bytes_to_copy == buffer->GetRemainingSize()) + if (bytes_to_copy == buffer->GetRemainingSize()) { + to_discard.push_back(std::move(queue_.front())); queue_.pop_front(); - else - buffer->Consume(bytes_to_copy); + } else { + to_consume = bytes_to_copy; + break; + } } total_size_ -= bytes_copied; + if (to_consume) { + // May delete |this|. + queue_.front()->Consume(to_consume); + } + // |to_discard| destruction may delete |this|; do not touch members past here. return bytes_copied; }