blob: 8f55edd16adf17d2575955e1469cbdd8bc262f69 [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
7#include "base/bind.h"
8#include "base/location.h"
9#include "base/logging.h"
10#include "base/time/time.h"
11#include "base/timer/timer.h"
12#include "net/http/bidirectional_stream_request_info.h"
13#include "net/spdy/spdy_buffer.h"
14#include "net/spdy/spdy_header_block.h"
15#include "net/spdy/spdy_http_utils.h"
16#include "net/spdy/spdy_stream.h"
17
18namespace net {
19
20namespace {
21
22// Time to wait in millisecond to notify |delegate_| of data received.
23// Handing small chunks of data to the caller creates measurable overhead.
24// So buffer data in short time-spans and send a single read notification.
mefe666e9a2015-12-30 23:41:3025const int kBufferTimeMs = 1;
xunjieli11834f02015-12-22 04:27:0826
27} // namespace
28
xunjieli5749218c2016-03-22 16:43:0629BidirectionalStreamSpdyImpl::BidirectionalStreamSpdyImpl(
xunjieli11834f02015-12-22 04:27:0830 const base::WeakPtr<SpdySession>& spdy_session)
31 : spdy_session_(spdy_session),
32 request_info_(nullptr),
33 delegate_(nullptr),
34 negotiated_protocol_(kProtoUnknown),
35 more_read_data_pending_(false),
36 read_buffer_len_(0),
37 stream_closed_(false),
38 closed_stream_status_(ERR_FAILED),
39 closed_stream_received_bytes_(0),
40 closed_stream_sent_bytes_(0),
xunjieli6d0b944d2016-09-21 01:53:5741 closed_has_load_timing_info_(false),
xunjieli11834f02015-12-22 04:27:0842 weak_factory_(this) {}
43
xunjieli5749218c2016-03-22 16:43:0644BidirectionalStreamSpdyImpl::~BidirectionalStreamSpdyImpl() {
xunjieli9ff75c562016-08-10 20:26:1645 // Sends a RST to the remote if the stream is destroyed before it completes.
46 ResetStream();
xunjieli11834f02015-12-22 04:27:0847}
48
xunjieli5749218c2016-03-22 16:43:0649void BidirectionalStreamSpdyImpl::Start(
xunjieli11834f02015-12-22 04:27:0850 const BidirectionalStreamRequestInfo* request_info,
51 const BoundNetLog& net_log,
xunjielibcb0f86e2016-06-03 00:49:2952 bool /*send_request_headers_automatically*/,
xunjieli5749218c2016-03-22 16:43:0653 BidirectionalStreamImpl::Delegate* delegate,
danakjaee3e1ec2016-04-16 00:23:1854 std::unique_ptr<base::Timer> timer) {
xunjieli11834f02015-12-22 04:27:0855 DCHECK(!stream_);
56 DCHECK(timer);
57
58 delegate_ = delegate;
59 timer_ = std::move(timer);
60
61 if (!spdy_session_) {
xunjieli707f8952016-06-06 15:22:0662 base::ThreadTaskRunnerHandle::Get()->PostTask(
63 FROM_HERE,
64 base::Bind(&BidirectionalStreamSpdyImpl::NotifyError,
65 weak_factory_.GetWeakPtr(), ERR_CONNECTION_CLOSED));
xunjieli11834f02015-12-22 04:27:0866 return;
67 }
68
69 request_info_ = request_info;
70
71 int rv = stream_request_.StartRequest(
72 SPDY_BIDIRECTIONAL_STREAM, spdy_session_, request_info_->url,
73 request_info_->priority, net_log,
xunjieli5749218c2016-03-22 16:43:0674 base::Bind(&BidirectionalStreamSpdyImpl::OnStreamInitialized,
xunjieli11834f02015-12-22 04:27:0875 weak_factory_.GetWeakPtr()));
76 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
xunjieli2328a2682016-05-16 19:38:25106void BidirectionalStreamSpdyImpl::SendData(const scoped_refptr<IOBuffer>& data,
xunjieli5749218c2016-03-22 16:43:06107 int length,
108 bool end_stream) {
xunjieli707f8952016-06-06 15:22:06109 DCHECK(length > 0 || (length == 0 && end_stream));
xunjieli11834f02015-12-22 04:27:08110
xunjieli707f8952016-06-06 15:22:06111 if (!stream_) {
112 LOG(ERROR) << "Trying to send data after stream has been destroyed.";
113 base::ThreadTaskRunnerHandle::Get()->PostTask(
114 FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::NotifyError,
115 weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
116 return;
117 }
118
119 DCHECK(!stream_closed_);
xunjieli2328a2682016-05-16 19:38:25120 stream_->SendData(data.get(), length,
xunjieli11834f02015-12-22 04:27:08121 end_stream ? NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND);
122}
123
xunjieli07a42ce2016-04-26 20:05:31124void BidirectionalStreamSpdyImpl::SendvData(
xunjieli2328a2682016-05-16 19:38:25125 const std::vector<scoped_refptr<IOBuffer>>& buffers,
xunjieli07a42ce2016-04-26 20:05:31126 const std::vector<int>& lengths,
127 bool end_stream) {
xunjieli07a42ce2016-04-26 20:05:31128 DCHECK_EQ(buffers.size(), lengths.size());
129
xunjieli707f8952016-06-06 15:22:06130 if (!stream_) {
131 LOG(ERROR) << "Trying to send data after stream has been destroyed.";
132 base::ThreadTaskRunnerHandle::Get()->PostTask(
133 FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::NotifyError,
134 weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
135 return;
136 }
137
138 DCHECK(!stream_closed_);
xunjieli07a42ce2016-04-26 20:05:31139 int total_len = 0;
140 for (int len : lengths) {
141 total_len += len;
142 }
143
144 pending_combined_buffer_ = new net::IOBuffer(total_len);
145 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 }
152 stream_->SendData(pending_combined_buffer_.get(), total_len,
153 end_stream ? NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND);
154}
155
xunjieli5749218c2016-03-22 16:43:06156NextProto BidirectionalStreamSpdyImpl::GetProtocol() const {
xunjieli11834f02015-12-22 04:27:08157 return negotiated_protocol_;
158}
159
xunjieli5749218c2016-03-22 16:43:06160int64_t BidirectionalStreamSpdyImpl::GetTotalReceivedBytes() const {
xunjieli11834f02015-12-22 04:27:08161 if (stream_closed_)
162 return closed_stream_received_bytes_;
163
164 if (!stream_)
165 return 0;
166
167 return stream_->raw_received_bytes();
168}
169
xunjieli5749218c2016-03-22 16:43:06170int64_t BidirectionalStreamSpdyImpl::GetTotalSentBytes() const {
xunjieli11834f02015-12-22 04:27:08171 if (stream_closed_)
172 return closed_stream_sent_bytes_;
173
174 if (!stream_)
175 return 0;
176
177 return stream_->raw_sent_bytes();
178}
179
xunjieliba8de4322016-09-20 21:57:54180bool BidirectionalStreamSpdyImpl::GetLoadTimingInfo(
181 LoadTimingInfo* load_timing_info) const {
xunjieli6d0b944d2016-09-21 01:53:57182 if (stream_closed_) {
183 if (!closed_has_load_timing_info_)
184 return false;
185 *load_timing_info = closed_load_timing_info_;
186 return true;
187 }
188
189 // If |stream_| isn't created or has ID 0, return false. This is to match
190 // the implementation in SpdyHttpStream.
191 if (!stream_ || stream_->stream_id() == 0)
192 return false;
193
194 return stream_->GetLoadTimingInfo(load_timing_info);
xunjieliba8de4322016-09-20 21:57:54195}
196
xunjieli5749218c2016-03-22 16:43:06197void BidirectionalStreamSpdyImpl::OnRequestHeadersSent() {
xunjieli11834f02015-12-22 04:27:08198 DCHECK(stream_);
199
bncbca843ba2016-07-14 13:05:48200 negotiated_protocol_ = kProtoHTTP2;
xunjieli707f8952016-06-06 15:22:06201 if (delegate_)
202 delegate_->OnStreamReady(/*request_headers_sent=*/true);
xunjieli11834f02015-12-22 04:27:08203}
204
xunjieli5749218c2016-03-22 16:43:06205SpdyResponseHeadersStatus BidirectionalStreamSpdyImpl::OnResponseHeadersUpdated(
xunjieli11834f02015-12-22 04:27:08206 const SpdyHeaderBlock& response_headers) {
207 DCHECK(stream_);
208
xunjieli707f8952016-06-06 15:22:06209 if (delegate_)
210 delegate_->OnHeadersReceived(response_headers);
211
xunjieli11834f02015-12-22 04:27:08212 return RESPONSE_HEADERS_ARE_COMPLETE;
213}
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() {
xunjieli11834f02015-12-22 04:27:08236 DCHECK(stream_);
237 DCHECK(!stream_closed_);
238
xunjielibcb0f86e2016-06-03 00:49:29239 pending_combined_buffer_ = nullptr;
xunjieli707f8952016-06-06 15:22:06240 if (delegate_)
241 delegate_->OnDataSent();
xunjieli11834f02015-12-22 04:27:08242}
243
xunjieli5749218c2016-03-22 16:43:06244void BidirectionalStreamSpdyImpl::OnTrailers(const SpdyHeaderBlock& trailers) {
xunjieli11834f02015-12-22 04:27:08245 DCHECK(stream_);
246 DCHECK(!stream_closed_);
247
xunjieli707f8952016-06-06 15:22:06248 if (delegate_)
249 delegate_->OnTrailersReceived(trailers);
xunjieli11834f02015-12-22 04:27:08250}
251
xunjieli5749218c2016-03-22 16:43:06252void BidirectionalStreamSpdyImpl::OnClose(int status) {
xunjieli11834f02015-12-22 04:27:08253 DCHECK(stream_);
254
255 stream_closed_ = true;
256 closed_stream_status_ = status;
257 closed_stream_received_bytes_ = stream_->raw_received_bytes();
258 closed_stream_sent_bytes_ = stream_->raw_sent_bytes();
xunjieli6d0b944d2016-09-21 01:53:57259 closed_has_load_timing_info_ =
260 stream_->GetLoadTimingInfo(&closed_load_timing_info_);
xunjieli11834f02015-12-22 04:27:08261
262 if (status != OK) {
xunjieli707f8952016-06-06 15:22:06263 NotifyError(status);
xunjieli11834f02015-12-22 04:27:08264 return;
265 }
xunjieli707f8952016-06-06 15:22:06266 ResetStream();
xunjieli11834f02015-12-22 04:27:08267 // Complete any remaining read, as all data has been buffered.
268 // If user has not called ReadData (i.e |read_buffer_| is nullptr), this will
269 // do nothing.
270 timer_->Stop();
271 DoBufferedRead();
272}
273
xunjielibcb0f86e2016-06-03 00:49:29274int BidirectionalStreamSpdyImpl::SendRequestHeadersHelper() {
bnc1d1d86462016-07-20 16:51:55275 SpdyHeaderBlock headers;
xunjieli11834f02015-12-22 04:27:08276 HttpRequestInfo http_request_info;
277 http_request_info.url = request_info_->url;
278 http_request_info.method = request_info_->method;
279 http_request_info.extra_headers = request_info_->extra_headers;
280
281 CreateSpdyHeadersFromHttpRequest(
bnc1d1d86462016-07-20 16:51:55282 http_request_info, http_request_info.extra_headers, true, &headers);
xunjielibcb0f86e2016-06-03 00:49:29283 return stream_->SendRequestHeaders(std::move(headers),
284 request_info_->end_stream_on_headers
285 ? NO_MORE_DATA_TO_SEND
286 : MORE_DATA_TO_SEND);
xunjieli11834f02015-12-22 04:27:08287}
288
xunjieli5749218c2016-03-22 16:43:06289void BidirectionalStreamSpdyImpl::OnStreamInitialized(int rv) {
xunjieli11834f02015-12-22 04:27:08290 DCHECK_NE(ERR_IO_PENDING, rv);
291 if (rv == OK) {
292 stream_ = stream_request_.ReleaseStream();
293 stream_->SetDelegate(this);
xunjielibcb0f86e2016-06-03 00:49:29294 rv = SendRequestHeadersHelper();
295 if (rv == OK) {
296 OnRequestHeadersSent();
297 return;
298 } else if (rv == ERR_IO_PENDING) {
299 return;
300 }
xunjieli11834f02015-12-22 04:27:08301 }
xunjieli707f8952016-06-06 15:22:06302 NotifyError(rv);
303}
304
305void BidirectionalStreamSpdyImpl::NotifyError(int rv) {
306 ResetStream();
307 if (delegate_) {
308 BidirectionalStreamImpl::Delegate* delegate = delegate_;
309 delegate_ = nullptr;
310 // Cancel any pending callback.
311 weak_factory_.InvalidateWeakPtrs();
312 delegate->OnFailed(rv);
313 // |this| can be null when returned from delegate.
314 }
315}
316
317void BidirectionalStreamSpdyImpl::ResetStream() {
318 if (!stream_)
319 return;
320 if (!stream_->IsClosed()) {
321 // This sends a RST to the remote.
322 stream_->DetachDelegate();
323 DCHECK(!stream_);
324 } else {
325 // Stream is already closed, so it is not legal to call DetachDelegate.
326 stream_.reset();
327 }
xunjieli11834f02015-12-22 04:27:08328}
329
xunjieli5749218c2016-03-22 16:43:06330void BidirectionalStreamSpdyImpl::ScheduleBufferedRead() {
xunjieli11834f02015-12-22 04:27:08331 // If there is already a scheduled DoBufferedRead, don't issue
332 // another one. Mark that we have received more data and return.
333 if (timer_->IsRunning()) {
334 more_read_data_pending_ = true;
335 return;
336 }
337
338 more_read_data_pending_ = false;
339 timer_->Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kBufferTimeMs),
xunjieli5749218c2016-03-22 16:43:06340 base::Bind(&BidirectionalStreamSpdyImpl::DoBufferedRead,
xunjieli11834f02015-12-22 04:27:08341 weak_factory_.GetWeakPtr()));
342}
343
xunjieli5749218c2016-03-22 16:43:06344void BidirectionalStreamSpdyImpl::DoBufferedRead() {
xunjieli11834f02015-12-22 04:27:08345 DCHECK(!timer_->IsRunning());
346 // Check to see that the stream has not errored out.
347 DCHECK(stream_ || stream_closed_);
348 DCHECK(!stream_closed_ || closed_stream_status_ == OK);
349
350 // When |more_read_data_pending_| is true, it means that more data has arrived
351 // since started waiting. Wait a little longer and continue to buffer.
352 if (more_read_data_pending_ && ShouldWaitForMoreBufferedData()) {
353 ScheduleBufferedRead();
354 return;
355 }
356
357 int rv = 0;
358 if (read_buffer_) {
359 rv = ReadData(read_buffer_.get(), read_buffer_len_);
360 DCHECK_NE(ERR_IO_PENDING, rv);
361 read_buffer_ = nullptr;
362 read_buffer_len_ = 0;
xunjieli707f8952016-06-06 15:22:06363 if (delegate_)
364 delegate_->OnDataRead(rv);
xunjieli11834f02015-12-22 04:27:08365 }
366}
367
xunjieli5749218c2016-03-22 16:43:06368bool BidirectionalStreamSpdyImpl::ShouldWaitForMoreBufferedData() const {
xunjieli11834f02015-12-22 04:27:08369 if (stream_closed_)
370 return false;
371 DCHECK_GT(read_buffer_len_, 0);
372 return read_data_queue_.GetTotalSize() <
373 static_cast<size_t>(read_buffer_len_);
374}
375
376} // namespace net