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