blob: f428a759d5a413e0da9084cf33cf1442a098009a [file] [log] [blame]
xunjieli11834f02015-12-22 04:27:081// Copyright 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Bence Béky94658bf2018-05-11 19:22:585#include "net/spdy/bidirectional_stream_spdy_impl.h"
xunjieli11834f02015-12-22 04:27:086
xunjieli4f8b6bb62016-10-31 23:16:007#include <utility>
8
xunjieli11834f02015-12-22 04:27:089#include "base/bind.h"
10#include "base/location.h"
11#include "base/logging.h"
fdoraya19b7702016-12-23 14:19:3112#include "base/threading/thread_task_runner_handle.h"
xunjieli11834f02015-12-22 04:27:0813#include "base/time/time.h"
14#include "base/timer/timer.h"
15#include "net/http/bidirectional_stream_request_info.h"
Bence Béky94658bf2018-05-11 19:22:5816#include "net/spdy/spdy_buffer.h"
17#include "net/spdy/spdy_http_utils.h"
18#include "net/spdy/spdy_stream.h"
Victor Vasiliev27cc7712019-01-24 11:50:1419#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
xunjieli11834f02015-12-22 04:27:0820
21namespace net {
22
23namespace {
24
25// Time to wait in millisecond to notify |delegate_| of data received.
26// Handing small chunks of data to the caller creates measurable overhead.
27// So buffer data in short time-spans and send a single read notification.
mefe666e9a2015-12-30 23:41:3028const int kBufferTimeMs = 1;
xunjieli11834f02015-12-22 04:27:0829
30} // namespace
31
xunjieli5749218c2016-03-22 16:43:0632BidirectionalStreamSpdyImpl::BidirectionalStreamSpdyImpl(
bnc3d95ca92017-03-29 13:20:3433 const base::WeakPtr<SpdySession>& spdy_session,
34 NetLogSource source_dependency)
xunjieli11834f02015-12-22 04:27:0835 : spdy_session_(spdy_session),
36 request_info_(nullptr),
37 delegate_(nullptr),
bnc3d95ca92017-03-29 13:20:3438 source_dependency_(source_dependency),
xunjieli11834f02015-12-22 04:27:0839 negotiated_protocol_(kProtoUnknown),
40 more_read_data_pending_(false),
41 read_buffer_len_(0),
xunjieli4f8b6bb62016-10-31 23:16:0042 written_end_of_stream_(false),
43 write_pending_(false),
xunjieli11834f02015-12-22 04:27:0844 stream_closed_(false),
45 closed_stream_status_(ERR_FAILED),
46 closed_stream_received_bytes_(0),
47 closed_stream_sent_bytes_(0),
Jeremy Romand54000b22019-07-08 18:40:1648 closed_has_load_timing_info_(false) {}
xunjieli11834f02015-12-22 04:27:0849
xunjieli5749218c2016-03-22 16:43:0650BidirectionalStreamSpdyImpl::~BidirectionalStreamSpdyImpl() {
xunjieli9ff75c562016-08-10 20:26:1651 // Sends a RST to the remote if the stream is destroyed before it completes.
52 ResetStream();
xunjieli11834f02015-12-22 04:27:0853}
54
xunjieli5749218c2016-03-22 16:43:0655void BidirectionalStreamSpdyImpl::Start(
xunjieli11834f02015-12-22 04:27:0856 const BidirectionalStreamRequestInfo* request_info,
tfarina42834112016-09-22 13:38:2057 const NetLogWithSource& net_log,
xunjielibcb0f86e2016-06-03 00:49:2958 bool /*send_request_headers_automatically*/,
xunjieli5749218c2016-03-22 16:43:0659 BidirectionalStreamImpl::Delegate* delegate,
tzik08d8d6e2018-07-09 04:11:4760 std::unique_ptr<base::OneShotTimer> timer,
Ramin Halavati3c96c6d2018-03-11 13:29:4461 const NetworkTrafficAnnotationTag& traffic_annotation) {
xunjieli11834f02015-12-22 04:27:0862 DCHECK(!stream_);
63 DCHECK(timer);
64
65 delegate_ = delegate;
66 timer_ = std::move(timer);
67
68 if (!spdy_session_) {
xunjieli707f8952016-06-06 15:22:0669 base::ThreadTaskRunnerHandle::Get()->PostTask(
70 FROM_HERE,
kylecharf4fe5172019-02-15 18:53:4971 base::BindOnce(&BidirectionalStreamSpdyImpl::NotifyError,
72 weak_factory_.GetWeakPtr(), ERR_CONNECTION_CLOSED));
xunjieli11834f02015-12-22 04:27:0873 return;
74 }
75
76 request_info_ = request_info;
77
78 int rv = stream_request_.StartRequest(
79 SPDY_BIDIRECTIONAL_STREAM, spdy_session_, request_info_->url,
Steven Valdez1c1859172019-04-10 15:33:2880 false /* no early data */, request_info_->priority,
81 request_info_->socket_tag, net_log,
xunjieli5749218c2016-03-22 16:43:0682 base::Bind(&BidirectionalStreamSpdyImpl::OnStreamInitialized,
Ramin Halavatiead42712018-02-28 18:41:2783 weak_factory_.GetWeakPtr()),
Ramin Halavati3c96c6d2018-03-11 13:29:4484 traffic_annotation);
xunjieli11834f02015-12-22 04:27:0885 if (rv != ERR_IO_PENDING)
86 OnStreamInitialized(rv);
87}
88
xunjielibcb0f86e2016-06-03 00:49:2989void BidirectionalStreamSpdyImpl::SendRequestHeaders() {
90 // Request headers will be sent automatically.
91 NOTREACHED();
92}
93
xunjieli5749218c2016-03-22 16:43:0694int BidirectionalStreamSpdyImpl::ReadData(IOBuffer* buf, int buf_len) {
xunjieli11834f02015-12-22 04:27:0895 if (stream_)
96 DCHECK(!stream_->IsIdle());
97
98 DCHECK(buf);
99 DCHECK(buf_len);
100 DCHECK(!timer_->IsRunning()) << "There should be only one ReadData in flight";
101
102 // If there is data buffered, complete the IO immediately.
103 if (!read_data_queue_.IsEmpty()) {
104 return read_data_queue_.Dequeue(buf->data(), buf_len);
105 } else if (stream_closed_) {
106 return closed_stream_status_;
107 }
108 // Read will complete asynchronously and Delegate::OnReadCompleted will be
109 // called upon completion.
110 read_buffer_ = buf;
111 read_buffer_len_ = buf_len;
112 return ERR_IO_PENDING;
113}
114
xunjieli07a42ce2016-04-26 20:05:31115void BidirectionalStreamSpdyImpl::SendvData(
xunjieli2328a2682016-05-16 19:38:25116 const std::vector<scoped_refptr<IOBuffer>>& buffers,
xunjieli07a42ce2016-04-26 20:05:31117 const std::vector<int>& lengths,
118 bool end_stream) {
xunjieli07a42ce2016-04-26 20:05:31119 DCHECK_EQ(buffers.size(), lengths.size());
xunjieli4f8b6bb62016-10-31 23:16:00120 DCHECK(!write_pending_);
xunjieli07a42ce2016-04-26 20:05:31121
xunjieli4f8b6bb62016-10-31 23:16:00122 if (written_end_of_stream_) {
123 LOG(ERROR) << "Writing after end of stream is written.";
xunjieli707f8952016-06-06 15:22:06124 base::ThreadTaskRunnerHandle::Get()->PostTask(
kylecharf4fe5172019-02-15 18:53:49125 FROM_HERE, base::BindOnce(&BidirectionalStreamSpdyImpl::NotifyError,
126 weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
xunjieli707f8952016-06-06 15:22:06127 return;
128 }
129
xunjieli4f8b6bb62016-10-31 23:16:00130 write_pending_ = true;
131 written_end_of_stream_ = end_stream;
132 if (MaybeHandleStreamClosedInSendData())
133 return;
134
xunjieli707f8952016-06-06 15:22:06135 DCHECK(!stream_closed_);
xunjieli07a42ce2016-04-26 20:05:31136 int total_len = 0;
137 for (int len : lengths) {
138 total_len += len;
139 }
140
rchad39988f2017-06-02 05:34:32141 if (buffers.size() == 1) {
142 pending_combined_buffer_ = buffers[0];
143 } else {
Victor Costan9c7302b2018-08-27 16:39:44144 pending_combined_buffer_ = base::MakeRefCounted<net::IOBuffer>(total_len);
rchad39988f2017-06-02 05:34:32145 int len = 0;
146 // TODO(xunjieli): Get rid of extra copy. Coalesce headers and data frames.
147 for (size_t i = 0; i < buffers.size(); ++i) {
148 memcpy(pending_combined_buffer_->data() + len, buffers[i]->data(),
149 lengths[i]);
150 len += lengths[i];
151 }
xunjieli07a42ce2016-04-26 20:05:31152 }
153 stream_->SendData(pending_combined_buffer_.get(), total_len,
154 end_stream ? NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND);
155}
156
xunjieli5749218c2016-03-22 16:43:06157NextProto BidirectionalStreamSpdyImpl::GetProtocol() const {
xunjieli11834f02015-12-22 04:27:08158 return negotiated_protocol_;
159}
160
xunjieli5749218c2016-03-22 16:43:06161int64_t BidirectionalStreamSpdyImpl::GetTotalReceivedBytes() const {
xunjieli11834f02015-12-22 04:27:08162 if (stream_closed_)
163 return closed_stream_received_bytes_;
164
165 if (!stream_)
166 return 0;
167
168 return stream_->raw_received_bytes();
169}
170
xunjieli5749218c2016-03-22 16:43:06171int64_t BidirectionalStreamSpdyImpl::GetTotalSentBytes() const {
xunjieli11834f02015-12-22 04:27:08172 if (stream_closed_)
173 return closed_stream_sent_bytes_;
174
175 if (!stream_)
176 return 0;
177
178 return stream_->raw_sent_bytes();
179}
180
xunjieliba8de4322016-09-20 21:57:54181bool BidirectionalStreamSpdyImpl::GetLoadTimingInfo(
182 LoadTimingInfo* load_timing_info) const {
xunjieli6d0b944d2016-09-21 01:53:57183 if (stream_closed_) {
184 if (!closed_has_load_timing_info_)
185 return false;
186 *load_timing_info = closed_load_timing_info_;
187 return true;
188 }
189
190 // If |stream_| isn't created or has ID 0, return false. This is to match
191 // the implementation in SpdyHttpStream.
192 if (!stream_ || stream_->stream_id() == 0)
193 return false;
194
195 return stream_->GetLoadTimingInfo(load_timing_info);
xunjieliba8de4322016-09-20 21:57:54196}
197
Brad Lassey5f488db42017-07-19 00:30:46198void BidirectionalStreamSpdyImpl::PopulateNetErrorDetails(
199 NetErrorDetails* details) {}
200
bnc4c214312016-11-28 16:49:15201void BidirectionalStreamSpdyImpl::OnHeadersSent() {
xunjieli11834f02015-12-22 04:27:08202 DCHECK(stream_);
203
bncbca843ba2016-07-14 13:05:48204 negotiated_protocol_ = kProtoHTTP2;
xunjieli707f8952016-06-06 15:22:06205 if (delegate_)
206 delegate_->OnStreamReady(/*request_headers_sent=*/true);
xunjieli11834f02015-12-22 04:27:08207}
208
bnc4c214312016-11-28 16:49:15209void BidirectionalStreamSpdyImpl::OnHeadersReceived(
Ryan Hamilton0239aac2018-05-19 00:03:13210 const spdy::SpdyHeaderBlock& response_headers,
211 const spdy::SpdyHeaderBlock* pushed_request_headers) {
xunjieli11834f02015-12-22 04:27:08212 DCHECK(stream_);
213
xunjieli707f8952016-06-06 15:22:06214 if (delegate_)
215 delegate_->OnHeadersReceived(response_headers);
xunjieli11834f02015-12-22 04:27:08216}
217
xunjieli5749218c2016-03-22 16:43:06218void BidirectionalStreamSpdyImpl::OnDataReceived(
danakjaee3e1ec2016-04-16 00:23:18219 std::unique_ptr<SpdyBuffer> buffer) {
xunjieli11834f02015-12-22 04:27:08220 DCHECK(stream_);
221 DCHECK(!stream_closed_);
222
xunjieli5749218c2016-03-22 16:43:06223 // If |buffer| is null, BidirectionalStreamSpdyImpl::OnClose will be invoked
224 // by SpdyStream to indicate the end of stream.
xunjieli11834f02015-12-22 04:27:08225 if (!buffer)
226 return;
227
228 // When buffer is consumed, SpdyStream::OnReadBufferConsumed will adjust
229 // recv window size accordingly.
230 read_data_queue_.Enqueue(std::move(buffer));
231 if (read_buffer_) {
232 // Handing small chunks of data to the caller creates measurable overhead.
233 // So buffer data in short time-spans and send a single read notification.
234 ScheduleBufferedRead();
235 }
236}
237
xunjieli5749218c2016-03-22 16:43:06238void BidirectionalStreamSpdyImpl::OnDataSent() {
xunjieli4f8b6bb62016-10-31 23:16:00239 DCHECK(write_pending_);
xunjieli11834f02015-12-22 04:27:08240
xunjielibcb0f86e2016-06-03 00:49:29241 pending_combined_buffer_ = nullptr;
xunjieli4f8b6bb62016-10-31 23:16:00242 write_pending_ = false;
243
xunjieli707f8952016-06-06 15:22:06244 if (delegate_)
245 delegate_->OnDataSent();
xunjieli11834f02015-12-22 04:27:08246}
247
Ryan Hamilton0239aac2018-05-19 00:03:13248void BidirectionalStreamSpdyImpl::OnTrailers(
249 const spdy::SpdyHeaderBlock& trailers) {
xunjieli11834f02015-12-22 04:27:08250 DCHECK(stream_);
251 DCHECK(!stream_closed_);
252
xunjieli707f8952016-06-06 15:22:06253 if (delegate_)
254 delegate_->OnTrailersReceived(trailers);
xunjieli11834f02015-12-22 04:27:08255}
256
xunjieli5749218c2016-03-22 16:43:06257void BidirectionalStreamSpdyImpl::OnClose(int status) {
xunjieli11834f02015-12-22 04:27:08258 DCHECK(stream_);
259
260 stream_closed_ = true;
261 closed_stream_status_ = status;
262 closed_stream_received_bytes_ = stream_->raw_received_bytes();
263 closed_stream_sent_bytes_ = stream_->raw_sent_bytes();
xunjieli6d0b944d2016-09-21 01:53:57264 closed_has_load_timing_info_ =
265 stream_->GetLoadTimingInfo(&closed_load_timing_info_);
xunjieli11834f02015-12-22 04:27:08266
267 if (status != OK) {
xunjieli707f8952016-06-06 15:22:06268 NotifyError(status);
xunjieli11834f02015-12-22 04:27:08269 return;
270 }
xunjieli707f8952016-06-06 15:22:06271 ResetStream();
xunjieli11834f02015-12-22 04:27:08272 // Complete any remaining read, as all data has been buffered.
273 // If user has not called ReadData (i.e |read_buffer_| is nullptr), this will
274 // do nothing.
275 timer_->Stop();
xunjieli4f8b6bb62016-10-31 23:16:00276
277 // |this| might get destroyed after calling into |delegate_| in
278 // DoBufferedRead().
279 auto weak_this = weak_factory_.GetWeakPtr();
xunjieli11834f02015-12-22 04:27:08280 DoBufferedRead();
xunjieli4f8b6bb62016-10-31 23:16:00281 if (weak_this.get() && write_pending_)
282 OnDataSent();
xunjieli11834f02015-12-22 04:27:08283}
284
bnc3d95ca92017-03-29 13:20:34285NetLogSource BidirectionalStreamSpdyImpl::source_dependency() const {
286 return source_dependency_;
287}
288
xunjielibcb0f86e2016-06-03 00:49:29289int BidirectionalStreamSpdyImpl::SendRequestHeadersHelper() {
Ryan Hamilton0239aac2018-05-19 00:03:13290 spdy::SpdyHeaderBlock headers;
xunjieli11834f02015-12-22 04:27:08291 HttpRequestInfo http_request_info;
292 http_request_info.url = request_info_->url;
293 http_request_info.method = request_info_->method;
294 http_request_info.extra_headers = request_info_->extra_headers;
295
Bence Béky1af94d6f2018-02-08 00:40:14296 CreateSpdyHeadersFromHttpRequest(http_request_info,
297 http_request_info.extra_headers, &headers);
xunjieli4f8b6bb62016-10-31 23:16:00298 written_end_of_stream_ = request_info_->end_stream_on_headers;
xunjielibcb0f86e2016-06-03 00:49:29299 return stream_->SendRequestHeaders(std::move(headers),
300 request_info_->end_stream_on_headers
301 ? NO_MORE_DATA_TO_SEND
302 : MORE_DATA_TO_SEND);
xunjieli11834f02015-12-22 04:27:08303}
304
xunjieli5749218c2016-03-22 16:43:06305void BidirectionalStreamSpdyImpl::OnStreamInitialized(int rv) {
xunjieli11834f02015-12-22 04:27:08306 DCHECK_NE(ERR_IO_PENDING, rv);
307 if (rv == OK) {
308 stream_ = stream_request_.ReleaseStream();
309 stream_->SetDelegate(this);
xunjielibcb0f86e2016-06-03 00:49:29310 rv = SendRequestHeadersHelper();
311 if (rv == OK) {
bnc4c214312016-11-28 16:49:15312 OnHeadersSent();
xunjielibcb0f86e2016-06-03 00:49:29313 return;
314 } else if (rv == ERR_IO_PENDING) {
315 return;
316 }
xunjieli11834f02015-12-22 04:27:08317 }
xunjieli707f8952016-06-06 15:22:06318 NotifyError(rv);
319}
320
321void BidirectionalStreamSpdyImpl::NotifyError(int rv) {
322 ResetStream();
xunjieli4f8b6bb62016-10-31 23:16:00323 write_pending_ = false;
xunjieli707f8952016-06-06 15:22:06324 if (delegate_) {
325 BidirectionalStreamImpl::Delegate* delegate = delegate_;
326 delegate_ = nullptr;
327 // Cancel any pending callback.
328 weak_factory_.InvalidateWeakPtrs();
329 delegate->OnFailed(rv);
330 // |this| can be null when returned from delegate.
331 }
332}
333
334void BidirectionalStreamSpdyImpl::ResetStream() {
335 if (!stream_)
336 return;
337 if (!stream_->IsClosed()) {
338 // This sends a RST to the remote.
339 stream_->DetachDelegate();
340 DCHECK(!stream_);
341 } else {
342 // Stream is already closed, so it is not legal to call DetachDelegate.
343 stream_.reset();
344 }
xunjieli11834f02015-12-22 04:27:08345}
346
xunjieli5749218c2016-03-22 16:43:06347void BidirectionalStreamSpdyImpl::ScheduleBufferedRead() {
xunjieli11834f02015-12-22 04:27:08348 // If there is already a scheduled DoBufferedRead, don't issue
349 // another one. Mark that we have received more data and return.
350 if (timer_->IsRunning()) {
351 more_read_data_pending_ = true;
352 return;
353 }
354
355 more_read_data_pending_ = false;
356 timer_->Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kBufferTimeMs),
xunjieli5749218c2016-03-22 16:43:06357 base::Bind(&BidirectionalStreamSpdyImpl::DoBufferedRead,
xunjieli11834f02015-12-22 04:27:08358 weak_factory_.GetWeakPtr()));
359}
360
xunjieli5749218c2016-03-22 16:43:06361void BidirectionalStreamSpdyImpl::DoBufferedRead() {
xunjieli11834f02015-12-22 04:27:08362 DCHECK(!timer_->IsRunning());
363 // Check to see that the stream has not errored out.
364 DCHECK(stream_ || stream_closed_);
365 DCHECK(!stream_closed_ || closed_stream_status_ == OK);
366
367 // When |more_read_data_pending_| is true, it means that more data has arrived
368 // since started waiting. Wait a little longer and continue to buffer.
369 if (more_read_data_pending_ && ShouldWaitForMoreBufferedData()) {
370 ScheduleBufferedRead();
371 return;
372 }
373
374 int rv = 0;
375 if (read_buffer_) {
376 rv = ReadData(read_buffer_.get(), read_buffer_len_);
377 DCHECK_NE(ERR_IO_PENDING, rv);
378 read_buffer_ = nullptr;
379 read_buffer_len_ = 0;
xunjieli707f8952016-06-06 15:22:06380 if (delegate_)
381 delegate_->OnDataRead(rv);
xunjieli11834f02015-12-22 04:27:08382 }
383}
384
xunjieli5749218c2016-03-22 16:43:06385bool BidirectionalStreamSpdyImpl::ShouldWaitForMoreBufferedData() const {
xunjieli11834f02015-12-22 04:27:08386 if (stream_closed_)
387 return false;
388 DCHECK_GT(read_buffer_len_, 0);
389 return read_data_queue_.GetTotalSize() <
390 static_cast<size_t>(read_buffer_len_);
391}
392
xunjieli4f8b6bb62016-10-31 23:16:00393bool BidirectionalStreamSpdyImpl::MaybeHandleStreamClosedInSendData() {
394 if (stream_)
395 return false;
396 // If |stream_| is closed without an error before client half closes,
397 // blackhole any pending write data. crbug.com/650438.
398 if (stream_closed_ && closed_stream_status_ == OK) {
399 base::ThreadTaskRunnerHandle::Get()->PostTask(
kylecharf4fe5172019-02-15 18:53:49400 FROM_HERE, base::BindOnce(&BidirectionalStreamSpdyImpl::OnDataSent,
401 weak_factory_.GetWeakPtr()));
xunjieli4f8b6bb62016-10-31 23:16:00402 return true;
403 }
404 LOG(ERROR) << "Trying to send data after stream has been destroyed.";
405 base::ThreadTaskRunnerHandle::Get()->PostTask(
kylecharf4fe5172019-02-15 18:53:49406 FROM_HERE, base::BindOnce(&BidirectionalStreamSpdyImpl::NotifyError,
407 weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
xunjieli4f8b6bb62016-10-31 23:16:00408 return true;
409}
410
xunjieli11834f02015-12-22 04:27:08411} // namespace net