Chromium Code Reviews| Index: net/spdy/bidirectional_stream_spdy_impl.cc |
| diff --git a/net/spdy/bidirectional_stream_spdy_impl.cc b/net/spdy/bidirectional_stream_spdy_impl.cc |
| index 44ccc3a32872c6ba07e8b46183b392175861935d..61d337d34a7f17abd384b9218e51ee2010878f1b 100644 |
| --- a/net/spdy/bidirectional_stream_spdy_impl.cc |
| +++ b/net/spdy/bidirectional_stream_spdy_impl.cc |
| @@ -34,6 +34,8 @@ BidirectionalStreamSpdyImpl::BidirectionalStreamSpdyImpl( |
| negotiated_protocol_(kProtoUnknown), |
| more_read_data_pending_(false), |
| read_buffer_len_(0), |
| + written_end_of_stream_(false), |
| + write_pending_(false), |
| stream_closed_(false), |
| closed_stream_status_(ERR_FAILED), |
| closed_stream_received_bytes_(0), |
| @@ -44,6 +46,7 @@ BidirectionalStreamSpdyImpl::BidirectionalStreamSpdyImpl( |
| BidirectionalStreamSpdyImpl::~BidirectionalStreamSpdyImpl() { |
| // Sends a RST to the remote if the stream is destroyed before it completes. |
| ResetStream(); |
| + DCHECK(!write_pending_); |
| } |
| void BidirectionalStreamSpdyImpl::Start( |
| @@ -107,16 +110,17 @@ void BidirectionalStreamSpdyImpl::SendData(const scoped_refptr<IOBuffer>& data, |
| int length, |
| bool end_stream) { |
| DCHECK(length > 0 || (length == 0 && end_stream)); |
| + DCHECK(!write_pending_); |
|
kapishnikov
2016/10/31 17:03:07
Should we add DCHECK(!written_end_of_stream_) here
xunjieli
2016/10/31 17:18:03
The newly added test will crash instead of having
|
| - if (!stream_) { |
| - LOG(ERROR) << "Trying to send data after stream has been destroyed."; |
| - base::ThreadTaskRunnerHandle::Get()->PostTask( |
| - FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::NotifyError, |
| - weak_factory_.GetWeakPtr(), ERR_UNEXPECTED)); |
| + write_pending_ = true; |
| + if (MaybeHandleStreamClosedInSendData()) { |
| + written_end_of_stream_ = end_stream; |
| return; |
| } |
| DCHECK(!stream_closed_); |
| + written_end_of_stream_ = end_stream; |
| + |
| stream_->SendData(data.get(), length, |
| end_stream ? NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND); |
| } |
| @@ -126,16 +130,17 @@ void BidirectionalStreamSpdyImpl::SendvData( |
| const std::vector<int>& lengths, |
| bool end_stream) { |
| DCHECK_EQ(buffers.size(), lengths.size()); |
| + DCHECK(!write_pending_); |
|
kapishnikov
2016/10/31 17:03:07
Same DCHECK(!written_end_of_stream_) here
xunjieli
2016/10/31 17:18:02
Same as above. The newly added test will crash. We
|
| - if (!stream_) { |
| - LOG(ERROR) << "Trying to send data after stream has been destroyed."; |
| - base::ThreadTaskRunnerHandle::Get()->PostTask( |
| - FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::NotifyError, |
| - weak_factory_.GetWeakPtr(), ERR_UNEXPECTED)); |
| + write_pending_ = true; |
| + if (MaybeHandleStreamClosedInSendData()) { |
| + written_end_of_stream_ = end_stream; |
| return; |
| } |
| DCHECK(!stream_closed_); |
| + written_end_of_stream_ = end_stream; |
| + |
| int total_len = 0; |
| for (int len : lengths) { |
| total_len += len; |
| @@ -268,7 +273,13 @@ void BidirectionalStreamSpdyImpl::OnClose(int status) { |
| // If user has not called ReadData (i.e |read_buffer_| is nullptr), this will |
| // do nothing. |
| timer_->Stop(); |
| + |
| + // |this| might get destroyed after calling into |delegate_| in |
| + // DoBufferedRead(). |
| + auto weak_this = weak_factory_.GetWeakPtr(); |
| DoBufferedRead(); |
| + if (weak_this.get() && write_pending_) |
| + NotifyDataSent(); |
| } |
| int BidirectionalStreamSpdyImpl::SendRequestHeadersHelper() { |
| @@ -280,6 +291,7 @@ int BidirectionalStreamSpdyImpl::SendRequestHeadersHelper() { |
| CreateSpdyHeadersFromHttpRequest( |
| http_request_info, http_request_info.extra_headers, true, &headers); |
| + written_end_of_stream_ = request_info_->end_stream_on_headers; |
| return stream_->SendRequestHeaders(std::move(headers), |
| request_info_->end_stream_on_headers |
| ? NO_MORE_DATA_TO_SEND |
| @@ -304,6 +316,7 @@ void BidirectionalStreamSpdyImpl::OnStreamInitialized(int rv) { |
| void BidirectionalStreamSpdyImpl::NotifyError(int rv) { |
| ResetStream(); |
| + write_pending_ = false; |
| if (delegate_) { |
| BidirectionalStreamImpl::Delegate* delegate = delegate_; |
| delegate_ = nullptr; |
| @@ -314,6 +327,14 @@ void BidirectionalStreamSpdyImpl::NotifyError(int rv) { |
| } |
| } |
| +void BidirectionalStreamSpdyImpl::NotifyDataSent() { |
| + DCHECK(write_pending_); |
| + |
| + write_pending_ = false; |
| + if (delegate_) |
| + delegate_->OnDataSent(); |
| +} |
| + |
| void BidirectionalStreamSpdyImpl::ResetStream() { |
| if (!stream_) |
| return; |
| @@ -373,4 +394,23 @@ bool BidirectionalStreamSpdyImpl::ShouldWaitForMoreBufferedData() const { |
| static_cast<size_t>(read_buffer_len_); |
| } |
| +bool BidirectionalStreamSpdyImpl::MaybeHandleStreamClosedInSendData() { |
| + if (stream_) |
| + return false; |
| + // If |stream_| is closed without an error before client half closes, |
| + // blackhole any pending write data. crbug.com/650438. |
| + if (!written_end_of_stream_ && stream_closed_ && |
|
kapishnikov
2016/10/31 17:03:07
To make the code easier to understand, I think we
xunjieli
2016/10/31 17:18:02
Done.
|
| + closed_stream_status_ == OK) { |
| + base::ThreadTaskRunnerHandle::Get()->PostTask( |
| + FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::NotifyDataSent, |
| + weak_factory_.GetWeakPtr())); |
| + return true; |
| + } |
| + LOG(ERROR) << "Trying to send data after stream has been destroyed."; |
| + base::ThreadTaskRunnerHandle::Get()->PostTask( |
| + FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::NotifyError, |
| + weak_factory_.GetWeakPtr(), ERR_UNEXPECTED)); |
| + return true; |
| +} |
| + |
| } // namespace net |