blob: 4b443a4b891eee1c618ffe188bedd6087c09863b [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"
Ryan Hamiltonea4fa192022-04-12 18:30:4919#include "net/third_party/quiche/src/quiche/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),
Tsuyoshi Horob2022b52022-06-09 01:44:0738 source_dependency_(source_dependency) {}
xunjieli11834f02015-12-22 04:27:0839
xunjieli5749218c2016-03-22 16:43:0640BidirectionalStreamSpdyImpl::~BidirectionalStreamSpdyImpl() {
xunjieli9ff75c562016-08-10 20:26:1641 // Sends a RST to the remote if the stream is destroyed before it completes.
42 ResetStream();
xunjieli11834f02015-12-22 04:27:0843}
44
xunjieli5749218c2016-03-22 16:43:0645void BidirectionalStreamSpdyImpl::Start(
xunjieli11834f02015-12-22 04:27:0846 const BidirectionalStreamRequestInfo* request_info,
tfarina42834112016-09-22 13:38:2047 const NetLogWithSource& net_log,
xunjielibcb0f86e2016-06-03 00:49:2948 bool /*send_request_headers_automatically*/,
xunjieli5749218c2016-03-22 16:43:0649 BidirectionalStreamImpl::Delegate* delegate,
tzik08d8d6e2018-07-09 04:11:4750 std::unique_ptr<base::OneShotTimer> timer,
Ramin Halavati3c96c6d2018-03-11 13:29:4451 const NetworkTrafficAnnotationTag& traffic_annotation) {
xunjieli11834f02015-12-22 04:27:0852 DCHECK(!stream_);
53 DCHECK(timer);
54
55 delegate_ = delegate;
56 timer_ = std::move(timer);
57
58 if (!spdy_session_) {
xunjieli707f8952016-06-06 15:22:0659 base::ThreadTaskRunnerHandle::Get()->PostTask(
60 FROM_HERE,
kylecharf4fe5172019-02-15 18:53:4961 base::BindOnce(&BidirectionalStreamSpdyImpl::NotifyError,
62 weak_factory_.GetWeakPtr(), ERR_CONNECTION_CLOSED));
xunjieli11834f02015-12-22 04:27:0863 return;
64 }
65
66 request_info_ = request_info;
67
68 int rv = stream_request_.StartRequest(
69 SPDY_BIDIRECTIONAL_STREAM, spdy_session_, request_info_->url,
Steven Valdez1c1859172019-04-10 15:33:2870 false /* no early data */, request_info_->priority,
71 request_info_->socket_tag, net_log,
Yannic Bonenberger00e09842019-08-31 20:46:1972 base::BindOnce(&BidirectionalStreamSpdyImpl::OnStreamInitialized,
73 weak_factory_.GetWeakPtr()),
Stefano Duo90efd492022-01-17 13:31:1374 traffic_annotation, request_info_->detect_broken_connection,
75 request_info_->heartbeat_interval);
xunjieli11834f02015-12-22 04:27:0876 if (rv != ERR_IO_PENDING)
77 OnStreamInitialized(rv);
78}
79
xunjielibcb0f86e2016-06-03 00:49:2980void BidirectionalStreamSpdyImpl::SendRequestHeaders() {
81 // Request headers will be sent automatically.
82 NOTREACHED();
83}
84
xunjieli5749218c2016-03-22 16:43:0685int BidirectionalStreamSpdyImpl::ReadData(IOBuffer* buf, int buf_len) {
xunjieli11834f02015-12-22 04:27:0886 if (stream_)
87 DCHECK(!stream_->IsIdle());
88
89 DCHECK(buf);
90 DCHECK(buf_len);
91 DCHECK(!timer_->IsRunning()) << "There should be only one ReadData in flight";
92
93 // If there is data buffered, complete the IO immediately.
94 if (!read_data_queue_.IsEmpty()) {
95 return read_data_queue_.Dequeue(buf->data(), buf_len);
96 } else if (stream_closed_) {
97 return closed_stream_status_;
98 }
99 // Read will complete asynchronously and Delegate::OnReadCompleted will be
100 // called upon completion.
101 read_buffer_ = buf;
102 read_buffer_len_ = buf_len;
103 return ERR_IO_PENDING;
104}
105
xunjieli07a42ce2016-04-26 20:05:31106void BidirectionalStreamSpdyImpl::SendvData(
xunjieli2328a2682016-05-16 19:38:25107 const std::vector<scoped_refptr<IOBuffer>>& buffers,
xunjieli07a42ce2016-04-26 20:05:31108 const std::vector<int>& lengths,
109 bool end_stream) {
xunjieli07a42ce2016-04-26 20:05:31110 DCHECK_EQ(buffers.size(), lengths.size());
xunjieli4f8b6bb62016-10-31 23:16:00111 DCHECK(!write_pending_);
xunjieli07a42ce2016-04-26 20:05:31112
xunjieli4f8b6bb62016-10-31 23:16:00113 if (written_end_of_stream_) {
114 LOG(ERROR) << "Writing after end of stream is written.";
xunjieli707f8952016-06-06 15:22:06115 base::ThreadTaskRunnerHandle::Get()->PostTask(
kylecharf4fe5172019-02-15 18:53:49116 FROM_HERE, base::BindOnce(&BidirectionalStreamSpdyImpl::NotifyError,
117 weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
xunjieli707f8952016-06-06 15:22:06118 return;
119 }
120
xunjieli4f8b6bb62016-10-31 23:16:00121 write_pending_ = true;
122 written_end_of_stream_ = end_stream;
123 if (MaybeHandleStreamClosedInSendData())
124 return;
125
xunjieli707f8952016-06-06 15:22:06126 DCHECK(!stream_closed_);
xunjieli07a42ce2016-04-26 20:05:31127 int total_len = 0;
128 for (int len : lengths) {
129 total_len += len;
130 }
131
rchad39988f2017-06-02 05:34:32132 if (buffers.size() == 1) {
133 pending_combined_buffer_ = buffers[0];
134 } else {
Victor Costan9c7302b2018-08-27 16:39:44135 pending_combined_buffer_ = base::MakeRefCounted<net::IOBuffer>(total_len);
rchad39988f2017-06-02 05:34:32136 int len = 0;
137 // TODO(xunjieli): Get rid of extra copy. Coalesce headers and data frames.
138 for (size_t i = 0; i < buffers.size(); ++i) {
139 memcpy(pending_combined_buffer_->data() + len, buffers[i]->data(),
140 lengths[i]);
141 len += lengths[i];
142 }
xunjieli07a42ce2016-04-26 20:05:31143 }
144 stream_->SendData(pending_combined_buffer_.get(), total_len,
145 end_stream ? NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND);
146}
147
xunjieli5749218c2016-03-22 16:43:06148NextProto BidirectionalStreamSpdyImpl::GetProtocol() const {
xunjieli11834f02015-12-22 04:27:08149 return negotiated_protocol_;
150}
151
xunjieli5749218c2016-03-22 16:43:06152int64_t BidirectionalStreamSpdyImpl::GetTotalReceivedBytes() const {
xunjieli11834f02015-12-22 04:27:08153 if (stream_closed_)
154 return closed_stream_received_bytes_;
155
156 if (!stream_)
157 return 0;
158
159 return stream_->raw_received_bytes();
160}
161
xunjieli5749218c2016-03-22 16:43:06162int64_t BidirectionalStreamSpdyImpl::GetTotalSentBytes() const {
xunjieli11834f02015-12-22 04:27:08163 if (stream_closed_)
164 return closed_stream_sent_bytes_;
165
166 if (!stream_)
167 return 0;
168
169 return stream_->raw_sent_bytes();
170}
171
xunjieliba8de4322016-09-20 21:57:54172bool BidirectionalStreamSpdyImpl::GetLoadTimingInfo(
173 LoadTimingInfo* load_timing_info) const {
xunjieli6d0b944d2016-09-21 01:53:57174 if (stream_closed_) {
175 if (!closed_has_load_timing_info_)
176 return false;
177 *load_timing_info = closed_load_timing_info_;
178 return true;
179 }
180
181 // If |stream_| isn't created or has ID 0, return false. This is to match
182 // the implementation in SpdyHttpStream.
183 if (!stream_ || stream_->stream_id() == 0)
184 return false;
185
186 return stream_->GetLoadTimingInfo(load_timing_info);
xunjieliba8de4322016-09-20 21:57:54187}
188
Brad Lassey5f488db42017-07-19 00:30:46189void BidirectionalStreamSpdyImpl::PopulateNetErrorDetails(
190 NetErrorDetails* details) {}
191
bnc4c214312016-11-28 16:49:15192void BidirectionalStreamSpdyImpl::OnHeadersSent() {
xunjieli11834f02015-12-22 04:27:08193 DCHECK(stream_);
194
bncbca843ba2016-07-14 13:05:48195 negotiated_protocol_ = kProtoHTTP2;
xunjieli707f8952016-06-06 15:22:06196 if (delegate_)
197 delegate_->OnStreamReady(/*request_headers_sent=*/true);
xunjieli11834f02015-12-22 04:27:08198}
199
Kenichi Ishibashi74155532021-03-13 01:38:06200void BidirectionalStreamSpdyImpl::OnEarlyHintsReceived(
201 const spdy::Http2HeaderBlock& headers) {
202 DCHECK(stream_);
203 // TODO(crbug.com/671310): Plumb Early Hints to `delegate_` if needed.
204}
205
bnc4c214312016-11-28 16:49:15206void BidirectionalStreamSpdyImpl::OnHeadersReceived(
Bence Béky4c325e52020-10-22 20:48:01207 const spdy::Http2HeaderBlock& response_headers,
208 const spdy::Http2HeaderBlock* pushed_request_headers) {
xunjieli11834f02015-12-22 04:27:08209 DCHECK(stream_);
210
xunjieli707f8952016-06-06 15:22:06211 if (delegate_)
212 delegate_->OnHeadersReceived(response_headers);
xunjieli11834f02015-12-22 04:27:08213}
214
xunjieli5749218c2016-03-22 16:43:06215void BidirectionalStreamSpdyImpl::OnDataReceived(
danakjaee3e1ec2016-04-16 00:23:18216 std::unique_ptr<SpdyBuffer> buffer) {
xunjieli11834f02015-12-22 04:27:08217 DCHECK(stream_);
218 DCHECK(!stream_closed_);
219
xunjieli5749218c2016-03-22 16:43:06220 // If |buffer| is null, BidirectionalStreamSpdyImpl::OnClose will be invoked
221 // by SpdyStream to indicate the end of stream.
xunjieli11834f02015-12-22 04:27:08222 if (!buffer)
223 return;
224
225 // When buffer is consumed, SpdyStream::OnReadBufferConsumed will adjust
226 // recv window size accordingly.
227 read_data_queue_.Enqueue(std::move(buffer));
228 if (read_buffer_) {
229 // Handing small chunks of data to the caller creates measurable overhead.
230 // So buffer data in short time-spans and send a single read notification.
231 ScheduleBufferedRead();
232 }
233}
234
xunjieli5749218c2016-03-22 16:43:06235void BidirectionalStreamSpdyImpl::OnDataSent() {
xunjieli4f8b6bb62016-10-31 23:16:00236 DCHECK(write_pending_);
xunjieli11834f02015-12-22 04:27:08237
xunjielibcb0f86e2016-06-03 00:49:29238 pending_combined_buffer_ = nullptr;
xunjieli4f8b6bb62016-10-31 23:16:00239 write_pending_ = false;
240
xunjieli707f8952016-06-06 15:22:06241 if (delegate_)
242 delegate_->OnDataSent();
xunjieli11834f02015-12-22 04:27:08243}
244
Ryan Hamilton0239aac2018-05-19 00:03:13245void BidirectionalStreamSpdyImpl::OnTrailers(
Bence Béky4c325e52020-10-22 20:48:01246 const spdy::Http2HeaderBlock& trailers) {
xunjieli11834f02015-12-22 04:27:08247 DCHECK(stream_);
248 DCHECK(!stream_closed_);
249
xunjieli707f8952016-06-06 15:22:06250 if (delegate_)
251 delegate_->OnTrailersReceived(trailers);
xunjieli11834f02015-12-22 04:27:08252}
253
xunjieli5749218c2016-03-22 16:43:06254void BidirectionalStreamSpdyImpl::OnClose(int status) {
xunjieli11834f02015-12-22 04:27:08255 DCHECK(stream_);
256
257 stream_closed_ = true;
258 closed_stream_status_ = status;
259 closed_stream_received_bytes_ = stream_->raw_received_bytes();
260 closed_stream_sent_bytes_ = stream_->raw_sent_bytes();
xunjieli6d0b944d2016-09-21 01:53:57261 closed_has_load_timing_info_ =
262 stream_->GetLoadTimingInfo(&closed_load_timing_info_);
xunjieli11834f02015-12-22 04:27:08263
264 if (status != OK) {
xunjieli707f8952016-06-06 15:22:06265 NotifyError(status);
xunjieli11834f02015-12-22 04:27:08266 return;
267 }
xunjieli707f8952016-06-06 15:22:06268 ResetStream();
xunjieli11834f02015-12-22 04:27:08269 // Complete any remaining read, as all data has been buffered.
270 // If user has not called ReadData (i.e |read_buffer_| is nullptr), this will
271 // do nothing.
272 timer_->Stop();
xunjieli4f8b6bb62016-10-31 23:16:00273
274 // |this| might get destroyed after calling into |delegate_| in
275 // DoBufferedRead().
276 auto weak_this = weak_factory_.GetWeakPtr();
xunjieli11834f02015-12-22 04:27:08277 DoBufferedRead();
xunjieli4f8b6bb62016-10-31 23:16:00278 if (weak_this.get() && write_pending_)
279 OnDataSent();
xunjieli11834f02015-12-22 04:27:08280}
281
Bence Béky21755dd62019-11-19 13:47:35282bool BidirectionalStreamSpdyImpl::CanGreaseFrameType() const {
283 return false;
284}
285
bnc3d95ca92017-03-29 13:20:34286NetLogSource BidirectionalStreamSpdyImpl::source_dependency() const {
287 return source_dependency_;
288}
289
xunjielibcb0f86e2016-06-03 00:49:29290int BidirectionalStreamSpdyImpl::SendRequestHeadersHelper() {
Bence Béky4c325e52020-10-22 20:48:01291 spdy::Http2HeaderBlock headers;
xunjieli11834f02015-12-22 04:27:08292 HttpRequestInfo http_request_info;
293 http_request_info.url = request_info_->url;
294 http_request_info.method = request_info_->method;
295 http_request_info.extra_headers = request_info_->extra_headers;
296
Bence Béky1af94d6f2018-02-08 00:40:14297 CreateSpdyHeadersFromHttpRequest(http_request_info,
298 http_request_info.extra_headers, &headers);
xunjieli4f8b6bb62016-10-31 23:16:00299 written_end_of_stream_ = request_info_->end_stream_on_headers;
xunjielibcb0f86e2016-06-03 00:49:29300 return stream_->SendRequestHeaders(std::move(headers),
301 request_info_->end_stream_on_headers
302 ? NO_MORE_DATA_TO_SEND
303 : MORE_DATA_TO_SEND);
xunjieli11834f02015-12-22 04:27:08304}
305
xunjieli5749218c2016-03-22 16:43:06306void BidirectionalStreamSpdyImpl::OnStreamInitialized(int rv) {
xunjieli11834f02015-12-22 04:27:08307 DCHECK_NE(ERR_IO_PENDING, rv);
308 if (rv == OK) {
309 stream_ = stream_request_.ReleaseStream();
310 stream_->SetDelegate(this);
xunjielibcb0f86e2016-06-03 00:49:29311 rv = SendRequestHeadersHelper();
312 if (rv == OK) {
bnc4c214312016-11-28 16:49:15313 OnHeadersSent();
xunjielibcb0f86e2016-06-03 00:49:29314 return;
315 } else if (rv == ERR_IO_PENDING) {
316 return;
317 }
xunjieli11834f02015-12-22 04:27:08318 }
xunjieli707f8952016-06-06 15:22:06319 NotifyError(rv);
320}
321
322void BidirectionalStreamSpdyImpl::NotifyError(int rv) {
323 ResetStream();
xunjieli4f8b6bb62016-10-31 23:16:00324 write_pending_ = false;
xunjieli707f8952016-06-06 15:22:06325 if (delegate_) {
326 BidirectionalStreamImpl::Delegate* delegate = delegate_;
327 delegate_ = nullptr;
328 // Cancel any pending callback.
329 weak_factory_.InvalidateWeakPtrs();
330 delegate->OnFailed(rv);
331 // |this| can be null when returned from delegate.
332 }
333}
334
335void BidirectionalStreamSpdyImpl::ResetStream() {
336 if (!stream_)
337 return;
338 if (!stream_->IsClosed()) {
339 // This sends a RST to the remote.
340 stream_->DetachDelegate();
341 DCHECK(!stream_);
342 } else {
343 // Stream is already closed, so it is not legal to call DetachDelegate.
344 stream_.reset();
345 }
xunjieli11834f02015-12-22 04:27:08346}
347
xunjieli5749218c2016-03-22 16:43:06348void BidirectionalStreamSpdyImpl::ScheduleBufferedRead() {
xunjieli11834f02015-12-22 04:27:08349 // If there is already a scheduled DoBufferedRead, don't issue
350 // another one. Mark that we have received more data and return.
351 if (timer_->IsRunning()) {
352 more_read_data_pending_ = true;
353 return;
354 }
355
356 more_read_data_pending_ = false;
Peter Kastinge5a38ed2021-10-02 03:06:35357 timer_->Start(FROM_HERE, base::Milliseconds(kBufferTimeMs),
Yannic Bonenberger00e09842019-08-31 20:46:19358 base::BindOnce(&BidirectionalStreamSpdyImpl::DoBufferedRead,
359 weak_factory_.GetWeakPtr()));
xunjieli11834f02015-12-22 04:27:08360}
361
xunjieli5749218c2016-03-22 16:43:06362void BidirectionalStreamSpdyImpl::DoBufferedRead() {
xunjieli11834f02015-12-22 04:27:08363 DCHECK(!timer_->IsRunning());
364 // Check to see that the stream has not errored out.
365 DCHECK(stream_ || stream_closed_);
366 DCHECK(!stream_closed_ || closed_stream_status_ == OK);
367
368 // When |more_read_data_pending_| is true, it means that more data has arrived
369 // since started waiting. Wait a little longer and continue to buffer.
370 if (more_read_data_pending_ && ShouldWaitForMoreBufferedData()) {
371 ScheduleBufferedRead();
372 return;
373 }
374
375 int rv = 0;
376 if (read_buffer_) {
377 rv = ReadData(read_buffer_.get(), read_buffer_len_);
378 DCHECK_NE(ERR_IO_PENDING, rv);
379 read_buffer_ = nullptr;
380 read_buffer_len_ = 0;
xunjieli707f8952016-06-06 15:22:06381 if (delegate_)
382 delegate_->OnDataRead(rv);
xunjieli11834f02015-12-22 04:27:08383 }
384}
385
xunjieli5749218c2016-03-22 16:43:06386bool BidirectionalStreamSpdyImpl::ShouldWaitForMoreBufferedData() const {
xunjieli11834f02015-12-22 04:27:08387 if (stream_closed_)
388 return false;
389 DCHECK_GT(read_buffer_len_, 0);
390 return read_data_queue_.GetTotalSize() <
391 static_cast<size_t>(read_buffer_len_);
392}
393
xunjieli4f8b6bb62016-10-31 23:16:00394bool BidirectionalStreamSpdyImpl::MaybeHandleStreamClosedInSendData() {
395 if (stream_)
396 return false;
397 // If |stream_| is closed without an error before client half closes,
398 // blackhole any pending write data. crbug.com/650438.
399 if (stream_closed_ && closed_stream_status_ == OK) {
400 base::ThreadTaskRunnerHandle::Get()->PostTask(
kylecharf4fe5172019-02-15 18:53:49401 FROM_HERE, base::BindOnce(&BidirectionalStreamSpdyImpl::OnDataSent,
402 weak_factory_.GetWeakPtr()));
xunjieli4f8b6bb62016-10-31 23:16:00403 return true;
404 }
405 LOG(ERROR) << "Trying to send data after stream has been destroyed.";
406 base::ThreadTaskRunnerHandle::Get()->PostTask(
kylecharf4fe5172019-02-15 18:53:49407 FROM_HERE, base::BindOnce(&BidirectionalStreamSpdyImpl::NotifyError,
408 weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
xunjieli4f8b6bb62016-10-31 23:16:00409 return true;
410}
411
xunjieli11834f02015-12-22 04:27:08412} // namespace net