blob: e926201edaa8a78847e6591de08cf59ae69bb5c0 [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(
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),
xunjieli6d0b944d2016-09-21 01:53:5748 closed_has_load_timing_info_(false),
xunjieli11834f02015-12-22 04:27:0849 weak_factory_(this) {}
50
xunjieli5749218c2016-03-22 16:43:0651BidirectionalStreamSpdyImpl::~BidirectionalStreamSpdyImpl() {
xunjieli9ff75c562016-08-10 20:26:1652 // Sends a RST to the remote if the stream is destroyed before it completes.
53 ResetStream();
xunjieli11834f02015-12-22 04:27:0854}
55
xunjieli5749218c2016-03-22 16:43:0656void BidirectionalStreamSpdyImpl::Start(
xunjieli11834f02015-12-22 04:27:0857 const BidirectionalStreamRequestInfo* request_info,
tfarina42834112016-09-22 13:38:2058 const NetLogWithSource& net_log,
xunjielibcb0f86e2016-06-03 00:49:2959 bool /*send_request_headers_automatically*/,
xunjieli5749218c2016-03-22 16:43:0660 BidirectionalStreamImpl::Delegate* delegate,
danakjaee3e1ec2016-04-16 00:23:1861 std::unique_ptr<base::Timer> timer) {
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,
71 base::Bind(&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,
80 request_info_->priority, net_log,
xunjieli5749218c2016-03-22 16:43:0681 base::Bind(&BidirectionalStreamSpdyImpl::OnStreamInitialized,
xunjieli11834f02015-12-22 04:27:0882 weak_factory_.GetWeakPtr()));
83 if (rv != ERR_IO_PENDING)
84 OnStreamInitialized(rv);
85}
86
xunjielibcb0f86e2016-06-03 00:49:2987void BidirectionalStreamSpdyImpl::SendRequestHeaders() {
88 // Request headers will be sent automatically.
89 NOTREACHED();
90}
91
xunjieli5749218c2016-03-22 16:43:0692int BidirectionalStreamSpdyImpl::ReadData(IOBuffer* buf, int buf_len) {
xunjieli11834f02015-12-22 04:27:0893 if (stream_)
94 DCHECK(!stream_->IsIdle());
95
96 DCHECK(buf);
97 DCHECK(buf_len);
98 DCHECK(!timer_->IsRunning()) << "There should be only one ReadData in flight";
99
100 // If there is data buffered, complete the IO immediately.
101 if (!read_data_queue_.IsEmpty()) {
102 return read_data_queue_.Dequeue(buf->data(), buf_len);
103 } else if (stream_closed_) {
104 return closed_stream_status_;
105 }
106 // Read will complete asynchronously and Delegate::OnReadCompleted will be
107 // called upon completion.
108 read_buffer_ = buf;
109 read_buffer_len_ = buf_len;
110 return ERR_IO_PENDING;
111}
112
xunjieli2328a2682016-05-16 19:38:25113void BidirectionalStreamSpdyImpl::SendData(const scoped_refptr<IOBuffer>& data,
xunjieli5749218c2016-03-22 16:43:06114 int length,
115 bool end_stream) {
xunjieli707f8952016-06-06 15:22:06116 DCHECK(length > 0 || (length == 0 && end_stream));
xunjieli4f8b6bb62016-10-31 23:16:00117 DCHECK(!write_pending_);
xunjieli11834f02015-12-22 04:27:08118
xunjieli4f8b6bb62016-10-31 23:16:00119 if (written_end_of_stream_) {
120 LOG(ERROR) << "Writing after end of stream is written.";
xunjieli707f8952016-06-06 15:22:06121 base::ThreadTaskRunnerHandle::Get()->PostTask(
122 FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::NotifyError,
123 weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
124 return;
125 }
126
xunjieli4f8b6bb62016-10-31 23:16:00127 write_pending_ = true;
128 written_end_of_stream_ = end_stream;
129 if (MaybeHandleStreamClosedInSendData())
130 return;
131
xunjieli707f8952016-06-06 15:22:06132 DCHECK(!stream_closed_);
xunjieli2328a2682016-05-16 19:38:25133 stream_->SendData(data.get(), length,
xunjieli11834f02015-12-22 04:27:08134 end_stream ? NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND);
135}
136
xunjieli07a42ce2016-04-26 20:05:31137void BidirectionalStreamSpdyImpl::SendvData(
xunjieli2328a2682016-05-16 19:38:25138 const std::vector<scoped_refptr<IOBuffer>>& buffers,
xunjieli07a42ce2016-04-26 20:05:31139 const std::vector<int>& lengths,
140 bool end_stream) {
xunjieli07a42ce2016-04-26 20:05:31141 DCHECK_EQ(buffers.size(), lengths.size());
xunjieli4f8b6bb62016-10-31 23:16:00142 DCHECK(!write_pending_);
xunjieli07a42ce2016-04-26 20:05:31143
xunjieli4f8b6bb62016-10-31 23:16:00144 if (written_end_of_stream_) {
145 LOG(ERROR) << "Writing after end of stream is written.";
xunjieli707f8952016-06-06 15:22:06146 base::ThreadTaskRunnerHandle::Get()->PostTask(
147 FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::NotifyError,
148 weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
149 return;
150 }
151
xunjieli4f8b6bb62016-10-31 23:16:00152 write_pending_ = true;
153 written_end_of_stream_ = end_stream;
154 if (MaybeHandleStreamClosedInSendData())
155 return;
156
xunjieli707f8952016-06-06 15:22:06157 DCHECK(!stream_closed_);
xunjieli07a42ce2016-04-26 20:05:31158 int total_len = 0;
159 for (int len : lengths) {
160 total_len += len;
161 }
162
163 pending_combined_buffer_ = new net::IOBuffer(total_len);
164 int len = 0;
165 // TODO(xunjieli): Get rid of extra copy. Coalesce headers and data frames.
166 for (size_t i = 0; i < buffers.size(); ++i) {
167 memcpy(pending_combined_buffer_->data() + len, buffers[i]->data(),
168 lengths[i]);
169 len += lengths[i];
170 }
171 stream_->SendData(pending_combined_buffer_.get(), total_len,
172 end_stream ? NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND);
173}
174
xunjieli5749218c2016-03-22 16:43:06175NextProto BidirectionalStreamSpdyImpl::GetProtocol() const {
xunjieli11834f02015-12-22 04:27:08176 return negotiated_protocol_;
177}
178
xunjieli5749218c2016-03-22 16:43:06179int64_t BidirectionalStreamSpdyImpl::GetTotalReceivedBytes() const {
xunjieli11834f02015-12-22 04:27:08180 if (stream_closed_)
181 return closed_stream_received_bytes_;
182
183 if (!stream_)
184 return 0;
185
186 return stream_->raw_received_bytes();
187}
188
xunjieli5749218c2016-03-22 16:43:06189int64_t BidirectionalStreamSpdyImpl::GetTotalSentBytes() const {
xunjieli11834f02015-12-22 04:27:08190 if (stream_closed_)
191 return closed_stream_sent_bytes_;
192
193 if (!stream_)
194 return 0;
195
196 return stream_->raw_sent_bytes();
197}
198
xunjieliba8de4322016-09-20 21:57:54199bool BidirectionalStreamSpdyImpl::GetLoadTimingInfo(
200 LoadTimingInfo* load_timing_info) const {
xunjieli6d0b944d2016-09-21 01:53:57201 if (stream_closed_) {
202 if (!closed_has_load_timing_info_)
203 return false;
204 *load_timing_info = closed_load_timing_info_;
205 return true;
206 }
207
208 // If |stream_| isn't created or has ID 0, return false. This is to match
209 // the implementation in SpdyHttpStream.
210 if (!stream_ || stream_->stream_id() == 0)
211 return false;
212
213 return stream_->GetLoadTimingInfo(load_timing_info);
xunjieliba8de4322016-09-20 21:57:54214}
215
bnc4c214312016-11-28 16:49:15216void BidirectionalStreamSpdyImpl::OnHeadersSent() {
xunjieli11834f02015-12-22 04:27:08217 DCHECK(stream_);
218
bncbca843ba2016-07-14 13:05:48219 negotiated_protocol_ = kProtoHTTP2;
xunjieli707f8952016-06-06 15:22:06220 if (delegate_)
221 delegate_->OnStreamReady(/*request_headers_sent=*/true);
xunjieli11834f02015-12-22 04:27:08222}
223
bnc4c214312016-11-28 16:49:15224void BidirectionalStreamSpdyImpl::OnHeadersReceived(
xunjieli11834f02015-12-22 04:27:08225 const SpdyHeaderBlock& response_headers) {
226 DCHECK(stream_);
227
xunjieli707f8952016-06-06 15:22:06228 if (delegate_)
229 delegate_->OnHeadersReceived(response_headers);
xunjieli11834f02015-12-22 04:27:08230}
231
xunjieli5749218c2016-03-22 16:43:06232void BidirectionalStreamSpdyImpl::OnDataReceived(
danakjaee3e1ec2016-04-16 00:23:18233 std::unique_ptr<SpdyBuffer> buffer) {
xunjieli11834f02015-12-22 04:27:08234 DCHECK(stream_);
235 DCHECK(!stream_closed_);
236
xunjieli5749218c2016-03-22 16:43:06237 // If |buffer| is null, BidirectionalStreamSpdyImpl::OnClose will be invoked
238 // by SpdyStream to indicate the end of stream.
xunjieli11834f02015-12-22 04:27:08239 if (!buffer)
240 return;
241
242 // When buffer is consumed, SpdyStream::OnReadBufferConsumed will adjust
243 // recv window size accordingly.
244 read_data_queue_.Enqueue(std::move(buffer));
245 if (read_buffer_) {
246 // Handing small chunks of data to the caller creates measurable overhead.
247 // So buffer data in short time-spans and send a single read notification.
248 ScheduleBufferedRead();
249 }
250}
251
xunjieli5749218c2016-03-22 16:43:06252void BidirectionalStreamSpdyImpl::OnDataSent() {
xunjieli4f8b6bb62016-10-31 23:16:00253 DCHECK(write_pending_);
xunjieli11834f02015-12-22 04:27:08254
xunjielibcb0f86e2016-06-03 00:49:29255 pending_combined_buffer_ = nullptr;
xunjieli4f8b6bb62016-10-31 23:16:00256 write_pending_ = false;
257
xunjieli707f8952016-06-06 15:22:06258 if (delegate_)
259 delegate_->OnDataSent();
xunjieli11834f02015-12-22 04:27:08260}
261
xunjieli5749218c2016-03-22 16:43:06262void BidirectionalStreamSpdyImpl::OnTrailers(const SpdyHeaderBlock& trailers) {
xunjieli11834f02015-12-22 04:27:08263 DCHECK(stream_);
264 DCHECK(!stream_closed_);
265
xunjieli707f8952016-06-06 15:22:06266 if (delegate_)
267 delegate_->OnTrailersReceived(trailers);
xunjieli11834f02015-12-22 04:27:08268}
269
xunjieli5749218c2016-03-22 16:43:06270void BidirectionalStreamSpdyImpl::OnClose(int status) {
xunjieli11834f02015-12-22 04:27:08271 DCHECK(stream_);
272
273 stream_closed_ = true;
274 closed_stream_status_ = status;
275 closed_stream_received_bytes_ = stream_->raw_received_bytes();
276 closed_stream_sent_bytes_ = stream_->raw_sent_bytes();
xunjieli6d0b944d2016-09-21 01:53:57277 closed_has_load_timing_info_ =
278 stream_->GetLoadTimingInfo(&closed_load_timing_info_);
xunjieli11834f02015-12-22 04:27:08279
280 if (status != OK) {
xunjieli707f8952016-06-06 15:22:06281 NotifyError(status);
xunjieli11834f02015-12-22 04:27:08282 return;
283 }
xunjieli707f8952016-06-06 15:22:06284 ResetStream();
xunjieli11834f02015-12-22 04:27:08285 // Complete any remaining read, as all data has been buffered.
286 // If user has not called ReadData (i.e |read_buffer_| is nullptr), this will
287 // do nothing.
288 timer_->Stop();
xunjieli4f8b6bb62016-10-31 23:16:00289
290 // |this| might get destroyed after calling into |delegate_| in
291 // DoBufferedRead().
292 auto weak_this = weak_factory_.GetWeakPtr();
xunjieli11834f02015-12-22 04:27:08293 DoBufferedRead();
xunjieli4f8b6bb62016-10-31 23:16:00294 if (weak_this.get() && write_pending_)
295 OnDataSent();
xunjieli11834f02015-12-22 04:27:08296}
297
bnc3d95ca92017-03-29 13:20:34298NetLogSource BidirectionalStreamSpdyImpl::source_dependency() const {
299 return source_dependency_;
300}
301
xunjielibcb0f86e2016-06-03 00:49:29302int BidirectionalStreamSpdyImpl::SendRequestHeadersHelper() {
bnc1d1d86462016-07-20 16:51:55303 SpdyHeaderBlock headers;
xunjieli11834f02015-12-22 04:27:08304 HttpRequestInfo http_request_info;
305 http_request_info.url = request_info_->url;
306 http_request_info.method = request_info_->method;
307 http_request_info.extra_headers = request_info_->extra_headers;
308
309 CreateSpdyHeadersFromHttpRequest(
bnc1d1d86462016-07-20 16:51:55310 http_request_info, http_request_info.extra_headers, true, &headers);
xunjieli4f8b6bb62016-10-31 23:16:00311 written_end_of_stream_ = request_info_->end_stream_on_headers;
xunjielibcb0f86e2016-06-03 00:49:29312 return stream_->SendRequestHeaders(std::move(headers),
313 request_info_->end_stream_on_headers
314 ? NO_MORE_DATA_TO_SEND
315 : MORE_DATA_TO_SEND);
xunjieli11834f02015-12-22 04:27:08316}
317
xunjieli5749218c2016-03-22 16:43:06318void BidirectionalStreamSpdyImpl::OnStreamInitialized(int rv) {
xunjieli11834f02015-12-22 04:27:08319 DCHECK_NE(ERR_IO_PENDING, rv);
320 if (rv == OK) {
321 stream_ = stream_request_.ReleaseStream();
322 stream_->SetDelegate(this);
xunjielibcb0f86e2016-06-03 00:49:29323 rv = SendRequestHeadersHelper();
324 if (rv == OK) {
bnc4c214312016-11-28 16:49:15325 OnHeadersSent();
xunjielibcb0f86e2016-06-03 00:49:29326 return;
327 } else if (rv == ERR_IO_PENDING) {
328 return;
329 }
xunjieli11834f02015-12-22 04:27:08330 }
xunjieli707f8952016-06-06 15:22:06331 NotifyError(rv);
332}
333
334void BidirectionalStreamSpdyImpl::NotifyError(int rv) {
335 ResetStream();
xunjieli4f8b6bb62016-10-31 23:16:00336 write_pending_ = false;
xunjieli707f8952016-06-06 15:22:06337 if (delegate_) {
338 BidirectionalStreamImpl::Delegate* delegate = delegate_;
339 delegate_ = nullptr;
340 // Cancel any pending callback.
341 weak_factory_.InvalidateWeakPtrs();
342 delegate->OnFailed(rv);
343 // |this| can be null when returned from delegate.
344 }
345}
346
347void BidirectionalStreamSpdyImpl::ResetStream() {
348 if (!stream_)
349 return;
350 if (!stream_->IsClosed()) {
351 // This sends a RST to the remote.
352 stream_->DetachDelegate();
353 DCHECK(!stream_);
354 } else {
355 // Stream is already closed, so it is not legal to call DetachDelegate.
356 stream_.reset();
357 }
xunjieli11834f02015-12-22 04:27:08358}
359
xunjieli5749218c2016-03-22 16:43:06360void BidirectionalStreamSpdyImpl::ScheduleBufferedRead() {
xunjieli11834f02015-12-22 04:27:08361 // If there is already a scheduled DoBufferedRead, don't issue
362 // another one. Mark that we have received more data and return.
363 if (timer_->IsRunning()) {
364 more_read_data_pending_ = true;
365 return;
366 }
367
368 more_read_data_pending_ = false;
369 timer_->Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kBufferTimeMs),
xunjieli5749218c2016-03-22 16:43:06370 base::Bind(&BidirectionalStreamSpdyImpl::DoBufferedRead,
xunjieli11834f02015-12-22 04:27:08371 weak_factory_.GetWeakPtr()));
372}
373
xunjieli5749218c2016-03-22 16:43:06374void BidirectionalStreamSpdyImpl::DoBufferedRead() {
xunjieli11834f02015-12-22 04:27:08375 DCHECK(!timer_->IsRunning());
376 // Check to see that the stream has not errored out.
377 DCHECK(stream_ || stream_closed_);
378 DCHECK(!stream_closed_ || closed_stream_status_ == OK);
379
380 // When |more_read_data_pending_| is true, it means that more data has arrived
381 // since started waiting. Wait a little longer and continue to buffer.
382 if (more_read_data_pending_ && ShouldWaitForMoreBufferedData()) {
383 ScheduleBufferedRead();
384 return;
385 }
386
387 int rv = 0;
388 if (read_buffer_) {
389 rv = ReadData(read_buffer_.get(), read_buffer_len_);
390 DCHECK_NE(ERR_IO_PENDING, rv);
391 read_buffer_ = nullptr;
392 read_buffer_len_ = 0;
xunjieli707f8952016-06-06 15:22:06393 if (delegate_)
394 delegate_->OnDataRead(rv);
xunjieli11834f02015-12-22 04:27:08395 }
396}
397
xunjieli5749218c2016-03-22 16:43:06398bool BidirectionalStreamSpdyImpl::ShouldWaitForMoreBufferedData() const {
xunjieli11834f02015-12-22 04:27:08399 if (stream_closed_)
400 return false;
401 DCHECK_GT(read_buffer_len_, 0);
402 return read_data_queue_.GetTotalSize() <
403 static_cast<size_t>(read_buffer_len_);
404}
405
xunjieli4f8b6bb62016-10-31 23:16:00406bool BidirectionalStreamSpdyImpl::MaybeHandleStreamClosedInSendData() {
407 if (stream_)
408 return false;
409 // If |stream_| is closed without an error before client half closes,
410 // blackhole any pending write data. crbug.com/650438.
411 if (stream_closed_ && closed_stream_status_ == OK) {
412 base::ThreadTaskRunnerHandle::Get()->PostTask(
413 FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::OnDataSent,
414 weak_factory_.GetWeakPtr()));
415 return true;
416 }
417 LOG(ERROR) << "Trying to send data after stream has been destroyed.";
418 base::ThreadTaskRunnerHandle::Get()->PostTask(
419 FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::NotifyError,
420 weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
421 return true;
422}
423
xunjieli11834f02015-12-22 04:27:08424} // namespace net