xref: /aosp_15_r20/external/cronet/net/spdy/spdy_http_stream.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 <string_view>
12 #include <utility>
13 
14 #include "base/check_op.h"
15 #include "base/functional/bind.h"
16 #include "base/location.h"
17 #include "base/metrics/histogram_macros.h"
18 #include "base/task/single_thread_task_runner.h"
19 #include "base/values.h"
20 #include "net/base/ip_endpoint.h"
21 #include "net/base/upload_data_stream.h"
22 #include "net/http/http_request_headers.h"
23 #include "net/http/http_request_info.h"
24 #include "net/http/http_response_info.h"
25 #include "net/log/net_log_event_type.h"
26 #include "net/log/net_log_with_source.h"
27 #include "net/socket/next_proto.h"
28 #include "net/spdy/spdy_http_utils.h"
29 #include "net/spdy/spdy_session.h"
30 #include "net/third_party/quiche/src/quiche/spdy/core/http2_header_block.h"
31 #include "net/third_party/quiche/src/quiche/spdy/core/spdy_protocol.h"
32 #include "url/scheme_host_port.h"
33 
34 namespace net {
35 
36 // Align our request body with |kMaxSpdyFrameChunkSize| to prevent unexpected
37 // buffer chunking. This is 16KB - frame header size.
38 const size_t SpdyHttpStream::kRequestBodyBufferSize = kMaxSpdyFrameChunkSize;
39 
SpdyHttpStream(const base::WeakPtr<SpdySession> & spdy_session,NetLogSource source_dependency,std::set<std::string> dns_aliases)40 SpdyHttpStream::SpdyHttpStream(const base::WeakPtr<SpdySession>& spdy_session,
41                                NetLogSource source_dependency,
42                                std::set<std::string> dns_aliases)
43     : MultiplexedHttpStream(
44           std::make_unique<MultiplexedSessionHandle>(spdy_session)),
45       spdy_session_(spdy_session),
46       is_reused_(spdy_session_->IsReused()),
47       source_dependency_(source_dependency),
48       dns_aliases_(std::move(dns_aliases)) {
49   DCHECK(spdy_session_.get());
50 }
51 
~SpdyHttpStream()52 SpdyHttpStream::~SpdyHttpStream() {
53   if (stream_) {
54     stream_->DetachDelegate();
55     DCHECK(!stream_);
56   }
57 }
58 
RegisterRequest(const HttpRequestInfo * request_info)59 void SpdyHttpStream::RegisterRequest(const HttpRequestInfo* request_info) {
60   DCHECK(request_info);
61   request_info_ = request_info;
62 }
63 
InitializeStream(bool can_send_early,RequestPriority priority,const NetLogWithSource & stream_net_log,CompletionOnceCallback callback)64 int SpdyHttpStream::InitializeStream(bool can_send_early,
65                                      RequestPriority priority,
66                                      const NetLogWithSource& stream_net_log,
67                                      CompletionOnceCallback callback) {
68   DCHECK(!stream_);
69   DCHECK(request_info_);
70   if (!spdy_session_)
71     return ERR_CONNECTION_CLOSED;
72 
73   priority_ = priority;
74   int rv = stream_request_.StartRequest(
75       SPDY_REQUEST_RESPONSE_STREAM, spdy_session_, request_info_->url,
76       can_send_early, priority, request_info_->socket_tag, stream_net_log,
77       base::BindOnce(&SpdyHttpStream::OnStreamCreated,
78                      weak_factory_.GetWeakPtr(), std::move(callback)),
79       NetworkTrafficAnnotationTag{request_info_->traffic_annotation});
80 
81   if (rv == OK) {
82     stream_ = stream_request_.ReleaseStream().get();
83     InitializeStreamHelper();
84   }
85 
86   return rv;
87 }
88 
ReadResponseHeaders(CompletionOnceCallback callback)89 int SpdyHttpStream::ReadResponseHeaders(CompletionOnceCallback callback) {
90   CHECK(!callback.is_null());
91   if (stream_closed_)
92     return closed_stream_status_;
93 
94   CHECK(stream_);
95 
96   // Check if we already have the response headers. If so, return synchronously.
97   if (response_headers_complete_) {
98     CHECK(!stream_->IsIdle());
99     return OK;
100   }
101 
102   // Still waiting for the response, return IO_PENDING.
103   CHECK(response_callback_.is_null());
104   response_callback_ = std::move(callback);
105   return ERR_IO_PENDING;
106 }
107 
ReadResponseBody(IOBuffer * buf,int buf_len,CompletionOnceCallback callback)108 int SpdyHttpStream::ReadResponseBody(IOBuffer* buf,
109                                      int buf_len,
110                                      CompletionOnceCallback callback) {
111   if (stream_)
112     CHECK(!stream_->IsIdle());
113 
114   CHECK(buf);
115   CHECK(buf_len);
116   CHECK(!callback.is_null());
117 
118   // If we have data buffered, complete the IO immediately.
119   if (!response_body_queue_.IsEmpty()) {
120     return response_body_queue_.Dequeue(buf->data(), buf_len);
121   } else if (stream_closed_) {
122     return closed_stream_status_;
123   }
124 
125   CHECK(response_callback_.is_null());
126   CHECK(!user_buffer_.get());
127   CHECK_EQ(0, user_buffer_len_);
128 
129   response_callback_ = std::move(callback);
130   user_buffer_ = buf;
131   user_buffer_len_ = buf_len;
132   return ERR_IO_PENDING;
133 }
134 
Close(bool not_reusable)135 void SpdyHttpStream::Close(bool not_reusable) {
136   // Note: the not_reusable flag has no meaning for SPDY streams.
137 
138   Cancel();
139   DCHECK(!stream_);
140 }
141 
IsResponseBodyComplete() const142 bool SpdyHttpStream::IsResponseBodyComplete() const {
143   return stream_closed_;
144 }
145 
IsConnectionReused() const146 bool SpdyHttpStream::IsConnectionReused() const {
147   return is_reused_;
148 }
149 
GetTotalReceivedBytes() const150 int64_t SpdyHttpStream::GetTotalReceivedBytes() const {
151   if (stream_closed_)
152     return closed_stream_received_bytes_;
153 
154   if (!stream_)
155     return 0;
156 
157   return stream_->raw_received_bytes();
158 }
159 
GetTotalSentBytes() const160 int64_t SpdyHttpStream::GetTotalSentBytes() const {
161   if (stream_closed_)
162     return closed_stream_sent_bytes_;
163 
164   if (!stream_)
165     return 0;
166 
167   return stream_->raw_sent_bytes();
168 }
169 
GetAlternativeService(AlternativeService * alternative_service) const170 bool SpdyHttpStream::GetAlternativeService(
171     AlternativeService* alternative_service) const {
172   return false;
173 }
174 
GetLoadTimingInfo(LoadTimingInfo * load_timing_info) const175 bool SpdyHttpStream::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const {
176   if (stream_closed_) {
177     if (!closed_stream_has_load_timing_info_)
178       return false;
179     *load_timing_info = closed_stream_load_timing_info_;
180   } else {
181     // If |stream_| has yet to be created, or does not yet have an ID, fail.
182     // The reused flag can only be correctly set once a stream has an ID.
183     // Streams get their IDs once the request has been successfully sent, so
184     // this does not behave that differently from other stream types.
185     if (!stream_ || stream_->stream_id() == 0)
186       return false;
187 
188     if (!stream_->GetLoadTimingInfo(load_timing_info))
189       return false;
190   }
191 
192   // If the request waited for handshake confirmation, shift |ssl_end| to
193   // include that time.
194   if (!load_timing_info->connect_timing.ssl_end.is_null() &&
195       !stream_request_.confirm_handshake_end().is_null()) {
196     load_timing_info->connect_timing.ssl_end =
197         stream_request_.confirm_handshake_end();
198     load_timing_info->connect_timing.connect_end =
199         stream_request_.confirm_handshake_end();
200   }
201 
202   return true;
203 }
204 
SendRequest(const HttpRequestHeaders & request_headers,HttpResponseInfo * response,CompletionOnceCallback callback)205 int SpdyHttpStream::SendRequest(const HttpRequestHeaders& request_headers,
206                                 HttpResponseInfo* response,
207                                 CompletionOnceCallback callback) {
208   if (stream_closed_) {
209     return closed_stream_status_;
210   }
211 
212   base::Time request_time = base::Time::Now();
213   CHECK(stream_);
214 
215   stream_->SetRequestTime(request_time);
216   // This should only get called in the case of a request occurring
217   // during server push that has already begun but hasn't finished,
218   // so we set the response's request time to be the actual one
219   if (response_info_)
220     response_info_->request_time = request_time;
221 
222   CHECK(!request_body_buf_.get());
223   if (HasUploadData()) {
224     request_body_buf_ =
225         base::MakeRefCounted<IOBufferWithSize>(kRequestBodyBufferSize);
226     // The request body buffer is empty at first.
227     request_body_buf_size_ = 0;
228   }
229 
230   CHECK(!callback.is_null());
231   CHECK(response);
232   DCHECK(!response_info_);
233 
234   response_info_ = response;
235 
236   // Put the peer's IP address and port into the response.
237   IPEndPoint address;
238   int result = stream_->GetPeerAddress(&address);
239   if (result != OK)
240     return result;
241   response_info_->remote_endpoint = address;
242 
243   spdy::Http2HeaderBlock headers;
244   CreateSpdyHeadersFromHttpRequest(*request_info_, priority_, request_headers,
245                                    &headers);
246   DispatchRequestHeadersCallback(headers);
247 
248   bool will_send_data =
249       HasUploadData() || spdy_session_->EndStreamWithDataFrame();
250   result = stream_->SendRequestHeaders(
251       std::move(headers),
252       will_send_data ? MORE_DATA_TO_SEND : NO_MORE_DATA_TO_SEND);
253 
254   if (result == ERR_IO_PENDING) {
255     CHECK(request_callback_.is_null());
256     request_callback_ = std::move(callback);
257   }
258   return result;
259 }
260 
Cancel()261 void SpdyHttpStream::Cancel() {
262   request_callback_.Reset();
263   response_callback_.Reset();
264   if (stream_) {
265     stream_->Cancel(ERR_ABORTED);
266     DCHECK(!stream_);
267   }
268 }
269 
OnHeadersSent()270 void SpdyHttpStream::OnHeadersSent() {
271   if (HasUploadData()) {
272     ReadAndSendRequestBodyData();
273   } else if (spdy_session_->EndStreamWithDataFrame()) {
274     SendEmptyBody();
275   } else {
276     MaybePostRequestCallback(OK);
277   }
278 }
279 
OnEarlyHintsReceived(const spdy::Http2HeaderBlock & headers)280 void SpdyHttpStream::OnEarlyHintsReceived(
281     const spdy::Http2HeaderBlock& headers) {
282   DCHECK(!response_headers_complete_);
283   DCHECK(response_info_);
284   DCHECK_EQ(stream_->type(), SPDY_REQUEST_RESPONSE_STREAM);
285 
286   const int rv = SpdyHeadersToHttpResponse(headers, response_info_);
287   CHECK_NE(rv, ERR_INCOMPLETE_HTTP2_HEADERS);
288 
289   if (!response_callback_.is_null()) {
290     DoResponseCallback(OK);
291   }
292 }
293 
OnHeadersReceived(const spdy::Http2HeaderBlock & response_headers)294 void SpdyHttpStream::OnHeadersReceived(
295     const spdy::Http2HeaderBlock& response_headers) {
296   DCHECK(!response_headers_complete_);
297   DCHECK(response_info_);
298   response_headers_complete_ = true;
299 
300   const int rv = SpdyHeadersToHttpResponse(response_headers, response_info_);
301   DCHECK_NE(rv, ERR_INCOMPLETE_HTTP2_HEADERS);
302 
303   if (rv == ERR_RESPONSE_HEADERS_MULTIPLE_LOCATION) {
304     // Cancel will call OnClose, which might call callbacks and might destroy
305     // `this`.
306     stream_->Cancel(rv);
307     return;
308   }
309 
310   response_info_->response_time = stream_->response_time();
311   // Don't store the SSLInfo in the response here, HttpNetworkTransaction
312   // will take care of that part.
313   CHECK_EQ(stream_->GetNegotiatedProtocol(), kProtoHTTP2);
314   response_info_->was_alpn_negotiated = true;
315   response_info_->request_time = stream_->GetRequestTime();
316   response_info_->connection_info = HttpConnectionInfo::kHTTP2;
317   response_info_->alpn_negotiated_protocol =
318       HttpConnectionInfoToString(response_info_->connection_info);
319 
320   // Invalidate HttpRequestInfo pointer. This is to allow |this| to be
321   // shared across multiple consumers at the cache layer which might require
322   // this stream to outlive the request_info_'s owner.
323   if (!upload_stream_in_progress_)
324     request_info_ = nullptr;
325 
326   if (!response_callback_.is_null()) {
327     DoResponseCallback(OK);
328   }
329 }
330 
OnDataReceived(std::unique_ptr<SpdyBuffer> buffer)331 void SpdyHttpStream::OnDataReceived(std::unique_ptr<SpdyBuffer> buffer) {
332   DCHECK(response_headers_complete_);
333 
334   // Note that data may be received for a SpdyStream prior to the user calling
335   // ReadResponseBody(), therefore user_buffer_ may be NULL.  This may often
336   // happen for server initiated streams.
337   DCHECK(stream_);
338   DCHECK(!stream_->IsClosed());
339   if (buffer) {
340     response_body_queue_.Enqueue(std::move(buffer));
341     MaybeScheduleBufferedReadCallback();
342   }
343 }
344 
OnDataSent()345 void SpdyHttpStream::OnDataSent() {
346   if (request_info_ && HasUploadData()) {
347     request_body_buf_size_ = 0;
348     ReadAndSendRequestBodyData();
349   } else {
350     CHECK(spdy_session_->EndStreamWithDataFrame());
351     MaybePostRequestCallback(OK);
352   }
353 }
354 
355 // TODO(xunjieli): Maybe do something with the trailers. crbug.com/422958.
OnTrailers(const spdy::Http2HeaderBlock & trailers)356 void SpdyHttpStream::OnTrailers(const spdy::Http2HeaderBlock& trailers) {}
357 
OnClose(int status)358 void SpdyHttpStream::OnClose(int status) {
359   DCHECK(stream_);
360 
361   // Cancel any pending reads from the upload data stream.
362   if (request_info_ && request_info_->upload_data_stream)
363     request_info_->upload_data_stream->Reset();
364 
365   stream_closed_ = true;
366   closed_stream_status_ = status;
367   closed_stream_id_ = stream_->stream_id();
368   closed_stream_has_load_timing_info_ =
369       stream_->GetLoadTimingInfo(&closed_stream_load_timing_info_);
370   closed_stream_received_bytes_ = stream_->raw_received_bytes();
371   closed_stream_sent_bytes_ = stream_->raw_sent_bytes();
372   stream_ = nullptr;
373 
374   // Callbacks might destroy |this|.
375   base::WeakPtr<SpdyHttpStream> self = weak_factory_.GetWeakPtr();
376 
377   if (!request_callback_.is_null()) {
378     DoRequestCallback(status);
379     if (!self)
380       return;
381   }
382 
383   if (status == OK) {
384     // We need to complete any pending buffered read now.
385     DoBufferedReadCallback();
386     if (!self)
387       return;
388   }
389 
390   if (!response_callback_.is_null()) {
391     DoResponseCallback(status);
392   }
393 }
394 
CanGreaseFrameType() const395 bool SpdyHttpStream::CanGreaseFrameType() const {
396   return true;
397 }
398 
source_dependency() const399 NetLogSource SpdyHttpStream::source_dependency() const {
400   return source_dependency_;
401 }
402 
HasUploadData() const403 bool SpdyHttpStream::HasUploadData() const {
404   CHECK(request_info_);
405   return
406       request_info_->upload_data_stream &&
407       ((request_info_->upload_data_stream->size() > 0) ||
408        request_info_->upload_data_stream->is_chunked());
409 }
410 
OnStreamCreated(CompletionOnceCallback callback,int rv)411 void SpdyHttpStream::OnStreamCreated(CompletionOnceCallback callback, int rv) {
412   if (rv == OK) {
413     stream_ = stream_request_.ReleaseStream().get();
414     InitializeStreamHelper();
415   }
416   std::move(callback).Run(rv);
417 }
418 
ReadAndSendRequestBodyData()419 void SpdyHttpStream::ReadAndSendRequestBodyData() {
420   CHECK(HasUploadData());
421   upload_stream_in_progress_ = true;
422 
423   CHECK_EQ(request_body_buf_size_, 0);
424   if (request_info_->upload_data_stream->IsEOF()) {
425     MaybePostRequestCallback(OK);
426 
427     // Invalidate HttpRequestInfo pointer. This is to allow |this| to be
428     // shared across multiple consumers at the cache layer which might require
429     // this stream to outlive the request_info_'s owner.
430     upload_stream_in_progress_ = false;
431     if (response_headers_complete_)
432       request_info_ = nullptr;
433     return;
434   }
435 
436   // Read the data from the request body stream.
437   const int rv = request_info_->upload_data_stream->Read(
438       request_body_buf_.get(), request_body_buf_->size(),
439       base::BindOnce(&SpdyHttpStream::OnRequestBodyReadCompleted,
440                      weak_factory_.GetWeakPtr()));
441 
442   if (rv != ERR_IO_PENDING)
443     OnRequestBodyReadCompleted(rv);
444 }
445 
SendEmptyBody()446 void SpdyHttpStream::SendEmptyBody() {
447   CHECK(!HasUploadData());
448   CHECK(spdy_session_->EndStreamWithDataFrame());
449 
450   auto buffer = base::MakeRefCounted<IOBufferWithSize>(/* buffer_size = */ 0);
451   stream_->SendData(buffer.get(), /* length = */ 0, NO_MORE_DATA_TO_SEND);
452 }
453 
InitializeStreamHelper()454 void SpdyHttpStream::InitializeStreamHelper() {
455   stream_->SetDelegate(this);
456 }
457 
ResetStream(int error)458 void SpdyHttpStream::ResetStream(int error) {
459   spdy_session_->ResetStream(stream()->stream_id(), error, std::string());
460 }
461 
OnRequestBodyReadCompleted(int status)462 void SpdyHttpStream::OnRequestBodyReadCompleted(int status) {
463   if (status < 0) {
464     DCHECK_NE(ERR_IO_PENDING, status);
465     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
466         FROM_HERE, base::BindOnce(&SpdyHttpStream::ResetStream,
467                                   weak_factory_.GetWeakPtr(), status));
468 
469     return;
470   }
471 
472   CHECK_GE(status, 0);
473   request_body_buf_size_ = status;
474   const bool eof = request_info_->upload_data_stream->IsEOF();
475   // Only the final frame may have a length of 0.
476   if (eof) {
477     CHECK_GE(request_body_buf_size_, 0);
478   } else {
479     CHECK_GT(request_body_buf_size_, 0);
480   }
481   stream_->SendData(request_body_buf_.get(),
482                     request_body_buf_size_,
483                     eof ? NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND);
484 }
485 
MaybeScheduleBufferedReadCallback()486 void SpdyHttpStream::MaybeScheduleBufferedReadCallback() {
487   DCHECK(!stream_closed_);
488 
489   if (!user_buffer_.get())
490     return;
491 
492   // If enough data was received to fill the user buffer, invoke
493   // DoBufferedReadCallback() with no delay.
494   //
495   // Note: DoBufferedReadCallback() is invoked asynchronously to preserve
496   // historical behavior. It would be interesting to evaluate whether it can be
497   // invoked synchronously to avoid the overhead of posting a task. A long time
498   // ago, the callback was invoked synchronously
499   // https://codereview.chromium.org/652209/diff/2018/net/spdy/spdy_stream.cc.
500   if (response_body_queue_.GetTotalSize() >=
501       static_cast<size_t>(user_buffer_len_)) {
502     buffered_read_timer_.Start(FROM_HERE, base::TimeDelta() /* no delay */,
503                                this, &SpdyHttpStream::DoBufferedReadCallback);
504     return;
505   }
506 
507   // Handing small chunks of data to the caller creates measurable overhead.
508   // Wait 1ms to allow handing off multiple chunks of data received within a
509   // short time span at once.
510   buffered_read_timer_.Start(FROM_HERE, base::Milliseconds(1), this,
511                              &SpdyHttpStream::DoBufferedReadCallback);
512 }
513 
DoBufferedReadCallback()514 void SpdyHttpStream::DoBufferedReadCallback() {
515   buffered_read_timer_.Stop();
516 
517   // If the transaction is cancelled or errored out, we don't need to complete
518   // the read.
519   if (stream_closed_ && closed_stream_status_ != OK) {
520     if (response_callback_)
521       DoResponseCallback(closed_stream_status_);
522     return;
523   }
524 
525   if (!user_buffer_.get())
526     return;
527 
528   if (!response_body_queue_.IsEmpty()) {
529     int rv =
530         response_body_queue_.Dequeue(user_buffer_->data(), user_buffer_len_);
531     user_buffer_ = nullptr;
532     user_buffer_len_ = 0;
533     DoResponseCallback(rv);
534     return;
535   }
536 
537   if (stream_closed_ && response_callback_)
538     DoResponseCallback(closed_stream_status_);
539 }
540 
DoRequestCallback(int rv)541 void SpdyHttpStream::DoRequestCallback(int rv) {
542   CHECK_NE(rv, ERR_IO_PENDING);
543   CHECK(!request_callback_.is_null());
544   // Since Run may result in being called back, reset request_callback_ in
545   // advance.
546   std::move(request_callback_).Run(rv);
547 }
548 
MaybeDoRequestCallback(int rv)549 void SpdyHttpStream::MaybeDoRequestCallback(int rv) {
550   CHECK_NE(ERR_IO_PENDING, rv);
551   if (request_callback_)
552     std::move(request_callback_).Run(rv);
553 }
554 
MaybePostRequestCallback(int rv)555 void SpdyHttpStream::MaybePostRequestCallback(int rv) {
556   CHECK_NE(ERR_IO_PENDING, rv);
557   if (request_callback_)
558     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
559         FROM_HERE, base::BindOnce(&SpdyHttpStream::MaybeDoRequestCallback,
560                                   weak_factory_.GetWeakPtr(), rv));
561 }
562 
DoResponseCallback(int rv)563 void SpdyHttpStream::DoResponseCallback(int rv) {
564   CHECK_NE(rv, ERR_IO_PENDING);
565   CHECK(!response_callback_.is_null());
566 
567   // Since Run may result in being called back, reset response_callback_ in
568   // advance.
569   std::move(response_callback_).Run(rv);
570 }
571 
GetRemoteEndpoint(IPEndPoint * endpoint)572 int SpdyHttpStream::GetRemoteEndpoint(IPEndPoint* endpoint) {
573   if (!spdy_session_)
574     return ERR_SOCKET_NOT_CONNECTED;
575 
576   return spdy_session_->GetPeerAddress(endpoint);
577 }
578 
PopulateNetErrorDetails(NetErrorDetails * details)579 void SpdyHttpStream::PopulateNetErrorDetails(NetErrorDetails* details) {
580   details->connection_info = HttpConnectionInfo::kHTTP2;
581   return;
582 }
583 
SetPriority(RequestPriority priority)584 void SpdyHttpStream::SetPriority(RequestPriority priority) {
585   priority_ = priority;
586   if (stream_) {
587     stream_->SetPriority(priority);
588   }
589 }
590 
GetDnsAliases() const591 const std::set<std::string>& SpdyHttpStream::GetDnsAliases() const {
592   return dns_aliases_;
593 }
594 
GetAcceptChViaAlps() const595 std::string_view SpdyHttpStream::GetAcceptChViaAlps() const {
596   if (!request_info_) {
597     return {};
598   }
599 
600   return session()->GetAcceptChViaAlps(url::SchemeHostPort(request_info_->url));
601 }
602 
603 }  // namespace net
604