• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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