blob: c649cda7ca06f2e0c7f870e14801f4ce9ff72355 [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,
Yannic Bonenberger00e09842019-08-31 20:46:1982 base::BindOnce(&BidirectionalStreamSpdyImpl::OnStreamInitialized,
83 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
Kenichi Ishibashi74155532021-03-13 01:38:06209void BidirectionalStreamSpdyImpl::OnEarlyHintsReceived(
210 const spdy::Http2HeaderBlock& headers) {
211 DCHECK(stream_);
212 // TODO(crbug.com/671310): Plumb Early Hints to `delegate_` if needed.
213}
214
bnc4c214312016-11-28 16:49:15215void BidirectionalStreamSpdyImpl::OnHeadersReceived(
Bence Béky4c325e52020-10-22 20:48:01216 const spdy::Http2HeaderBlock& response_headers,
217 const spdy::Http2HeaderBlock* pushed_request_headers) {
xunjieli11834f02015-12-22 04:27:08218 DCHECK(stream_);
219
xunjieli707f8952016-06-06 15:22:06220 if (delegate_)
221 delegate_->OnHeadersReceived(response_headers);
xunjieli11834f02015-12-22 04:27:08222}
223
xunjieli5749218c2016-03-22 16:43:06224void BidirectionalStreamSpdyImpl::OnDataReceived(
danakjaee3e1ec2016-04-16 00:23:18225 std::unique_ptr<SpdyBuffer> buffer) {
xunjieli11834f02015-12-22 04:27:08226 DCHECK(stream_);
227 DCHECK(!stream_closed_);
228
xunjieli5749218c2016-03-22 16:43:06229 // If |buffer| is null, BidirectionalStreamSpdyImpl::OnClose will be invoked
230 // by SpdyStream to indicate the end of stream.
xunjieli11834f02015-12-22 04:27:08231 if (!buffer)
232 return;
233
234 // When buffer is consumed, SpdyStream::OnReadBufferConsumed will adjust
235 // recv window size accordingly.
236 read_data_queue_.Enqueue(std::move(buffer));
237 if (read_buffer_) {
238 // Handing small chunks of data to the caller creates measurable overhead.
239 // So buffer data in short time-spans and send a single read notification.
240 ScheduleBufferedRead();
241 }
242}
243
xunjieli5749218c2016-03-22 16:43:06244void BidirectionalStreamSpdyImpl::OnDataSent() {
xunjieli4f8b6bb62016-10-31 23:16:00245 DCHECK(write_pending_);
xunjieli11834f02015-12-22 04:27:08246
xunjielibcb0f86e2016-06-03 00:49:29247 pending_combined_buffer_ = nullptr;
xunjieli4f8b6bb62016-10-31 23:16:00248 write_pending_ = false;
249
xunjieli707f8952016-06-06 15:22:06250 if (delegate_)
251 delegate_->OnDataSent();
xunjieli11834f02015-12-22 04:27:08252}
253
Ryan Hamilton0239aac2018-05-19 00:03:13254void BidirectionalStreamSpdyImpl::OnTrailers(
Bence Béky4c325e52020-10-22 20:48:01255 const spdy::Http2HeaderBlock& trailers) {
xunjieli11834f02015-12-22 04:27:08256 DCHECK(stream_);
257 DCHECK(!stream_closed_);
258
xunjieli707f8952016-06-06 15:22:06259 if (delegate_)
260 delegate_->OnTrailersReceived(trailers);
xunjieli11834f02015-12-22 04:27:08261}
262
xunjieli5749218c2016-03-22 16:43:06263void BidirectionalStreamSpdyImpl::OnClose(int status) {
xunjieli11834f02015-12-22 04:27:08264 DCHECK(stream_);
265
266 stream_closed_ = true;
267 closed_stream_status_ = status;
268 closed_stream_received_bytes_ = stream_->raw_received_bytes();
269 closed_stream_sent_bytes_ = stream_->raw_sent_bytes();
xunjieli6d0b944d2016-09-21 01:53:57270 closed_has_load_timing_info_ =
271 stream_->GetLoadTimingInfo(&closed_load_timing_info_);
xunjieli11834f02015-12-22 04:27:08272
273 if (status != OK) {
xunjieli707f8952016-06-06 15:22:06274 NotifyError(status);
xunjieli11834f02015-12-22 04:27:08275 return;
276 }
xunjieli707f8952016-06-06 15:22:06277 ResetStream();
xunjieli11834f02015-12-22 04:27:08278 // Complete any remaining read, as all data has been buffered.
279 // If user has not called ReadData (i.e |read_buffer_| is nullptr), this will
280 // do nothing.
281 timer_->Stop();
xunjieli4f8b6bb62016-10-31 23:16:00282
283 // |this| might get destroyed after calling into |delegate_| in
284 // DoBufferedRead().
285 auto weak_this = weak_factory_.GetWeakPtr();
xunjieli11834f02015-12-22 04:27:08286 DoBufferedRead();
xunjieli4f8b6bb62016-10-31 23:16:00287 if (weak_this.get() && write_pending_)
288 OnDataSent();
xunjieli11834f02015-12-22 04:27:08289}
290
Bence Béky21755dd62019-11-19 13:47:35291bool BidirectionalStreamSpdyImpl::CanGreaseFrameType() const {
292 return false;
293}
294
bnc3d95ca92017-03-29 13:20:34295NetLogSource BidirectionalStreamSpdyImpl::source_dependency() const {
296 return source_dependency_;
297}
298
xunjielibcb0f86e2016-06-03 00:49:29299int BidirectionalStreamSpdyImpl::SendRequestHeadersHelper() {
Bence Béky4c325e52020-10-22 20:48:01300 spdy::Http2HeaderBlock headers;
xunjieli11834f02015-12-22 04:27:08301 HttpRequestInfo http_request_info;
302 http_request_info.url = request_info_->url;
303 http_request_info.method = request_info_->method;
304 http_request_info.extra_headers = request_info_->extra_headers;
305
Bence Béky1af94d6f2018-02-08 00:40:14306 CreateSpdyHeadersFromHttpRequest(http_request_info,
307 http_request_info.extra_headers, &headers);
xunjieli4f8b6bb62016-10-31 23:16:00308 written_end_of_stream_ = request_info_->end_stream_on_headers;
xunjielibcb0f86e2016-06-03 00:49:29309 return stream_->SendRequestHeaders(std::move(headers),
310 request_info_->end_stream_on_headers
311 ? NO_MORE_DATA_TO_SEND
312 : MORE_DATA_TO_SEND);
xunjieli11834f02015-12-22 04:27:08313}
314
xunjieli5749218c2016-03-22 16:43:06315void BidirectionalStreamSpdyImpl::OnStreamInitialized(int rv) {
xunjieli11834f02015-12-22 04:27:08316 DCHECK_NE(ERR_IO_PENDING, rv);
317 if (rv == OK) {
318 stream_ = stream_request_.ReleaseStream();
319 stream_->SetDelegate(this);
xunjielibcb0f86e2016-06-03 00:49:29320 rv = SendRequestHeadersHelper();
321 if (rv == OK) {
bnc4c214312016-11-28 16:49:15322 OnHeadersSent();
xunjielibcb0f86e2016-06-03 00:49:29323 return;
324 } else if (rv == ERR_IO_PENDING) {
325 return;
326 }
xunjieli11834f02015-12-22 04:27:08327 }
xunjieli707f8952016-06-06 15:22:06328 NotifyError(rv);
329}
330
331void BidirectionalStreamSpdyImpl::NotifyError(int rv) {
332 ResetStream();
xunjieli4f8b6bb62016-10-31 23:16:00333 write_pending_ = false;
xunjieli707f8952016-06-06 15:22:06334 if (delegate_) {
335 BidirectionalStreamImpl::Delegate* delegate = delegate_;
336 delegate_ = nullptr;
337 // Cancel any pending callback.
338 weak_factory_.InvalidateWeakPtrs();
339 delegate->OnFailed(rv);
340 // |this| can be null when returned from delegate.
341 }
342}
343
344void BidirectionalStreamSpdyImpl::ResetStream() {
345 if (!stream_)
346 return;
347 if (!stream_->IsClosed()) {
348 // This sends a RST to the remote.
349 stream_->DetachDelegate();
350 DCHECK(!stream_);
351 } else {
352 // Stream is already closed, so it is not legal to call DetachDelegate.
353 stream_.reset();
354 }
xunjieli11834f02015-12-22 04:27:08355}
356
xunjieli5749218c2016-03-22 16:43:06357void BidirectionalStreamSpdyImpl::ScheduleBufferedRead() {
xunjieli11834f02015-12-22 04:27:08358 // If there is already a scheduled DoBufferedRead, don't issue
359 // another one. Mark that we have received more data and return.
360 if (timer_->IsRunning()) {
361 more_read_data_pending_ = true;
362 return;
363 }
364
365 more_read_data_pending_ = false;
Peter Kastinge5a38ed2021-10-02 03:06:35366 timer_->Start(FROM_HERE, base::Milliseconds(kBufferTimeMs),
Yannic Bonenberger00e09842019-08-31 20:46:19367 base::BindOnce(&BidirectionalStreamSpdyImpl::DoBufferedRead,
368 weak_factory_.GetWeakPtr()));
xunjieli11834f02015-12-22 04:27:08369}
370
xunjieli5749218c2016-03-22 16:43:06371void BidirectionalStreamSpdyImpl::DoBufferedRead() {
xunjieli11834f02015-12-22 04:27:08372 DCHECK(!timer_->IsRunning());
373 // Check to see that the stream has not errored out.
374 DCHECK(stream_ || stream_closed_);
375 DCHECK(!stream_closed_ || closed_stream_status_ == OK);
376
377 // When |more_read_data_pending_| is true, it means that more data has arrived
378 // since started waiting. Wait a little longer and continue to buffer.
379 if (more_read_data_pending_ && ShouldWaitForMoreBufferedData()) {
380 ScheduleBufferedRead();
381 return;
382 }
383
384 int rv = 0;
385 if (read_buffer_) {
386 rv = ReadData(read_buffer_.get(), read_buffer_len_);
387 DCHECK_NE(ERR_IO_PENDING, rv);
388 read_buffer_ = nullptr;
389 read_buffer_len_ = 0;
xunjieli707f8952016-06-06 15:22:06390 if (delegate_)
391 delegate_->OnDataRead(rv);
xunjieli11834f02015-12-22 04:27:08392 }
393}
394
xunjieli5749218c2016-03-22 16:43:06395bool BidirectionalStreamSpdyImpl::ShouldWaitForMoreBufferedData() const {
xunjieli11834f02015-12-22 04:27:08396 if (stream_closed_)
397 return false;
398 DCHECK_GT(read_buffer_len_, 0);
399 return read_data_queue_.GetTotalSize() <
400 static_cast<size_t>(read_buffer_len_);
401}
402
xunjieli4f8b6bb62016-10-31 23:16:00403bool BidirectionalStreamSpdyImpl::MaybeHandleStreamClosedInSendData() {
404 if (stream_)
405 return false;
406 // If |stream_| is closed without an error before client half closes,
407 // blackhole any pending write data. crbug.com/650438.
408 if (stream_closed_ && closed_stream_status_ == OK) {
409 base::ThreadTaskRunnerHandle::Get()->PostTask(
kylecharf4fe5172019-02-15 18:53:49410 FROM_HERE, base::BindOnce(&BidirectionalStreamSpdyImpl::OnDataSent,
411 weak_factory_.GetWeakPtr()));
xunjieli4f8b6bb62016-10-31 23:16:00412 return true;
413 }
414 LOG(ERROR) << "Trying to send data after stream has been destroyed.";
415 base::ThreadTaskRunnerHandle::Get()->PostTask(
kylecharf4fe5172019-02-15 18:53:49416 FROM_HERE, base::BindOnce(&BidirectionalStreamSpdyImpl::NotifyError,
417 weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
xunjieli4f8b6bb62016-10-31 23:16:00418 return true;
419}
420
xunjieli11834f02015-12-22 04:27:08421} // namespace net