xref: /aosp_15_r20/external/cronet/net/quic/quic_chromium_client_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/quic/quic_chromium_client_stream.h"
6 
7 #include <string_view>
8 #include <utility>
9 
10 #include "base/functional/bind.h"
11 #include "base/functional/callback_helpers.h"
12 #include "base/location.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/memory/raw_ptr.h"
15 #include "base/metrics/histogram_functions.h"
16 #include "base/task/single_thread_task_runner.h"
17 #include "net/base/io_buffer.h"
18 #include "net/base/net_errors.h"
19 #include "net/http/http_status_code.h"
20 #include "net/log/net_log_event_type.h"
21 #include "net/quic/quic_chromium_client_session.h"
22 #include "net/quic/quic_http_utils.h"
23 #include "net/spdy/spdy_log_util.h"
24 #include "net/third_party/quiche/src/quiche/quic/core/http/quic_spdy_session.h"
25 #include "net/third_party/quiche/src/quiche/quic/core/http/spdy_utils.h"
26 #include "net/third_party/quiche/src/quiche/quic/core/quic_utils.h"
27 #include "net/third_party/quiche/src/quiche/quic/core/quic_write_blocked_list.h"
28 
29 namespace net {
30 namespace {
31 // Sets a boolean to a value, and restores it to the previous value once
32 // the saver goes out of scope.
33 class ScopedBoolSaver {
34  public:
ScopedBoolSaver(bool * var,bool new_val)35   ScopedBoolSaver(bool* var, bool new_val) : var_(var), old_val_(*var) {
36     *var_ = new_val;
37   }
38 
~ScopedBoolSaver()39   ~ScopedBoolSaver() { *var_ = old_val_; }
40 
41  private:
42   raw_ptr<bool> var_;
43   bool old_val_;
44 };
45 }  // namespace
46 
Handle(QuicChromiumClientStream * stream)47 QuicChromiumClientStream::Handle::Handle(QuicChromiumClientStream* stream)
48     : stream_(stream), net_log_(stream->net_log()) {
49   SaveState();
50 }
51 
~Handle()52 QuicChromiumClientStream::Handle::~Handle() {
53   if (stream_) {
54     stream_->ClearHandle();
55     // TODO(rch): If stream_ is still valid, it should probably be Reset()
56     // so that it does not leak.
57     // stream_->Reset(quic::QUIC_STREAM_CANCELLED);
58   }
59 }
60 
OnEarlyHintsAvailable()61 void QuicChromiumClientStream::Handle::OnEarlyHintsAvailable() {
62   if (first_early_hints_time_.is_null())
63     first_early_hints_time_ = base::TimeTicks::Now();
64 
65   if (!read_headers_callback_)
66     return;  // Wait for ReadInitialHeaders to be called.
67 
68   DCHECK(read_headers_buffer_);
69   int rv = stream_->DeliverEarlyHints(read_headers_buffer_);
70   DCHECK_NE(ERR_IO_PENDING, rv);
71 
72   ResetAndRun(std::move(read_headers_callback_), rv);
73 }
74 
OnInitialHeadersAvailable()75 void QuicChromiumClientStream::Handle::OnInitialHeadersAvailable() {
76   if (headers_received_start_time_.is_null())
77     headers_received_start_time_ = base::TimeTicks::Now();
78 
79   if (!read_headers_callback_)
80     return;  // Wait for ReadInitialHeaders to be called.
81 
82   int rv = stream_->DeliverInitialHeaders(read_headers_buffer_);
83   DCHECK_NE(ERR_IO_PENDING, rv);
84 
85   ResetAndRun(std::move(read_headers_callback_), rv);
86 }
87 
OnTrailingHeadersAvailable()88 void QuicChromiumClientStream::Handle::OnTrailingHeadersAvailable() {
89   if (!read_headers_callback_)
90     return;  // Wait for ReadInitialHeaders to be called.
91 
92   int rv = ERR_QUIC_PROTOCOL_ERROR;
93   if (!stream_->DeliverTrailingHeaders(read_headers_buffer_, &rv))
94     rv = ERR_QUIC_PROTOCOL_ERROR;
95 
96   base::UmaHistogramBoolean(
97       "Net.QuicChromiumClientStream.TrailingHeadersProcessSuccess", rv >= 0);
98   ResetAndRun(std::move(read_headers_callback_), rv);
99 }
100 
OnDataAvailable()101 void QuicChromiumClientStream::Handle::OnDataAvailable() {
102   if (!read_body_callback_)
103     return;  // Wait for ReadBody to be called.
104 
105   DCHECK(read_body_buffer_);
106   DCHECK_GT(read_body_buffer_len_, 0);
107 
108   int rv = stream_->Read(read_body_buffer_, read_body_buffer_len_);
109   if (rv == ERR_IO_PENDING)
110     return;  // Spurrious, likely because of trailers?
111 
112   read_body_buffer_ = nullptr;
113   read_body_buffer_len_ = 0;
114   ResetAndRun(std::move(read_body_callback_), rv);
115 }
116 
OnCanWrite()117 void QuicChromiumClientStream::Handle::OnCanWrite() {
118   if (!write_callback_)
119     return;
120 
121   ResetAndRun(std::move(write_callback_), OK);
122 }
123 
OnClose()124 void QuicChromiumClientStream::Handle::OnClose() {
125   if (net_error_ == ERR_UNEXPECTED) {
126     if (stream_error() == quic::QUIC_STREAM_NO_ERROR &&
127         connection_error() == quic::QUIC_NO_ERROR && fin_sent() &&
128         fin_received()) {
129       net_error_ = ERR_CONNECTION_CLOSED;
130     } else {
131       net_error_ = ERR_QUIC_PROTOCOL_ERROR;
132     }
133   }
134   base::UmaHistogramSparse("Net.QuicChromiumClientStream.HandleOnCloseNetError",
135                            -net_error_);
136   base::UmaHistogramSparse(
137       "Net.QuicChromiumClientStream.HandleOnCloseStreamError", stream_error());
138   base::UmaHistogramSparse(
139       "Net.QuicChromiumClientStream.HandleOnCloseConnectionError",
140       connection_error());
141   OnError(net_error_);
142 }
143 
OnError(int error)144 void QuicChromiumClientStream::Handle::OnError(int error) {
145   net_error_ = error;
146   if (stream_)
147     SaveState();
148   stream_ = nullptr;
149 
150   // Post a task to invoke the callbacks to ensure that there is no reentrancy.
151   // A ScopedPacketFlusher might cause an error which closes the stream under
152   // the call stack of the owner of the handle.
153   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
154       FROM_HERE,
155       base::BindOnce(&QuicChromiumClientStream::Handle::InvokeCallbacksOnClose,
156                      weak_factory_.GetWeakPtr(), error));
157 }
158 
InvokeCallbacksOnClose(int error)159 void QuicChromiumClientStream::Handle::InvokeCallbacksOnClose(int error) {
160   // Invoking a callback may cause |this| to be deleted. If this happens, no
161   // more callbacks should be invoked. Guard against this by holding a WeakPtr
162   // to |this| and ensuring it's still valid.
163   auto guard(weak_factory_.GetWeakPtr());
164   for (auto* callback :
165        {&read_headers_callback_, &read_body_callback_, &write_callback_}) {
166     if (*callback)
167       ResetAndRun(std::move(*callback), error);
168     if (!guard.get())
169       return;
170   }
171 }
172 
ReadInitialHeaders(spdy::Http2HeaderBlock * header_block,CompletionOnceCallback callback)173 int QuicChromiumClientStream::Handle::ReadInitialHeaders(
174     spdy::Http2HeaderBlock* header_block,
175     CompletionOnceCallback callback) {
176   ScopedBoolSaver saver(&may_invoke_callbacks_, false);
177   if (!stream_)
178     return net_error_;
179 
180   // Check Early Hints first.
181   int rv = stream_->DeliverEarlyHints(header_block);
182   if (rv != ERR_IO_PENDING) {
183     return rv;
184   }
185 
186   rv = stream_->DeliverInitialHeaders(header_block);
187   if (rv != ERR_IO_PENDING) {
188     return rv;
189   }
190 
191   read_headers_buffer_ = header_block;
192   DCHECK(!read_headers_callback_);
193   SetCallback(std::move(callback), &read_headers_callback_);
194   return ERR_IO_PENDING;
195 }
196 
ReadBody(IOBuffer * buffer,int buffer_len,CompletionOnceCallback callback)197 int QuicChromiumClientStream::Handle::ReadBody(
198     IOBuffer* buffer,
199     int buffer_len,
200     CompletionOnceCallback callback) {
201   ScopedBoolSaver saver(&may_invoke_callbacks_, false);
202   if (IsDoneReading())
203     return OK;
204 
205   if (!stream_)
206     return net_error_;
207 
208   if (stream_->read_side_closed()) {
209     return OK;
210   }
211 
212   int rv = stream_->Read(buffer, buffer_len);
213   if (rv != ERR_IO_PENDING)
214     return rv;
215 
216   DCHECK(buffer);
217   DCHECK_GT(buffer_len, 0);
218 
219   SetCallback(std::move(callback), &read_body_callback_);
220   read_body_buffer_ = buffer;
221   read_body_buffer_len_ = buffer_len;
222   return ERR_IO_PENDING;
223 }
224 
ReadTrailingHeaders(spdy::Http2HeaderBlock * header_block,CompletionOnceCallback callback)225 int QuicChromiumClientStream::Handle::ReadTrailingHeaders(
226     spdy::Http2HeaderBlock* header_block,
227     CompletionOnceCallback callback) {
228   ScopedBoolSaver saver(&may_invoke_callbacks_, false);
229   if (!stream_)
230     return net_error_;
231 
232   int frame_len = 0;
233   if (stream_->DeliverTrailingHeaders(header_block, &frame_len))
234     return frame_len;
235 
236   read_headers_buffer_ = header_block;
237   SetCallback(std::move(callback), &read_headers_callback_);
238   return ERR_IO_PENDING;
239 }
240 
WriteHeaders(spdy::Http2HeaderBlock header_block,bool fin,quiche::QuicheReferenceCountedPointer<quic::QuicAckListenerInterface> ack_notifier_delegate)241 int QuicChromiumClientStream::Handle::WriteHeaders(
242     spdy::Http2HeaderBlock header_block,
243     bool fin,
244     quiche::QuicheReferenceCountedPointer<quic::QuicAckListenerInterface>
245         ack_notifier_delegate) {
246   if (!stream_)
247     return 0;
248   return HandleIOComplete(stream_->WriteHeaders(std::move(header_block), fin,
249                                                 ack_notifier_delegate));
250 }
251 
WriteStreamData(std::string_view data,bool fin,CompletionOnceCallback callback)252 int QuicChromiumClientStream::Handle::WriteStreamData(
253     std::string_view data,
254     bool fin,
255     CompletionOnceCallback callback) {
256   ScopedBoolSaver saver(&may_invoke_callbacks_, false);
257   if (!stream_)
258     return net_error_;
259 
260   if (stream_->WriteStreamData(data, fin)) {
261     return HandleIOComplete(OK);
262   }
263 
264   SetCallback(std::move(callback), &write_callback_);
265   return ERR_IO_PENDING;
266 }
267 
WritevStreamData(const std::vector<scoped_refptr<IOBuffer>> & buffers,const std::vector<int> & lengths,bool fin,CompletionOnceCallback callback)268 int QuicChromiumClientStream::Handle::WritevStreamData(
269     const std::vector<scoped_refptr<IOBuffer>>& buffers,
270     const std::vector<int>& lengths,
271     bool fin,
272     CompletionOnceCallback callback) {
273   ScopedBoolSaver saver(&may_invoke_callbacks_, false);
274   if (!stream_)
275     return net_error_;
276 
277   if (stream_->WritevStreamData(buffers, lengths, fin))
278     return HandleIOComplete(OK);
279 
280   SetCallback(std::move(callback), &write_callback_);
281   return ERR_IO_PENDING;
282 }
283 
WriteConnectUdpPayload(absl::string_view packet)284 int QuicChromiumClientStream::Handle::WriteConnectUdpPayload(
285     absl::string_view packet) {
286   ScopedBoolSaver saver(&may_invoke_callbacks_, false);
287   if (!stream_) {
288     return net_error_;
289   }
290 
291   base::UmaHistogramBoolean(kHttp3DatagramDroppedHistogram,
292                             !stream_->SupportsH3Datagram());
293   if (!stream_->SupportsH3Datagram()) {
294     DLOG(WARNING)
295         << "Dropping datagram because the session has either not received "
296            "settings frame with H3_DATAGRAM yet or received settings that "
297            "indicate datagrams are not supported (i.e., H3_DATAGRAM=0).";
298     return OK;
299   }
300   // Set Context ID to zero as per RFC 9298
301   // (https://datatracker.ietf.org/doc/html/rfc9298#name-http-datagram-payload-forma)
302   // and copy packet data.
303   std::string http_payload;
304   http_payload.resize(1 + packet.size());
305   http_payload[0] = 0;
306   memcpy(&http_payload[1], packet.data(), packet.size());
307 
308   // Attempt to send the HTTP payload as a datagram over the stream.
309   quic::MessageStatus message_status = stream_->SendHttp3Datagram(http_payload);
310 
311   // If the attempt was successful or blocked (e.g., due to buffer
312   // constraints), proceed to handle the I/O completion with an OK status.
313   if (message_status == quic::MessageStatus::MESSAGE_STATUS_SUCCESS ||
314       message_status == quic::MessageStatus::MESSAGE_STATUS_BLOCKED) {
315     return HandleIOComplete(OK);
316   }
317   // If the attempt failed due to a unsupported feature, internal error, or
318   // unexpected condition (encryption not established or message too large),
319   // reset the stream and close the connection.
320   else {
321     // These two errors should not be possible here.
322     DCHECK(message_status !=
323            quic::MessageStatus::MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED);
324     DCHECK(message_status != quic::MessageStatus::MESSAGE_STATUS_TOO_LARGE);
325     DLOG(ERROR) << "Failed to send Http3 Datagram on " << stream_->id();
326     stream_->Reset(quic::QUIC_STREAM_CANCELLED);
327     return ERR_CONNECTION_CLOSED;
328   }
329 }
330 
Read(IOBuffer * buf,int buf_len)331 int QuicChromiumClientStream::Handle::Read(IOBuffer* buf, int buf_len) {
332   if (!stream_)
333     return net_error_;
334   return stream_->Read(buf, buf_len);
335 }
336 
OnFinRead()337 void QuicChromiumClientStream::Handle::OnFinRead() {
338   read_headers_callback_.Reset();
339   if (stream_)
340     stream_->OnFinRead();
341 }
342 
343 void QuicChromiumClientStream::Handle::
DisableConnectionMigrationToCellularNetwork()344     DisableConnectionMigrationToCellularNetwork() {
345   if (stream_)
346     stream_->DisableConnectionMigrationToCellularNetwork();
347 }
348 
SetPriority(const quic::QuicStreamPriority & priority)349 void QuicChromiumClientStream::Handle::SetPriority(
350     const quic::QuicStreamPriority& priority) {
351   if (stream_) {
352     stream_->SetPriority(priority);
353   }
354 }
355 
Reset(quic::QuicRstStreamErrorCode error_code)356 void QuicChromiumClientStream::Handle::Reset(
357     quic::QuicRstStreamErrorCode error_code) {
358   if (stream_)
359     stream_->Reset(error_code);
360 }
361 
RegisterHttp3DatagramVisitor(Http3DatagramVisitor * visitor)362 void QuicChromiumClientStream::Handle::RegisterHttp3DatagramVisitor(
363     Http3DatagramVisitor* visitor) {
364   if (stream_) {
365     stream_->RegisterHttp3DatagramVisitor(visitor);
366   }
367 }
368 
UnregisterHttp3DatagramVisitor()369 void QuicChromiumClientStream::Handle::UnregisterHttp3DatagramVisitor() {
370   if (stream_) {
371     stream_->UnregisterHttp3DatagramVisitor();
372   }
373 }
374 
id() const375 quic::QuicStreamId QuicChromiumClientStream::Handle::id() const {
376   if (!stream_)
377     return id_;
378   return stream_->id();
379 }
380 
connection_error() const381 quic::QuicErrorCode QuicChromiumClientStream::Handle::connection_error() const {
382   if (!stream_)
383     return connection_error_;
384   return stream_->connection_error();
385 }
386 
stream_error() const387 quic::QuicRstStreamErrorCode QuicChromiumClientStream::Handle::stream_error()
388     const {
389   if (!stream_)
390     return stream_error_;
391   return stream_->stream_error();
392 }
393 
connection_wire_error() const394 uint64_t QuicChromiumClientStream::Handle::connection_wire_error() const {
395   if (!stream_) {
396     return connection_wire_error_;
397   }
398   // TODO(crbug.com/40715622): Don't access session. Instead, modify
399   // quic::QuicStream::OnConnectionClosed() to take the wire error code.
400   CHECK(stream_->session());
401   return stream_->session()->wire_error();
402 }
403 
ietf_application_error() const404 uint64_t QuicChromiumClientStream::Handle::ietf_application_error() const {
405   if (!stream_) {
406     return ietf_application_error_;
407   }
408   return stream_->ietf_application_error();
409 }
410 
fin_sent() const411 bool QuicChromiumClientStream::Handle::fin_sent() const {
412   if (!stream_)
413     return fin_sent_;
414   return stream_->fin_sent();
415 }
416 
fin_received() const417 bool QuicChromiumClientStream::Handle::fin_received() const {
418   if (!stream_)
419     return fin_received_;
420   return stream_->fin_received();
421 }
422 
stream_bytes_read() const423 uint64_t QuicChromiumClientStream::Handle::stream_bytes_read() const {
424   if (!stream_)
425     return stream_bytes_read_;
426   return stream_->stream_bytes_read();
427 }
428 
stream_bytes_written() const429 uint64_t QuicChromiumClientStream::Handle::stream_bytes_written() const {
430   if (!stream_)
431     return stream_bytes_written_;
432   return stream_->stream_bytes_written();
433 }
434 
NumBytesConsumed() const435 size_t QuicChromiumClientStream::Handle::NumBytesConsumed() const {
436   if (!stream_)
437     return num_bytes_consumed_;
438   return stream_->sequencer()->NumBytesConsumed();
439 }
440 
HasBytesToRead() const441 bool QuicChromiumClientStream::Handle::HasBytesToRead() const {
442   if (!stream_)
443     return false;
444   return stream_->HasBytesToRead();
445 }
446 
IsDoneReading() const447 bool QuicChromiumClientStream::Handle::IsDoneReading() const {
448   if (!stream_)
449     return is_done_reading_;
450   return stream_->IsDoneReading();
451 }
452 
IsFirstStream() const453 bool QuicChromiumClientStream::Handle::IsFirstStream() const {
454   if (!stream_)
455     return is_first_stream_;
456   return stream_->IsFirstStream();
457 }
458 
can_migrate_to_cellular_network()459 bool QuicChromiumClientStream::Handle::can_migrate_to_cellular_network() {
460   if (!stream_)
461     return false;
462   return stream_->can_migrate_to_cellular_network();
463 }
464 
net_log() const465 const NetLogWithSource& QuicChromiumClientStream::Handle::net_log() const {
466   return net_log_;
467 }
468 
SaveState()469 void QuicChromiumClientStream::Handle::SaveState() {
470   DCHECK(stream_);
471   fin_sent_ = stream_->fin_sent();
472   fin_received_ = stream_->fin_received();
473   num_bytes_consumed_ = stream_->sequencer()->NumBytesConsumed();
474   id_ = stream_->id();
475   connection_error_ = stream_->connection_error();
476   stream_error_ = stream_->stream_error();
477   // TODO(crbug.com/40715622): Don't access stream_->session(). Instead, update
478   // quic::QuicStream::OnConnectionClosed() to take the wire error code.
479   CHECK(stream_->session());
480   connection_wire_error_ = stream_->session()->wire_error();
481   ietf_application_error_ = stream_->ietf_application_error();
482   is_done_reading_ = stream_->IsDoneReading();
483   is_first_stream_ = stream_->IsFirstStream();
484   stream_bytes_read_ = stream_->stream_bytes_read();
485   stream_bytes_written_ = stream_->stream_bytes_written();
486 }
487 
SetCallback(CompletionOnceCallback new_callback,CompletionOnceCallback * callback)488 void QuicChromiumClientStream::Handle::SetCallback(
489     CompletionOnceCallback new_callback,
490     CompletionOnceCallback* callback) {
491   // TODO(rch): Convert this to a DCHECK once we ensure the API is stable and
492   // bug free.
493   CHECK(!may_invoke_callbacks_);
494   *callback = std::move(new_callback);
495 }
496 
ResetAndRun(CompletionOnceCallback callback,int rv)497 void QuicChromiumClientStream::Handle::ResetAndRun(
498     CompletionOnceCallback callback,
499     int rv) {
500   // TODO(rch): Convert this to a DCHECK once we ensure the API is stable and
501   // bug free.
502   CHECK(may_invoke_callbacks_);
503   std::move(callback).Run(rv);
504 }
505 
HandleIOComplete(int rv)506 int QuicChromiumClientStream::Handle::HandleIOComplete(int rv) {
507   // If |stream_| is still valid the stream has not been closed. If the stream
508   // has not been closed, then just return |rv|.
509   if (rv < 0 || stream_)
510     return rv;
511 
512   if (stream_error_ == quic::QUIC_STREAM_NO_ERROR &&
513       connection_error_ == quic::QUIC_NO_ERROR && fin_sent_ && fin_received_) {
514     return rv;
515   }
516 
517   return net_error_;
518 }
519 
SetRequestIdempotency(Idempotency idempotency)520 void QuicChromiumClientStream::Handle::SetRequestIdempotency(
521     Idempotency idempotency) {
522   idempotency_ = idempotency;
523 }
524 
GetRequestIdempotency() const525 Idempotency QuicChromiumClientStream::Handle::GetRequestIdempotency() const {
526   return idempotency_;
527 }
528 
529 quic::QuicPacketLength
GetGuaranteedLargestMessagePayload() const530 QuicChromiumClientStream::Handle::GetGuaranteedLargestMessagePayload() const {
531   if (!stream_) {
532     return 0;
533   }
534   return stream_->GetGuaranteedLargestMessagePayload();
535 }
536 
QuicChromiumClientStream(quic::QuicStreamId id,quic::QuicSpdyClientSessionBase * session,quic::StreamType type,const NetLogWithSource & net_log,const NetworkTrafficAnnotationTag & traffic_annotation)537 QuicChromiumClientStream::QuicChromiumClientStream(
538     quic::QuicStreamId id,
539     quic::QuicSpdyClientSessionBase* session,
540     quic::StreamType type,
541     const NetLogWithSource& net_log,
542     const NetworkTrafficAnnotationTag& traffic_annotation)
543     : quic::QuicSpdyStream(id, session, type),
544       net_log_(net_log),
545       session_(session),
546       quic_version_(session->connection()->transport_version()) {}
547 
QuicChromiumClientStream(quic::PendingStream * pending,quic::QuicSpdyClientSessionBase * session,const NetLogWithSource & net_log,const NetworkTrafficAnnotationTag & traffic_annotation)548 QuicChromiumClientStream::QuicChromiumClientStream(
549     quic::PendingStream* pending,
550     quic::QuicSpdyClientSessionBase* session,
551     const NetLogWithSource& net_log,
552     const NetworkTrafficAnnotationTag& traffic_annotation)
553     : quic::QuicSpdyStream(pending, session),
554       net_log_(net_log),
555       session_(session),
556       quic_version_(session->connection()->transport_version()) {}
557 
~QuicChromiumClientStream()558 QuicChromiumClientStream::~QuicChromiumClientStream() {
559   if (handle_)
560     handle_->OnClose();
561 }
562 
OnInitialHeadersComplete(bool fin,size_t frame_len,const quic::QuicHeaderList & header_list)563 void QuicChromiumClientStream::OnInitialHeadersComplete(
564     bool fin,
565     size_t frame_len,
566     const quic::QuicHeaderList& header_list) {
567   DCHECK(!initial_headers_arrived_);
568   quic::QuicSpdyStream::OnInitialHeadersComplete(fin, frame_len, header_list);
569 
570   spdy::Http2HeaderBlock header_block;
571   int64_t length = -1;
572   if (!quic::SpdyUtils::CopyAndValidateHeaders(header_list, &length,
573                                                &header_block)) {
574     DLOG(ERROR) << "Failed to parse header list: " << header_list.DebugString();
575     ConsumeHeaderList();
576     Reset(quic::QUIC_BAD_APPLICATION_PAYLOAD);
577     return;
578   }
579 
580   // Handle informational response. If the response is an Early Hints response,
581   // deliver the response to the owner of the handle. Otherwise ignore the
582   // response.
583   int response_code;
584   if (!ParseHeaderStatusCode(header_block, &response_code)) {
585     DLOG(ERROR) << "Received invalid response code: '"
586                 << header_block[":status"].as_string() << "' on stream "
587                 << id();
588     Reset(quic::QUIC_BAD_APPLICATION_PAYLOAD);
589     return;
590   }
591 
592   if (response_code == HTTP_SWITCHING_PROTOCOLS) {
593     DLOG(ERROR) << "Received forbidden 101 response code on stream " << id();
594     Reset(quic::QUIC_BAD_APPLICATION_PAYLOAD);
595     return;
596   }
597 
598   if (response_code >= 100 && response_code < 200) {
599     set_headers_decompressed(false);
600     ConsumeHeaderList();
601     if (response_code == HTTP_EARLY_HINTS) {
602       early_hints_.emplace_back(std::move(header_block), frame_len);
603       if (handle_)
604         handle_->OnEarlyHintsAvailable();
605     } else {
606       DVLOG(1) << "Ignore informational response " << response_code
607                << " on stream" << id();
608     }
609     return;
610   }
611 
612   ConsumeHeaderList();
613 
614   // Buffer the headers and deliver them when the handle arrives.
615   initial_headers_arrived_ = true;
616   initial_headers_ = std::move(header_block);
617   initial_headers_frame_len_ = frame_len;
618 
619   if (handle_) {
620     // The handle will be notified of the headers via a posted task.
621     NotifyHandleOfInitialHeadersAvailableLater();
622   }
623 }
624 
OnTrailingHeadersComplete(bool fin,size_t frame_len,const quic::QuicHeaderList & header_list)625 void QuicChromiumClientStream::OnTrailingHeadersComplete(
626     bool fin,
627     size_t frame_len,
628     const quic::QuicHeaderList& header_list) {
629   quic::QuicSpdyStream::OnTrailingHeadersComplete(fin, frame_len, header_list);
630   trailing_headers_frame_len_ = frame_len;
631   if (handle_) {
632     // The handle will be notified of the headers via a posted task.
633     NotifyHandleOfTrailingHeadersAvailableLater();
634   }
635 }
636 
OnBodyAvailable()637 void QuicChromiumClientStream::OnBodyAvailable() {
638   if (!FinishedReadingHeaders() || !headers_delivered_) {
639     // Buffer the data in the sequencer until the headers have been read.
640     return;
641   }
642 
643   if (!HasBytesToRead() && !FinishedReadingTrailers()) {
644     // If there is no data to read, wait until either FIN is received or
645     // trailers are delivered.
646     return;
647   }
648 
649   // The handle will read the data via a posted task, and
650   // will be able to, potentially, read all data which has queued up.
651   if (handle_)
652     NotifyHandleOfDataAvailableLater();
653 }
654 
OnClose()655 void QuicChromiumClientStream::OnClose() {
656   if (handle_) {
657     handle_->OnClose();
658     handle_ = nullptr;
659   }
660   quic::QuicStream::OnClose();
661 }
662 
OnCanWrite()663 void QuicChromiumClientStream::OnCanWrite() {
664   quic::QuicStream::OnCanWrite();
665 
666   if (!HasBufferedData() && handle_)
667     handle_->OnCanWrite();
668 }
669 
WriteHeaders(spdy::Http2HeaderBlock header_block,bool fin,quiche::QuicheReferenceCountedPointer<quic::QuicAckListenerInterface> ack_listener)670 size_t QuicChromiumClientStream::WriteHeaders(
671     spdy::Http2HeaderBlock header_block,
672     bool fin,
673     quiche::QuicheReferenceCountedPointer<quic::QuicAckListenerInterface>
674         ack_listener) {
675   if (!session()->OneRttKeysAvailable()) {
676     auto entry = header_block.find(":method");
677     DCHECK(entry != header_block.end());
678     DCHECK(
679         entry->second != "POST" ||
680         (handle_ != nullptr && handle_->GetRequestIdempotency() == IDEMPOTENT));
681   }
682   net_log_.AddEvent(
683       NetLogEventType::QUIC_CHROMIUM_CLIENT_STREAM_SEND_REQUEST_HEADERS,
684       [&](NetLogCaptureMode capture_mode) {
685         return QuicRequestNetLogParams(id(), &header_block, priority(),
686                                        capture_mode);
687       });
688   size_t len = quic::QuicSpdyStream::WriteHeaders(std::move(header_block), fin,
689                                                   std::move(ack_listener));
690   initial_headers_sent_ = true;
691   return len;
692 }
693 
WriteStreamData(std::string_view data,bool fin)694 bool QuicChromiumClientStream::WriteStreamData(std::string_view data,
695                                                bool fin) {
696   // Writes the data, or buffers it.
697   WriteOrBufferBody(data, fin);
698   return !HasBufferedData();  // Was all data written?
699 }
700 
WritevStreamData(const std::vector<scoped_refptr<IOBuffer>> & buffers,const std::vector<int> & lengths,bool fin)701 bool QuicChromiumClientStream::WritevStreamData(
702     const std::vector<scoped_refptr<IOBuffer>>& buffers,
703     const std::vector<int>& lengths,
704     bool fin) {
705   // Writes the data, or buffers it.
706   for (size_t i = 0; i < buffers.size(); ++i) {
707     bool is_fin = fin && (i == buffers.size() - 1);
708     std::string_view string_data(buffers[i]->data(), lengths[i]);
709     WriteOrBufferBody(string_data, is_fin);
710   }
711   return !HasBufferedData();  // Was all data written?
712 }
713 
714 std::unique_ptr<QuicChromiumClientStream::Handle>
CreateHandle()715 QuicChromiumClientStream::CreateHandle() {
716   DCHECK(!handle_);
717   auto handle = base::WrapUnique(new QuicChromiumClientStream::Handle(this));
718   handle_ = handle.get();
719 
720   // Should this perhaps be via PostTask to make reasoning simpler?
721   if (initial_headers_arrived_) {
722     handle_->OnInitialHeadersAvailable();
723   }
724 
725   return handle;
726 }
727 
ClearHandle()728 void QuicChromiumClientStream::ClearHandle() {
729   handle_ = nullptr;
730 }
731 
OnError(int error)732 void QuicChromiumClientStream::OnError(int error) {
733   if (handle_) {
734     QuicChromiumClientStream::Handle* handle = handle_;
735     handle_ = nullptr;
736     handle->OnError(error);
737   }
738 }
739 
SupportsH3Datagram() const740 bool QuicChromiumClientStream::SupportsH3Datagram() const {
741   return session_->SupportsH3Datagram();
742 }
743 
Read(IOBuffer * buf,int buf_len)744 int QuicChromiumClientStream::Read(IOBuffer* buf, int buf_len) {
745   DCHECK_GT(buf_len, 0);
746   DCHECK(buf->data());
747 
748   if (IsDoneReading())
749     return 0;  // EOF
750 
751   if (!HasBytesToRead())
752     return ERR_IO_PENDING;
753 
754   iovec iov;
755   iov.iov_base = buf->data();
756   iov.iov_len = buf_len;
757   size_t bytes_read = Readv(&iov, 1);
758   // Since HasBytesToRead is true, Readv() must of read some data.
759   DCHECK_NE(0u, bytes_read);
760   return bytes_read;
761 }
762 
NotifyHandleOfInitialHeadersAvailableLater()763 void QuicChromiumClientStream::NotifyHandleOfInitialHeadersAvailableLater() {
764   DCHECK(handle_);
765   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
766       FROM_HERE,
767       base::BindOnce(
768           &QuicChromiumClientStream::NotifyHandleOfInitialHeadersAvailable,
769           weak_factory_.GetWeakPtr()));
770 }
771 
NotifyHandleOfInitialHeadersAvailable()772 void QuicChromiumClientStream::NotifyHandleOfInitialHeadersAvailable() {
773   if (!handle_)
774     return;
775 
776   if (!headers_delivered_)
777     handle_->OnInitialHeadersAvailable();
778 }
779 
NotifyHandleOfTrailingHeadersAvailableLater()780 void QuicChromiumClientStream::NotifyHandleOfTrailingHeadersAvailableLater() {
781   DCHECK(handle_);
782   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
783       FROM_HERE,
784       base::BindOnce(
785           &QuicChromiumClientStream::NotifyHandleOfTrailingHeadersAvailable,
786           weak_factory_.GetWeakPtr()));
787 }
788 
NotifyHandleOfTrailingHeadersAvailable()789 void QuicChromiumClientStream::NotifyHandleOfTrailingHeadersAvailable() {
790   if (!handle_)
791     return;
792 
793   // If trailers aren't decompressed it means that trailers are invalid
794   // (e.g., contain ":status" field). Don't notify to the handle if trailers
795   // aren't decompressed since the stream will be closed and
796   // `headers_delivered_` won't become true.
797   if (!trailers_decompressed())
798     return;
799 
800   // Notify only after the handle reads initial headers.
801   if (!headers_delivered_)
802     return;
803 
804   // Post an async task to notify handle of the FIN flag.
805   NotifyHandleOfDataAvailableLater();
806   handle_->OnTrailingHeadersAvailable();
807 }
808 
DeliverEarlyHints(spdy::Http2HeaderBlock * headers)809 int QuicChromiumClientStream::DeliverEarlyHints(
810     spdy::Http2HeaderBlock* headers) {
811   if (early_hints_.empty()) {
812     return ERR_IO_PENDING;
813   }
814 
815   DCHECK(!headers_delivered_);
816 
817   EarlyHints& hints = early_hints_.front();
818   *headers = std::move(hints.headers);
819   size_t frame_len = hints.frame_len;
820   early_hints_.pop_front();
821 
822   net_log_.AddEvent(
823       NetLogEventType::
824           QUIC_CHROMIUM_CLIENT_STREAM_READ_EARLY_HINTS_RESPONSE_HEADERS,
825       [&](NetLogCaptureMode capture_mode) {
826         return QuicResponseNetLogParams(id(), fin_received(), headers,
827                                         capture_mode);
828       });
829 
830   return frame_len;
831 }
832 
DeliverInitialHeaders(spdy::Http2HeaderBlock * headers)833 int QuicChromiumClientStream::DeliverInitialHeaders(
834     spdy::Http2HeaderBlock* headers) {
835   if (!initial_headers_arrived_) {
836     return ERR_IO_PENDING;
837   }
838 
839   headers_delivered_ = true;
840 
841   if (initial_headers_.empty()) {
842     return ERR_INVALID_RESPONSE;
843   }
844 
845   net_log_.AddEvent(
846       NetLogEventType::QUIC_CHROMIUM_CLIENT_STREAM_READ_RESPONSE_HEADERS,
847       [&](NetLogCaptureMode capture_mode) {
848         return QuicResponseNetLogParams(id(), fin_received(), &initial_headers_,
849                                         capture_mode);
850       });
851 
852   *headers = std::move(initial_headers_);
853   return initial_headers_frame_len_;
854 }
855 
DeliverTrailingHeaders(spdy::Http2HeaderBlock * headers,int * frame_len)856 bool QuicChromiumClientStream::DeliverTrailingHeaders(
857     spdy::Http2HeaderBlock* headers,
858     int* frame_len) {
859   if (trailing_headers_frame_len_ == 0) {
860     return false;
861   }
862 
863   net_log_.AddEvent(
864       NetLogEventType::QUIC_CHROMIUM_CLIENT_STREAM_READ_RESPONSE_TRAILERS,
865       [&](NetLogCaptureMode capture_mode) {
866         return QuicResponseNetLogParams(id(), fin_received(),
867                                         &received_trailers(), capture_mode);
868       });
869 
870   *headers = received_trailers().Clone();
871   *frame_len = trailing_headers_frame_len_;
872 
873   MarkTrailersConsumed();
874   return true;
875 }
876 
NotifyHandleOfDataAvailableLater()877 void QuicChromiumClientStream::NotifyHandleOfDataAvailableLater() {
878   DCHECK(handle_);
879   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
880       FROM_HERE,
881       base::BindOnce(&QuicChromiumClientStream::NotifyHandleOfDataAvailable,
882                      weak_factory_.GetWeakPtr()));
883 }
884 
NotifyHandleOfDataAvailable()885 void QuicChromiumClientStream::NotifyHandleOfDataAvailable() {
886   if (handle_)
887     handle_->OnDataAvailable();
888 }
889 
DisableConnectionMigrationToCellularNetwork()890 void QuicChromiumClientStream::DisableConnectionMigrationToCellularNetwork() {
891   can_migrate_to_cellular_network_ = false;
892 }
893 
894 quic::QuicPacketLength
GetGuaranteedLargestMessagePayload() const895 QuicChromiumClientStream::GetGuaranteedLargestMessagePayload() const {
896   if (!session()) {
897     return 0;
898   }
899   return session()->GetGuaranteedLargestMessagePayload();
900 }
901 
IsFirstStream()902 bool QuicChromiumClientStream::IsFirstStream() {
903   return id() == quic::QuicUtils::GetFirstBidirectionalStreamId(
904                      quic_version_, quic::Perspective::IS_CLIENT);
905 }
906 
907 }  // namespace net
908