blob: 07f240db5e45a781aa88fd36a1a8bfc9190be688 [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
xunjieli5749218c2016-03-22 16:43:065#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"
16#include "net/spdy/spdy_buffer.h"
17#include "net/spdy/spdy_header_block.h"
18#include "net/spdy/spdy_http_utils.h"
19#include "net/spdy/spdy_stream.h"
20
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(
xunjieli11834f02015-12-22 04:27:0833 const base::WeakPtr<SpdySession>& spdy_session)
34 : spdy_session_(spdy_session),
35 request_info_(nullptr),
36 delegate_(nullptr),
37 negotiated_protocol_(kProtoUnknown),
38 more_read_data_pending_(false),
39 read_buffer_len_(0),
xunjieli4f8b6bb62016-10-31 23:16:0040 written_end_of_stream_(false),
41 write_pending_(false),
xunjieli11834f02015-12-22 04:27:0842 stream_closed_(false),
43 closed_stream_status_(ERR_FAILED),
44 closed_stream_received_bytes_(0),
45 closed_stream_sent_bytes_(0),
xunjieli6d0b944d2016-09-21 01:53:5746 closed_has_load_timing_info_(false),
xunjieli11834f02015-12-22 04:27:0847 weak_factory_(this) {}
48
xunjieli5749218c2016-03-22 16:43:0649BidirectionalStreamSpdyImpl::~BidirectionalStreamSpdyImpl() {
xunjieli9ff75c562016-08-10 20:26:1650 // Sends a RST to the remote if the stream is destroyed before it completes.
51 ResetStream();
xunjieli11834f02015-12-22 04:27:0852}
53
xunjieli5749218c2016-03-22 16:43:0654void BidirectionalStreamSpdyImpl::Start(
xunjieli11834f02015-12-22 04:27:0855 const BidirectionalStreamRequestInfo* request_info,
tfarina42834112016-09-22 13:38:2056 const NetLogWithSource& net_log,
xunjielibcb0f86e2016-06-03 00:49:2957 bool /*send_request_headers_automatically*/,
xunjieli5749218c2016-03-22 16:43:0658 BidirectionalStreamImpl::Delegate* delegate,
danakjaee3e1ec2016-04-16 00:23:1859 std::unique_ptr<base::Timer> timer) {
xunjieli11834f02015-12-22 04:27:0860 DCHECK(!stream_);
61 DCHECK(timer);
62
63 delegate_ = delegate;
64 timer_ = std::move(timer);
65
66 if (!spdy_session_) {
xunjieli707f8952016-06-06 15:22:0667 base::ThreadTaskRunnerHandle::Get()->PostTask(
68 FROM_HERE,
69 base::Bind(&BidirectionalStreamSpdyImpl::NotifyError,
70 weak_factory_.GetWeakPtr(), ERR_CONNECTION_CLOSED));
xunjieli11834f02015-12-22 04:27:0871 return;
72 }
73
74 request_info_ = request_info;
75
76 int rv = stream_request_.StartRequest(
77 SPDY_BIDIRECTIONAL_STREAM, spdy_session_, request_info_->url,
78 request_info_->priority, net_log,
xunjieli5749218c2016-03-22 16:43:0679 base::Bind(&BidirectionalStreamSpdyImpl::OnStreamInitialized,
xunjieli11834f02015-12-22 04:27:0880 weak_factory_.GetWeakPtr()));
81 if (rv != ERR_IO_PENDING)
82 OnStreamInitialized(rv);
83}
84
xunjielibcb0f86e2016-06-03 00:49:2985void BidirectionalStreamSpdyImpl::SendRequestHeaders() {
86 // Request headers will be sent automatically.
87 NOTREACHED();
88}
89
xunjieli5749218c2016-03-22 16:43:0690int BidirectionalStreamSpdyImpl::ReadData(IOBuffer* buf, int buf_len) {
xunjieli11834f02015-12-22 04:27:0891 if (stream_)
92 DCHECK(!stream_->IsIdle());
93
94 DCHECK(buf);
95 DCHECK(buf_len);
96 DCHECK(!timer_->IsRunning()) << "There should be only one ReadData in flight";
97
98 // If there is data buffered, complete the IO immediately.
99 if (!read_data_queue_.IsEmpty()) {
100 return read_data_queue_.Dequeue(buf->data(), buf_len);
101 } else if (stream_closed_) {
102 return closed_stream_status_;
103 }
104 // Read will complete asynchronously and Delegate::OnReadCompleted will be
105 // called upon completion.
106 read_buffer_ = buf;
107 read_buffer_len_ = buf_len;
108 return ERR_IO_PENDING;
109}
110
xunjieli2328a2682016-05-16 19:38:25111void BidirectionalStreamSpdyImpl::SendData(const scoped_refptr<IOBuffer>& data,
xunjieli5749218c2016-03-22 16:43:06112 int length,
113 bool end_stream) {
xunjieli707f8952016-06-06 15:22:06114 DCHECK(length > 0 || (length == 0 && end_stream));
xunjieli4f8b6bb62016-10-31 23:16:00115 DCHECK(!write_pending_);
xunjieli11834f02015-12-22 04:27:08116
xunjieli4f8b6bb62016-10-31 23:16:00117 if (written_end_of_stream_) {
118 LOG(ERROR) << "Writing after end of stream is written.";
xunjieli707f8952016-06-06 15:22:06119 base::ThreadTaskRunnerHandle::Get()->PostTask(
120 FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::NotifyError,
121 weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
122 return;
123 }
124
xunjieli4f8b6bb62016-10-31 23:16:00125 write_pending_ = true;
126 written_end_of_stream_ = end_stream;
127 if (MaybeHandleStreamClosedInSendData())
128 return;
129
xunjieli707f8952016-06-06 15:22:06130 DCHECK(!stream_closed_);
xunjieli2328a2682016-05-16 19:38:25131 stream_->SendData(data.get(), length,
xunjieli11834f02015-12-22 04:27:08132 end_stream ? NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND);
133}
134
xunjieli07a42ce2016-04-26 20:05:31135void BidirectionalStreamSpdyImpl::SendvData(
xunjieli2328a2682016-05-16 19:38:25136 const std::vector<scoped_refptr<IOBuffer>>& buffers,
xunjieli07a42ce2016-04-26 20:05:31137 const std::vector<int>& lengths,
138 bool end_stream) {
xunjieli07a42ce2016-04-26 20:05:31139 DCHECK_EQ(buffers.size(), lengths.size());
xunjieli4f8b6bb62016-10-31 23:16:00140 DCHECK(!write_pending_);
xunjieli07a42ce2016-04-26 20:05:31141
xunjieli4f8b6bb62016-10-31 23:16:00142 if (written_end_of_stream_) {
143 LOG(ERROR) << "Writing after end of stream is written.";
xunjieli707f8952016-06-06 15:22:06144 base::ThreadTaskRunnerHandle::Get()->PostTask(
145 FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::NotifyError,
146 weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
147 return;
148 }
149
xunjieli4f8b6bb62016-10-31 23:16:00150 write_pending_ = true;
151 written_end_of_stream_ = end_stream;
152 if (MaybeHandleStreamClosedInSendData())
153 return;
154
xunjieli707f8952016-06-06 15:22:06155 DCHECK(!stream_closed_);
xunjieli07a42ce2016-04-26 20:05:31156 int total_len = 0;
157 for (int len : lengths) {
158 total_len += len;
159 }
160
161 pending_combined_buffer_ = new net::IOBuffer(total_len);
162 int len = 0;
163 // TODO(xunjieli): Get rid of extra copy. Coalesce headers and data frames.
164 for (size_t i = 0; i < buffers.size(); ++i) {
165 memcpy(pending_combined_buffer_->data() + len, buffers[i]->data(),
166 lengths[i]);
167 len += lengths[i];
168 }
169 stream_->SendData(pending_combined_buffer_.get(), total_len,
170 end_stream ? NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND);
171}
172
xunjieli5749218c2016-03-22 16:43:06173NextProto BidirectionalStreamSpdyImpl::GetProtocol() const {
xunjieli11834f02015-12-22 04:27:08174 return negotiated_protocol_;
175}
176
xunjieli5749218c2016-03-22 16:43:06177int64_t BidirectionalStreamSpdyImpl::GetTotalReceivedBytes() const {
xunjieli11834f02015-12-22 04:27:08178 if (stream_closed_)
179 return closed_stream_received_bytes_;
180
181 if (!stream_)
182 return 0;
183
184 return stream_->raw_received_bytes();
185}
186
xunjieli5749218c2016-03-22 16:43:06187int64_t BidirectionalStreamSpdyImpl::GetTotalSentBytes() const {
xunjieli11834f02015-12-22 04:27:08188 if (stream_closed_)
189 return closed_stream_sent_bytes_;
190
191 if (!stream_)
192 return 0;
193
194 return stream_->raw_sent_bytes();
195}
196
xunjieliba8de4322016-09-20 21:57:54197bool BidirectionalStreamSpdyImpl::GetLoadTimingInfo(
198 LoadTimingInfo* load_timing_info) const {
xunjieli6d0b944d2016-09-21 01:53:57199 if (stream_closed_) {
200 if (!closed_has_load_timing_info_)
201 return false;
202 *load_timing_info = closed_load_timing_info_;
203 return true;
204 }
205
206 // If |stream_| isn't created or has ID 0, return false. This is to match
207 // the implementation in SpdyHttpStream.
208 if (!stream_ || stream_->stream_id() == 0)
209 return false;
210
211 return stream_->GetLoadTimingInfo(load_timing_info);
xunjieliba8de4322016-09-20 21:57:54212}
213
bnc4c214312016-11-28 16:49:15214void BidirectionalStreamSpdyImpl::OnHeadersSent() {
xunjieli11834f02015-12-22 04:27:08215 DCHECK(stream_);
216
bncbca843ba2016-07-14 13:05:48217 negotiated_protocol_ = kProtoHTTP2;
xunjieli707f8952016-06-06 15:22:06218 if (delegate_)
219 delegate_->OnStreamReady(/*request_headers_sent=*/true);
xunjieli11834f02015-12-22 04:27:08220}
221
bnc4c214312016-11-28 16:49:15222void BidirectionalStreamSpdyImpl::OnHeadersReceived(
xunjieli11834f02015-12-22 04:27:08223 const SpdyHeaderBlock& response_headers) {
224 DCHECK(stream_);
225
xunjieli707f8952016-06-06 15:22:06226 if (delegate_)
227 delegate_->OnHeadersReceived(response_headers);
xunjieli11834f02015-12-22 04:27:08228}
229
xunjieli5749218c2016-03-22 16:43:06230void BidirectionalStreamSpdyImpl::OnDataReceived(
danakjaee3e1ec2016-04-16 00:23:18231 std::unique_ptr<SpdyBuffer> buffer) {
xunjieli11834f02015-12-22 04:27:08232 DCHECK(stream_);
233 DCHECK(!stream_closed_);
234
xunjieli5749218c2016-03-22 16:43:06235 // If |buffer| is null, BidirectionalStreamSpdyImpl::OnClose will be invoked
236 // by SpdyStream to indicate the end of stream.
xunjieli11834f02015-12-22 04:27:08237 if (!buffer)
238 return;
239
240 // When buffer is consumed, SpdyStream::OnReadBufferConsumed will adjust
241 // recv window size accordingly.
242 read_data_queue_.Enqueue(std::move(buffer));
243 if (read_buffer_) {
244 // Handing small chunks of data to the caller creates measurable overhead.
245 // So buffer data in short time-spans and send a single read notification.
246 ScheduleBufferedRead();
247 }
248}
249
xunjieli5749218c2016-03-22 16:43:06250void BidirectionalStreamSpdyImpl::OnDataSent() {
xunjieli4f8b6bb62016-10-31 23:16:00251 DCHECK(write_pending_);
xunjieli11834f02015-12-22 04:27:08252
xunjielibcb0f86e2016-06-03 00:49:29253 pending_combined_buffer_ = nullptr;
xunjieli4f8b6bb62016-10-31 23:16:00254 write_pending_ = false;
255
xunjieli707f8952016-06-06 15:22:06256 if (delegate_)
257 delegate_->OnDataSent();
xunjieli11834f02015-12-22 04:27:08258}
259
xunjieli5749218c2016-03-22 16:43:06260void BidirectionalStreamSpdyImpl::OnTrailers(const SpdyHeaderBlock& trailers) {
xunjieli11834f02015-12-22 04:27:08261 DCHECK(stream_);
262 DCHECK(!stream_closed_);
263
xunjieli707f8952016-06-06 15:22:06264 if (delegate_)
265 delegate_->OnTrailersReceived(trailers);
xunjieli11834f02015-12-22 04:27:08266}
267
xunjieli5749218c2016-03-22 16:43:06268void BidirectionalStreamSpdyImpl::OnClose(int status) {
xunjieli11834f02015-12-22 04:27:08269 DCHECK(stream_);
270
271 stream_closed_ = true;
272 closed_stream_status_ = status;
273 closed_stream_received_bytes_ = stream_->raw_received_bytes();
274 closed_stream_sent_bytes_ = stream_->raw_sent_bytes();
xunjieli6d0b944d2016-09-21 01:53:57275 closed_has_load_timing_info_ =
276 stream_->GetLoadTimingInfo(&closed_load_timing_info_);
xunjieli11834f02015-12-22 04:27:08277
278 if (status != OK) {
xunjieli707f8952016-06-06 15:22:06279 NotifyError(status);
xunjieli11834f02015-12-22 04:27:08280 return;
281 }
xunjieli707f8952016-06-06 15:22:06282 ResetStream();
xunjieli11834f02015-12-22 04:27:08283 // Complete any remaining read, as all data has been buffered.
284 // If user has not called ReadData (i.e |read_buffer_| is nullptr), this will
285 // do nothing.
286 timer_->Stop();
xunjieli4f8b6bb62016-10-31 23:16:00287
288 // |this| might get destroyed after calling into |delegate_| in
289 // DoBufferedRead().
290 auto weak_this = weak_factory_.GetWeakPtr();
xunjieli11834f02015-12-22 04:27:08291 DoBufferedRead();
xunjieli4f8b6bb62016-10-31 23:16:00292 if (weak_this.get() && write_pending_)
293 OnDataSent();
xunjieli11834f02015-12-22 04:27:08294}
295
xunjielibcb0f86e2016-06-03 00:49:29296int BidirectionalStreamSpdyImpl::SendRequestHeadersHelper() {
bnc1d1d86462016-07-20 16:51:55297 SpdyHeaderBlock headers;
xunjieli11834f02015-12-22 04:27:08298 HttpRequestInfo http_request_info;
299 http_request_info.url = request_info_->url;
300 http_request_info.method = request_info_->method;
301 http_request_info.extra_headers = request_info_->extra_headers;
302
303 CreateSpdyHeadersFromHttpRequest(
bnc1d1d86462016-07-20 16:51:55304 http_request_info, http_request_info.extra_headers, true, &headers);
xunjieli4f8b6bb62016-10-31 23:16:00305 written_end_of_stream_ = request_info_->end_stream_on_headers;
xunjielibcb0f86e2016-06-03 00:49:29306 return stream_->SendRequestHeaders(std::move(headers),
307 request_info_->end_stream_on_headers
308 ? NO_MORE_DATA_TO_SEND
309 : MORE_DATA_TO_SEND);
xunjieli11834f02015-12-22 04:27:08310}
311
xunjieli5749218c2016-03-22 16:43:06312void BidirectionalStreamSpdyImpl::OnStreamInitialized(int rv) {
xunjieli11834f02015-12-22 04:27:08313 DCHECK_NE(ERR_IO_PENDING, rv);
314 if (rv == OK) {
315 stream_ = stream_request_.ReleaseStream();
316 stream_->SetDelegate(this);
xunjielibcb0f86e2016-06-03 00:49:29317 rv = SendRequestHeadersHelper();
318 if (rv == OK) {
bnc4c214312016-11-28 16:49:15319 OnHeadersSent();
xunjielibcb0f86e2016-06-03 00:49:29320 return;
321 } else if (rv == ERR_IO_PENDING) {
322 return;
323 }
xunjieli11834f02015-12-22 04:27:08324 }
xunjieli707f8952016-06-06 15:22:06325 NotifyError(rv);
326}
327
328void BidirectionalStreamSpdyImpl::NotifyError(int rv) {
329 ResetStream();
xunjieli4f8b6bb62016-10-31 23:16:00330 write_pending_ = false;
xunjieli707f8952016-06-06 15:22:06331 if (delegate_) {
332 BidirectionalStreamImpl::Delegate* delegate = delegate_;
333 delegate_ = nullptr;
334 // Cancel any pending callback.
335 weak_factory_.InvalidateWeakPtrs();
336 delegate->OnFailed(rv);
337 // |this| can be null when returned from delegate.
338 }
339}
340
341void BidirectionalStreamSpdyImpl::ResetStream() {
342 if (!stream_)
343 return;
344 if (!stream_->IsClosed()) {
345 // This sends a RST to the remote.
346 stream_->DetachDelegate();
347 DCHECK(!stream_);
348 } else {
349 // Stream is already closed, so it is not legal to call DetachDelegate.
350 stream_.reset();
351 }
xunjieli11834f02015-12-22 04:27:08352}
353
xunjieli5749218c2016-03-22 16:43:06354void BidirectionalStreamSpdyImpl::ScheduleBufferedRead() {
xunjieli11834f02015-12-22 04:27:08355 // If there is already a scheduled DoBufferedRead, don't issue
356 // another one. Mark that we have received more data and return.
357 if (timer_->IsRunning()) {
358 more_read_data_pending_ = true;
359 return;
360 }
361
362 more_read_data_pending_ = false;
363 timer_->Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kBufferTimeMs),
xunjieli5749218c2016-03-22 16:43:06364 base::Bind(&BidirectionalStreamSpdyImpl::DoBufferedRead,
xunjieli11834f02015-12-22 04:27:08365 weak_factory_.GetWeakPtr()));
366}
367
xunjieli5749218c2016-03-22 16:43:06368void BidirectionalStreamSpdyImpl::DoBufferedRead() {
xunjieli11834f02015-12-22 04:27:08369 DCHECK(!timer_->IsRunning());
370 // Check to see that the stream has not errored out.
371 DCHECK(stream_ || stream_closed_);
372 DCHECK(!stream_closed_ || closed_stream_status_ == OK);
373
374 // When |more_read_data_pending_| is true, it means that more data has arrived
375 // since started waiting. Wait a little longer and continue to buffer.
376 if (more_read_data_pending_ && ShouldWaitForMoreBufferedData()) {
377 ScheduleBufferedRead();
378 return;
379 }
380
381 int rv = 0;
382 if (read_buffer_) {
383 rv = ReadData(read_buffer_.get(), read_buffer_len_);
384 DCHECK_NE(ERR_IO_PENDING, rv);
385 read_buffer_ = nullptr;
386 read_buffer_len_ = 0;
xunjieli707f8952016-06-06 15:22:06387 if (delegate_)
388 delegate_->OnDataRead(rv);
xunjieli11834f02015-12-22 04:27:08389 }
390}
391
xunjieli5749218c2016-03-22 16:43:06392bool BidirectionalStreamSpdyImpl::ShouldWaitForMoreBufferedData() const {
xunjieli11834f02015-12-22 04:27:08393 if (stream_closed_)
394 return false;
395 DCHECK_GT(read_buffer_len_, 0);
396 return read_data_queue_.GetTotalSize() <
397 static_cast<size_t>(read_buffer_len_);
398}
399
xunjieli4f8b6bb62016-10-31 23:16:00400bool BidirectionalStreamSpdyImpl::MaybeHandleStreamClosedInSendData() {
401 if (stream_)
402 return false;
403 // If |stream_| is closed without an error before client half closes,
404 // blackhole any pending write data. crbug.com/650438.
405 if (stream_closed_ && closed_stream_status_ == OK) {
406 base::ThreadTaskRunnerHandle::Get()->PostTask(
407 FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::OnDataSent,
408 weak_factory_.GetWeakPtr()));
409 return true;
410 }
411 LOG(ERROR) << "Trying to send data after stream has been destroyed.";
412 base::ThreadTaskRunnerHandle::Get()->PostTask(
413 FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::NotifyError,
414 weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
415 return true;
416}
417
xunjieli11834f02015-12-22 04:27:08418} // namespace net