1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/spdy/spdy_http_stream.h"
6
7 #include <algorithm>
8 #include <list>
9 #include <set>
10 #include <string>
11 #include <utility>
12
13 #include "base/check_op.h"
14 #include "base/functional/bind.h"
15 #include "base/location.h"
16 #include "base/metrics/histogram_macros.h"
17 #include "base/task/single_thread_task_runner.h"
18 #include "base/values.h"
19 #include "net/base/ip_endpoint.h"
20 #include "net/base/upload_data_stream.h"
21 #include "net/http/http_request_headers.h"
22 #include "net/http/http_request_info.h"
23 #include "net/http/http_response_info.h"
24 #include "net/log/net_log_event_type.h"
25 #include "net/log/net_log_with_source.h"
26 #include "net/spdy/spdy_http_utils.h"
27 #include "net/spdy/spdy_log_util.h"
28 #include "net/spdy/spdy_session.h"
29 #include "net/third_party/quiche/src/quiche/spdy/core/http2_header_block.h"
30 #include "net/third_party/quiche/src/quiche/spdy/core/spdy_protocol.h"
31 #include "url/scheme_host_port.h"
32
33 namespace net {
34
35 namespace {
36
ValidatePushedHeaders(const HttpRequestInfo & request_info,const spdy::Http2HeaderBlock & pushed_request_headers,const spdy::Http2HeaderBlock & pushed_response_headers,const HttpResponseInfo & pushed_response_info)37 bool ValidatePushedHeaders(
38 const HttpRequestInfo& request_info,
39 const spdy::Http2HeaderBlock& pushed_request_headers,
40 const spdy::Http2HeaderBlock& pushed_response_headers,
41 const HttpResponseInfo& pushed_response_info) {
42 spdy::Http2HeaderBlock::const_iterator status_it =
43 pushed_response_headers.find(spdy::kHttp2StatusHeader);
44 DCHECK(status_it != pushed_response_headers.end());
45 // 206 Partial Content and 416 Requested Range Not Satisfiable are range
46 // responses.
47 if (status_it->second == "206" || status_it->second == "416") {
48 std::string client_request_range;
49 if (!request_info.extra_headers.GetHeader(HttpRequestHeaders::kRange,
50 &client_request_range)) {
51 // Client initiated request is not a range request.
52 SpdySession::RecordSpdyPushedStreamFateHistogram(
53 SpdyPushedStreamFate::kClientRequestNotRange);
54 return false;
55 }
56 spdy::Http2HeaderBlock::const_iterator pushed_request_range_it =
57 pushed_request_headers.find("range");
58 if (pushed_request_range_it == pushed_request_headers.end()) {
59 // Pushed request is not a range request.
60 SpdySession::RecordSpdyPushedStreamFateHistogram(
61 SpdyPushedStreamFate::kPushedRequestNotRange);
62 return false;
63 }
64 if (client_request_range != pushed_request_range_it->second) {
65 // Client and pushed request ranges do not match.
66 SpdySession::RecordSpdyPushedStreamFateHistogram(
67 SpdyPushedStreamFate::kRangeMismatch);
68 return false;
69 }
70 }
71
72 HttpRequestInfo pushed_request_info;
73 ConvertHeaderBlockToHttpRequestHeaders(pushed_request_headers,
74 &pushed_request_info.extra_headers);
75 HttpVaryData vary_data;
76 if (!vary_data.Init(pushed_request_info,
77 *pushed_response_info.headers.get())) {
78 // Pushed response did not contain non-empty Vary header.
79 SpdySession::RecordSpdyPushedStreamFateHistogram(
80 SpdyPushedStreamFate::kAcceptedNoVary);
81 return true;
82 }
83
84 if (vary_data.MatchesRequest(request_info,
85 *pushed_response_info.headers.get())) {
86 SpdySession::RecordSpdyPushedStreamFateHistogram(
87 SpdyPushedStreamFate::kAcceptedMatchingVary);
88 return true;
89 }
90
91 SpdySession::RecordSpdyPushedStreamFateHistogram(
92 SpdyPushedStreamFate::kVaryMismatch);
93 return false;
94 }
95
96 } // anonymous namespace
97
98 // Align our request body with |kMaxSpdyFrameChunkSize| to prevent unexpected
99 // buffer chunking. This is 16KB - frame header size.
100 const size_t SpdyHttpStream::kRequestBodyBufferSize = kMaxSpdyFrameChunkSize;
101
SpdyHttpStream(const base::WeakPtr<SpdySession> & spdy_session,spdy::SpdyStreamId pushed_stream_id,NetLogSource source_dependency,std::set<std::string> dns_aliases)102 SpdyHttpStream::SpdyHttpStream(const base::WeakPtr<SpdySession>& spdy_session,
103 spdy::SpdyStreamId pushed_stream_id,
104 NetLogSource source_dependency,
105 std::set<std::string> dns_aliases)
106 : MultiplexedHttpStream(
107 std::make_unique<MultiplexedSessionHandle>(spdy_session)),
108 spdy_session_(spdy_session),
109 pushed_stream_id_(pushed_stream_id),
110 is_reused_(spdy_session_->IsReused()),
111 source_dependency_(source_dependency),
112 dns_aliases_(std::move(dns_aliases)) {
113 DCHECK(spdy_session_.get());
114 }
115
~SpdyHttpStream()116 SpdyHttpStream::~SpdyHttpStream() {
117 if (stream_) {
118 stream_->DetachDelegate();
119 DCHECK(!stream_);
120 }
121 }
122
RegisterRequest(const HttpRequestInfo * request_info)123 void SpdyHttpStream::RegisterRequest(const HttpRequestInfo* request_info) {
124 DCHECK(request_info);
125 request_info_ = request_info;
126 }
127
InitializeStream(bool can_send_early,RequestPriority priority,const NetLogWithSource & stream_net_log,CompletionOnceCallback callback)128 int SpdyHttpStream::InitializeStream(bool can_send_early,
129 RequestPriority priority,
130 const NetLogWithSource& stream_net_log,
131 CompletionOnceCallback callback) {
132 DCHECK(!stream_);
133 DCHECK(request_info_);
134 if (!spdy_session_)
135 return ERR_CONNECTION_CLOSED;
136
137 if (pushed_stream_id_ != kNoPushedStreamFound) {
138 int error = spdy_session_->GetPushedStream(
139 request_info_->url, pushed_stream_id_, priority, &stream_);
140 if (error != OK)
141 return error;
142
143 // |stream_| may be NULL even if OK was returned.
144 if (stream_) {
145 DCHECK_EQ(stream_->type(), SPDY_PUSH_STREAM);
146 InitializeStreamHelper();
147 return OK;
148 }
149 }
150
151 int rv = stream_request_.StartRequest(
152 SPDY_REQUEST_RESPONSE_STREAM, spdy_session_, request_info_->url,
153 can_send_early, priority, request_info_->socket_tag, stream_net_log,
154 base::BindOnce(&SpdyHttpStream::OnStreamCreated,
155 weak_factory_.GetWeakPtr(), std::move(callback)),
156 NetworkTrafficAnnotationTag{request_info_->traffic_annotation});
157
158 if (rv == OK) {
159 stream_ = stream_request_.ReleaseStream().get();
160 InitializeStreamHelper();
161 }
162
163 return rv;
164 }
165
ReadResponseHeaders(CompletionOnceCallback callback)166 int SpdyHttpStream::ReadResponseHeaders(CompletionOnceCallback callback) {
167 CHECK(!callback.is_null());
168 if (stream_closed_)
169 return closed_stream_status_;
170
171 CHECK(stream_);
172
173 // Check if we already have the response headers. If so, return synchronously.
174 if (response_headers_complete_) {
175 CHECK(!stream_->IsIdle());
176 return OK;
177 }
178
179 // Still waiting for the response, return IO_PENDING.
180 CHECK(response_callback_.is_null());
181 response_callback_ = std::move(callback);
182 return ERR_IO_PENDING;
183 }
184
ReadResponseBody(IOBuffer * buf,int buf_len,CompletionOnceCallback callback)185 int SpdyHttpStream::ReadResponseBody(IOBuffer* buf,
186 int buf_len,
187 CompletionOnceCallback callback) {
188 if (stream_)
189 CHECK(!stream_->IsIdle());
190
191 CHECK(buf);
192 CHECK(buf_len);
193 CHECK(!callback.is_null());
194
195 // If we have data buffered, complete the IO immediately.
196 if (!response_body_queue_.IsEmpty()) {
197 return response_body_queue_.Dequeue(buf->data(), buf_len);
198 } else if (stream_closed_) {
199 return closed_stream_status_;
200 }
201
202 CHECK(response_callback_.is_null());
203 CHECK(!user_buffer_.get());
204 CHECK_EQ(0, user_buffer_len_);
205
206 response_callback_ = std::move(callback);
207 user_buffer_ = buf;
208 user_buffer_len_ = buf_len;
209 return ERR_IO_PENDING;
210 }
211
Close(bool not_reusable)212 void SpdyHttpStream::Close(bool not_reusable) {
213 // Note: the not_reusable flag has no meaning for SPDY streams.
214
215 Cancel();
216 DCHECK(!stream_);
217 }
218
IsResponseBodyComplete() const219 bool SpdyHttpStream::IsResponseBodyComplete() const {
220 return stream_closed_;
221 }
222
IsConnectionReused() const223 bool SpdyHttpStream::IsConnectionReused() const {
224 return is_reused_;
225 }
226
GetTotalReceivedBytes() const227 int64_t SpdyHttpStream::GetTotalReceivedBytes() const {
228 if (stream_closed_)
229 return closed_stream_received_bytes_;
230
231 if (!stream_)
232 return 0;
233
234 return stream_->raw_received_bytes();
235 }
236
GetTotalSentBytes() const237 int64_t SpdyHttpStream::GetTotalSentBytes() const {
238 if (stream_closed_)
239 return closed_stream_sent_bytes_;
240
241 if (!stream_)
242 return 0;
243
244 return stream_->raw_sent_bytes();
245 }
246
GetAlternativeService(AlternativeService * alternative_service) const247 bool SpdyHttpStream::GetAlternativeService(
248 AlternativeService* alternative_service) const {
249 return false;
250 }
251
GetLoadTimingInfo(LoadTimingInfo * load_timing_info) const252 bool SpdyHttpStream::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const {
253 if (stream_closed_) {
254 if (!closed_stream_has_load_timing_info_)
255 return false;
256 *load_timing_info = closed_stream_load_timing_info_;
257 } else {
258 // If |stream_| has yet to be created, or does not yet have an ID, fail.
259 // The reused flag can only be correctly set once a stream has an ID.
260 // Streams get their IDs once the request has been successfully sent, so
261 // this does not behave that differently from other stream types.
262 if (!stream_ || stream_->stream_id() == 0)
263 return false;
264
265 if (!stream_->GetLoadTimingInfo(load_timing_info))
266 return false;
267 }
268
269 // If the request waited for handshake confirmation, shift |ssl_end| to
270 // include that time.
271 if (!load_timing_info->connect_timing.ssl_end.is_null() &&
272 !stream_request_.confirm_handshake_end().is_null()) {
273 load_timing_info->connect_timing.ssl_end =
274 stream_request_.confirm_handshake_end();
275 load_timing_info->connect_timing.connect_end =
276 stream_request_.confirm_handshake_end();
277 }
278
279 return true;
280 }
281
SendRequest(const HttpRequestHeaders & request_headers,HttpResponseInfo * response,CompletionOnceCallback callback)282 int SpdyHttpStream::SendRequest(const HttpRequestHeaders& request_headers,
283 HttpResponseInfo* response,
284 CompletionOnceCallback callback) {
285 if (stream_closed_) {
286 return closed_stream_status_;
287 }
288
289 base::Time request_time = base::Time::Now();
290 CHECK(stream_);
291
292 stream_->SetRequestTime(request_time);
293 // This should only get called in the case of a request occurring
294 // during server push that has already begun but hasn't finished,
295 // so we set the response's request time to be the actual one
296 if (response_info_)
297 response_info_->request_time = request_time;
298
299 CHECK(!request_body_buf_.get());
300 if (HasUploadData()) {
301 request_body_buf_ =
302 base::MakeRefCounted<IOBufferWithSize>(kRequestBodyBufferSize);
303 // The request body buffer is empty at first.
304 request_body_buf_size_ = 0;
305 }
306
307 CHECK(!callback.is_null());
308 CHECK(response);
309
310 // SendRequest can be called in two cases.
311 //
312 // a) A client initiated request. In this case, |response_info_| should be
313 // NULL to start with.
314 // b) A client request which matches a response that the server has already
315 // pushed.
316 if (push_response_info_.get()) {
317 *response = *(push_response_info_.get());
318 push_response_info_.reset();
319 } else {
320 DCHECK_EQ(static_cast<HttpResponseInfo*>(nullptr), response_info_);
321 }
322
323 response_info_ = response;
324
325 // Put the peer's IP address and port into the response.
326 IPEndPoint address;
327 int result = stream_->GetPeerAddress(&address);
328 if (result != OK)
329 return result;
330 response_info_->remote_endpoint = address;
331
332 if (stream_->type() == SPDY_PUSH_STREAM) {
333 // Pushed streams do not send any data, and should always be
334 // idle. However, we still want to return ERR_IO_PENDING to mimic
335 // non-push behavior. The callback will be called when the
336 // response is received.
337 CHECK(response_callback_.is_null());
338 response_callback_ = std::move(callback);
339 return ERR_IO_PENDING;
340 }
341
342 spdy::Http2HeaderBlock headers;
343 CreateSpdyHeadersFromHttpRequest(*request_info_, request_headers, &headers);
344 stream_->net_log().AddEvent(
345 NetLogEventType::HTTP_TRANSACTION_HTTP2_SEND_REQUEST_HEADERS,
346 [&](NetLogCaptureMode capture_mode) {
347 return Http2HeaderBlockNetLogParams(&headers, capture_mode);
348 });
349 DispatchRequestHeadersCallback(headers);
350
351 bool will_send_data =
352 HasUploadData() || spdy_session_->EndStreamWithDataFrame();
353 result = stream_->SendRequestHeaders(
354 std::move(headers),
355 will_send_data ? MORE_DATA_TO_SEND : NO_MORE_DATA_TO_SEND);
356
357 if (result == ERR_IO_PENDING) {
358 CHECK(request_callback_.is_null());
359 request_callback_ = std::move(callback);
360 }
361 return result;
362 }
363
Cancel()364 void SpdyHttpStream::Cancel() {
365 request_callback_.Reset();
366 response_callback_.Reset();
367 if (stream_) {
368 stream_->Cancel(ERR_ABORTED);
369 DCHECK(!stream_);
370 }
371 }
372
OnHeadersSent()373 void SpdyHttpStream::OnHeadersSent() {
374 if (HasUploadData()) {
375 ReadAndSendRequestBodyData();
376 } else if (spdy_session_->EndStreamWithDataFrame()) {
377 SendEmptyBody();
378 } else {
379 MaybePostRequestCallback(OK);
380 }
381 }
382
OnEarlyHintsReceived(const spdy::Http2HeaderBlock & headers)383 void SpdyHttpStream::OnEarlyHintsReceived(
384 const spdy::Http2HeaderBlock& headers) {
385 DCHECK(!response_headers_complete_);
386 DCHECK(response_info_);
387 DCHECK_EQ(stream_->type(), SPDY_REQUEST_RESPONSE_STREAM);
388
389 const int rv = SpdyHeadersToHttpResponse(headers, response_info_);
390 CHECK_NE(rv, ERR_INCOMPLETE_HTTP2_HEADERS);
391
392 if (!response_callback_.is_null()) {
393 DoResponseCallback(OK);
394 }
395 }
396
OnHeadersReceived(const spdy::Http2HeaderBlock & response_headers,const spdy::Http2HeaderBlock * pushed_request_headers)397 void SpdyHttpStream::OnHeadersReceived(
398 const spdy::Http2HeaderBlock& response_headers,
399 const spdy::Http2HeaderBlock* pushed_request_headers) {
400 DCHECK(!response_headers_complete_);
401 response_headers_complete_ = true;
402
403 if (!response_info_) {
404 DCHECK_EQ(stream_->type(), SPDY_PUSH_STREAM);
405 push_response_info_ = std::make_unique<HttpResponseInfo>();
406 response_info_ = push_response_info_.get();
407 }
408
409 const int rv = SpdyHeadersToHttpResponse(response_headers, response_info_);
410 DCHECK_NE(rv, ERR_INCOMPLETE_HTTP2_HEADERS);
411
412 if (rv == ERR_RESPONSE_HEADERS_MULTIPLE_LOCATION) {
413 // Cancel will call OnClose, which might call callbacks and might destroy
414 // `this`.
415 stream_->Cancel(rv);
416 return;
417 }
418
419 if (pushed_request_headers &&
420 !ValidatePushedHeaders(*request_info_, *pushed_request_headers,
421 response_headers, *response_info_)) {
422 // Cancel will call OnClose, which might call callbacks and might destroy
423 // `this`.
424 stream_->Cancel(ERR_HTTP2_PUSHED_RESPONSE_DOES_NOT_MATCH);
425
426 return;
427 }
428
429 response_info_->response_time = stream_->response_time();
430 // Don't store the SSLInfo in the response here, HttpNetworkTransaction
431 // will take care of that part.
432 response_info_->was_alpn_negotiated = was_alpn_negotiated_;
433 response_info_->request_time = stream_->GetRequestTime();
434 response_info_->connection_info = HttpResponseInfo::CONNECTION_INFO_HTTP2;
435 response_info_->alpn_negotiated_protocol =
436 HttpResponseInfo::ConnectionInfoToString(response_info_->connection_info);
437
438 // Invalidate HttpRequestInfo pointer. This is to allow |this| to be
439 // shared across multiple consumers at the cache layer which might require
440 // this stream to outlive the request_info_'s owner.
441 if (!upload_stream_in_progress_)
442 request_info_ = nullptr;
443
444 if (!response_callback_.is_null()) {
445 DoResponseCallback(OK);
446 }
447 }
448
OnDataReceived(std::unique_ptr<SpdyBuffer> buffer)449 void SpdyHttpStream::OnDataReceived(std::unique_ptr<SpdyBuffer> buffer) {
450 DCHECK(response_headers_complete_);
451
452 // Note that data may be received for a SpdyStream prior to the user calling
453 // ReadResponseBody(), therefore user_buffer_ may be NULL. This may often
454 // happen for server initiated streams.
455 DCHECK(stream_);
456 DCHECK(!stream_->IsClosed() || stream_->type() == SPDY_PUSH_STREAM);
457 if (buffer) {
458 response_body_queue_.Enqueue(std::move(buffer));
459 MaybeScheduleBufferedReadCallback();
460 }
461 }
462
OnDataSent()463 void SpdyHttpStream::OnDataSent() {
464 if (request_info_ && HasUploadData()) {
465 request_body_buf_size_ = 0;
466 ReadAndSendRequestBodyData();
467 } else {
468 CHECK(spdy_session_->EndStreamWithDataFrame());
469 MaybePostRequestCallback(OK);
470 }
471 }
472
473 // TODO(xunjieli): Maybe do something with the trailers. crbug.com/422958.
OnTrailers(const spdy::Http2HeaderBlock & trailers)474 void SpdyHttpStream::OnTrailers(const spdy::Http2HeaderBlock& trailers) {}
475
OnClose(int status)476 void SpdyHttpStream::OnClose(int status) {
477 DCHECK(stream_);
478
479 // Cancel any pending reads from the upload data stream.
480 if (request_info_ && request_info_->upload_data_stream)
481 request_info_->upload_data_stream->Reset();
482
483 stream_closed_ = true;
484 closed_stream_status_ = status;
485 closed_stream_id_ = stream_->stream_id();
486 closed_stream_has_load_timing_info_ =
487 stream_->GetLoadTimingInfo(&closed_stream_load_timing_info_);
488 closed_stream_received_bytes_ = stream_->raw_received_bytes();
489 closed_stream_sent_bytes_ = stream_->raw_sent_bytes();
490 stream_ = nullptr;
491
492 // Callbacks might destroy |this|.
493 base::WeakPtr<SpdyHttpStream> self = weak_factory_.GetWeakPtr();
494
495 if (!request_callback_.is_null()) {
496 DoRequestCallback(status);
497 if (!self)
498 return;
499 }
500
501 if (status == OK) {
502 // We need to complete any pending buffered read now.
503 DoBufferedReadCallback();
504 if (!self)
505 return;
506 }
507
508 if (!response_callback_.is_null()) {
509 DoResponseCallback(status);
510 }
511 }
512
CanGreaseFrameType() const513 bool SpdyHttpStream::CanGreaseFrameType() const {
514 return true;
515 }
516
source_dependency() const517 NetLogSource SpdyHttpStream::source_dependency() const {
518 return source_dependency_;
519 }
520
HasUploadData() const521 bool SpdyHttpStream::HasUploadData() const {
522 CHECK(request_info_);
523 return
524 request_info_->upload_data_stream &&
525 ((request_info_->upload_data_stream->size() > 0) ||
526 request_info_->upload_data_stream->is_chunked());
527 }
528
OnStreamCreated(CompletionOnceCallback callback,int rv)529 void SpdyHttpStream::OnStreamCreated(CompletionOnceCallback callback, int rv) {
530 if (rv == OK) {
531 stream_ = stream_request_.ReleaseStream().get();
532 InitializeStreamHelper();
533 }
534 std::move(callback).Run(rv);
535 }
536
ReadAndSendRequestBodyData()537 void SpdyHttpStream::ReadAndSendRequestBodyData() {
538 CHECK(HasUploadData());
539 upload_stream_in_progress_ = true;
540
541 CHECK_EQ(request_body_buf_size_, 0);
542 if (request_info_->upload_data_stream->IsEOF()) {
543 MaybePostRequestCallback(OK);
544
545 // Invalidate HttpRequestInfo pointer. This is to allow |this| to be
546 // shared across multiple consumers at the cache layer which might require
547 // this stream to outlive the request_info_'s owner.
548 upload_stream_in_progress_ = false;
549 if (response_headers_complete_)
550 request_info_ = nullptr;
551 return;
552 }
553
554 // Read the data from the request body stream.
555 const int rv = request_info_->upload_data_stream->Read(
556 request_body_buf_.get(), request_body_buf_->size(),
557 base::BindOnce(&SpdyHttpStream::OnRequestBodyReadCompleted,
558 weak_factory_.GetWeakPtr()));
559
560 if (rv != ERR_IO_PENDING)
561 OnRequestBodyReadCompleted(rv);
562 }
563
SendEmptyBody()564 void SpdyHttpStream::SendEmptyBody() {
565 CHECK(!HasUploadData());
566 CHECK(spdy_session_->EndStreamWithDataFrame());
567
568 auto buffer = base::MakeRefCounted<IOBuffer>(/* buffer_size = */ 0);
569 stream_->SendData(buffer.get(), /* length = */ 0, NO_MORE_DATA_TO_SEND);
570 }
571
InitializeStreamHelper()572 void SpdyHttpStream::InitializeStreamHelper() {
573 stream_->SetDelegate(this);
574 was_alpn_negotiated_ = stream_->WasAlpnNegotiated();
575 }
576
ResetStream(int error)577 void SpdyHttpStream::ResetStream(int error) {
578 spdy_session_->ResetStream(stream()->stream_id(), error, std::string());
579 }
580
OnRequestBodyReadCompleted(int status)581 void SpdyHttpStream::OnRequestBodyReadCompleted(int status) {
582 if (status < 0) {
583 DCHECK_NE(ERR_IO_PENDING, status);
584 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
585 FROM_HERE, base::BindOnce(&SpdyHttpStream::ResetStream,
586 weak_factory_.GetWeakPtr(), status));
587
588 return;
589 }
590
591 CHECK_GE(status, 0);
592 request_body_buf_size_ = status;
593 const bool eof = request_info_->upload_data_stream->IsEOF();
594 // Only the final frame may have a length of 0.
595 if (eof) {
596 CHECK_GE(request_body_buf_size_, 0);
597 } else {
598 CHECK_GT(request_body_buf_size_, 0);
599 }
600 stream_->SendData(request_body_buf_.get(),
601 request_body_buf_size_,
602 eof ? NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND);
603 }
604
MaybeScheduleBufferedReadCallback()605 void SpdyHttpStream::MaybeScheduleBufferedReadCallback() {
606 DCHECK(!stream_closed_);
607
608 if (!user_buffer_.get())
609 return;
610
611 // If enough data was received to fill the user buffer, invoke
612 // DoBufferedReadCallback() with no delay.
613 //
614 // Note: DoBufferedReadCallback() is invoked asynchronously to preserve
615 // historical behavior. It would be interesting to evaluate whether it can be
616 // invoked synchronously to avoid the overhead of posting a task. A long time
617 // ago, the callback was invoked synchronously
618 // https://codereview.chromium.org/652209/diff/2018/net/spdy/spdy_stream.cc.
619 if (response_body_queue_.GetTotalSize() >=
620 static_cast<size_t>(user_buffer_len_)) {
621 buffered_read_timer_.Start(FROM_HERE, base::TimeDelta() /* no delay */,
622 this, &SpdyHttpStream::DoBufferedReadCallback);
623 return;
624 }
625
626 // Handing small chunks of data to the caller creates measurable overhead.
627 // Wait 1ms to allow handing off multiple chunks of data received within a
628 // short time span at once.
629 buffered_read_timer_.Start(FROM_HERE, base::Milliseconds(1), this,
630 &SpdyHttpStream::DoBufferedReadCallback);
631 }
632
DoBufferedReadCallback()633 void SpdyHttpStream::DoBufferedReadCallback() {
634 buffered_read_timer_.Stop();
635
636 // If the transaction is cancelled or errored out, we don't need to complete
637 // the read.
638 if (stream_closed_ && closed_stream_status_ != OK) {
639 if (response_callback_)
640 DoResponseCallback(closed_stream_status_);
641 return;
642 }
643
644 if (!user_buffer_.get())
645 return;
646
647 if (!response_body_queue_.IsEmpty()) {
648 int rv =
649 response_body_queue_.Dequeue(user_buffer_->data(), user_buffer_len_);
650 user_buffer_ = nullptr;
651 user_buffer_len_ = 0;
652 DoResponseCallback(rv);
653 return;
654 }
655
656 if (stream_closed_ && response_callback_)
657 DoResponseCallback(closed_stream_status_);
658 }
659
DoRequestCallback(int rv)660 void SpdyHttpStream::DoRequestCallback(int rv) {
661 CHECK_NE(rv, ERR_IO_PENDING);
662 CHECK(!request_callback_.is_null());
663 // Since Run may result in being called back, reset request_callback_ in
664 // advance.
665 std::move(request_callback_).Run(rv);
666 }
667
MaybeDoRequestCallback(int rv)668 void SpdyHttpStream::MaybeDoRequestCallback(int rv) {
669 CHECK_NE(ERR_IO_PENDING, rv);
670 if (request_callback_)
671 std::move(request_callback_).Run(rv);
672 }
673
MaybePostRequestCallback(int rv)674 void SpdyHttpStream::MaybePostRequestCallback(int rv) {
675 CHECK_NE(ERR_IO_PENDING, rv);
676 if (request_callback_)
677 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
678 FROM_HERE, base::BindOnce(&SpdyHttpStream::MaybeDoRequestCallback,
679 weak_factory_.GetWeakPtr(), rv));
680 }
681
DoResponseCallback(int rv)682 void SpdyHttpStream::DoResponseCallback(int rv) {
683 CHECK_NE(rv, ERR_IO_PENDING);
684 CHECK(!response_callback_.is_null());
685
686 // Since Run may result in being called back, reset response_callback_ in
687 // advance.
688 std::move(response_callback_).Run(rv);
689 }
690
GetRemoteEndpoint(IPEndPoint * endpoint)691 int SpdyHttpStream::GetRemoteEndpoint(IPEndPoint* endpoint) {
692 if (!spdy_session_)
693 return ERR_SOCKET_NOT_CONNECTED;
694
695 return spdy_session_->GetPeerAddress(endpoint);
696 }
697
PopulateNetErrorDetails(NetErrorDetails * details)698 void SpdyHttpStream::PopulateNetErrorDetails(NetErrorDetails* details) {
699 details->connection_info = HttpResponseInfo::CONNECTION_INFO_HTTP2;
700 return;
701 }
702
SetPriority(RequestPriority priority)703 void SpdyHttpStream::SetPriority(RequestPriority priority) {
704 if (stream_) {
705 stream_->SetPriority(priority);
706 }
707 }
708
GetDnsAliases() const709 const std::set<std::string>& SpdyHttpStream::GetDnsAliases() const {
710 return dns_aliases_;
711 }
712
GetAcceptChViaAlps() const713 base::StringPiece SpdyHttpStream::GetAcceptChViaAlps() const {
714 if (!request_info_) {
715 return {};
716 }
717
718 return session()->GetAcceptChViaAlps(url::SchemeHostPort(request_info_->url));
719 }
720
721 } // namespace net
722