xref: /aosp_15_r20/external/cronet/net/quic/quic_chromium_packet_writer.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2013 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_packet_writer.h"
6 
7 #include <string>
8 #include <utility>
9 
10 #include "base/check_op.h"
11 #include "base/functional/bind.h"
12 #include "base/location.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/metrics/sparse_histogram.h"
15 #include "base/task/sequenced_task_runner.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/net_errors.h"
18 #include "net/quic/quic_chromium_client_session.h"
19 #include "net/traffic_annotation/network_traffic_annotation.h"
20 
21 namespace net {
22 
23 namespace {
24 
25 enum NotReusableReason {
26   NOT_REUSABLE_NULLPTR = 0,
27   NOT_REUSABLE_TOO_SMALL = 1,
28   NOT_REUSABLE_REF_COUNT = 2,
29   NUM_NOT_REUSABLE_REASONS = 3,
30 };
31 
32 const int kMaxRetries = 12;  // 2^12 = 4 seconds, which should be a LOT.
33 
RecordNotReusableReason(NotReusableReason reason)34 void RecordNotReusableReason(NotReusableReason reason) {
35   UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.WritePacketNotReusable", reason,
36                             NUM_NOT_REUSABLE_REASONS);
37 }
38 
RecordRetryCount(int count)39 void RecordRetryCount(int count) {
40   UMA_HISTOGRAM_EXACT_LINEAR("Net.QuicSession.RetryAfterWriteErrorCount2",
41                              count, kMaxRetries + 1);
42 }
43 
44 const net::NetworkTrafficAnnotationTag kTrafficAnnotation =
45     net::DefineNetworkTrafficAnnotation("quic_chromium_packet_writer", R"(
46         semantics {
47           sender: "QUIC Packet Writer"
48           description:
49             "A QUIC packet is written to the wire based on a request from "
50             "a QUIC stream."
51           trigger:
52             "A request from QUIC stream."
53           data: "Any data sent by the stream."
54           destination: OTHER
55           destination_other: "Any destination choosen by the stream."
56         }
57         policy {
58           cookies_allowed: NO
59           setting: "This feature cannot be disabled in settings."
60           policy_exception_justification:
61             "Essential for network access."
62         }
63         comments:
64           "All requests that are received by QUIC streams have network traffic "
65           "annotation, but the annotation is not passed to the writer function "
66           "due to technial overheads. Please see QuicChromiumClientSession and "
67           "QuicChromiumClientStream classes for references."
68     )");
69 
70 }  // namespace
71 
ReusableIOBuffer(size_t capacity)72 QuicChromiumPacketWriter::ReusableIOBuffer::ReusableIOBuffer(size_t capacity)
73     : IOBufferWithSize(capacity), capacity_(capacity) {}
74 
75 QuicChromiumPacketWriter::ReusableIOBuffer::~ReusableIOBuffer() = default;
76 
Set(const char * buffer,size_t buf_len)77 void QuicChromiumPacketWriter::ReusableIOBuffer::Set(const char* buffer,
78                                                      size_t buf_len) {
79   CHECK_LE(buf_len, capacity_);
80   CHECK(HasOneRef());
81   size_ = buf_len;
82   std::memcpy(data(), buffer, buf_len);
83 }
84 
QuicChromiumPacketWriter(DatagramClientSocket * socket,base::SequencedTaskRunner * task_runner)85 QuicChromiumPacketWriter::QuicChromiumPacketWriter(
86     DatagramClientSocket* socket,
87     base::SequencedTaskRunner* task_runner)
88     : socket_(socket),
89       packet_(base::MakeRefCounted<ReusableIOBuffer>(
90           quic::kMaxOutgoingPacketSize)) {
91   retry_timer_.SetTaskRunner(task_runner);
92   write_callback_ = base::BindRepeating(
93       &QuicChromiumPacketWriter::OnWriteComplete, weak_factory_.GetWeakPtr());
94 }
95 
96 QuicChromiumPacketWriter::~QuicChromiumPacketWriter() = default;
97 
set_force_write_blocked(bool force_write_blocked)98 void QuicChromiumPacketWriter::set_force_write_blocked(
99     bool force_write_blocked) {
100   force_write_blocked_ = force_write_blocked;
101   if (!IsWriteBlocked() && delegate_ != nullptr)
102     delegate_->OnWriteUnblocked();
103 }
104 
SetPacket(const char * buffer,size_t buf_len)105 void QuicChromiumPacketWriter::SetPacket(const char* buffer, size_t buf_len) {
106   if (UNLIKELY(!packet_)) {
107     packet_ = base::MakeRefCounted<ReusableIOBuffer>(
108         std::max(buf_len, static_cast<size_t>(quic::kMaxOutgoingPacketSize)));
109     RecordNotReusableReason(NOT_REUSABLE_NULLPTR);
110   }
111   if (UNLIKELY(packet_->capacity() < buf_len)) {
112     packet_ = base::MakeRefCounted<ReusableIOBuffer>(buf_len);
113     RecordNotReusableReason(NOT_REUSABLE_TOO_SMALL);
114   }
115   if (UNLIKELY(!packet_->HasOneRef())) {
116     packet_ = base::MakeRefCounted<ReusableIOBuffer>(
117         std::max(buf_len, static_cast<size_t>(quic::kMaxOutgoingPacketSize)));
118     RecordNotReusableReason(NOT_REUSABLE_REF_COUNT);
119   }
120   packet_->Set(buffer, buf_len);
121 }
122 
WritePacket(const char * buffer,size_t buf_len,const quic::QuicIpAddress & self_address,const quic::QuicSocketAddress & peer_address,quic::PerPacketOptions *,const quic::QuicPacketWriterParams &)123 quic::WriteResult QuicChromiumPacketWriter::WritePacket(
124     const char* buffer,
125     size_t buf_len,
126     const quic::QuicIpAddress& self_address,
127     const quic::QuicSocketAddress& peer_address,
128     quic::PerPacketOptions* /*options*/,
129     const quic::QuicPacketWriterParams& /*params*/) {
130   CHECK(!IsWriteBlocked());
131   SetPacket(buffer, buf_len);
132   return WritePacketToSocketImpl();
133 }
134 
WritePacketToSocket(scoped_refptr<ReusableIOBuffer> packet)135 void QuicChromiumPacketWriter::WritePacketToSocket(
136     scoped_refptr<ReusableIOBuffer> packet) {
137   CHECK(!force_write_blocked_);
138   CHECK(!IsWriteBlocked());
139   packet_ = std::move(packet);
140   quic::WriteResult result = WritePacketToSocketImpl();
141   if (result.error_code != ERR_IO_PENDING)
142     OnWriteComplete(result.error_code);
143 }
144 
WritePacketToSocketImpl()145 quic::WriteResult QuicChromiumPacketWriter::WritePacketToSocketImpl() {
146   base::TimeTicks now = base::TimeTicks::Now();
147 
148   // When the connection is closed, the socket is cleaned up. If socket is
149   // invalidated, packets should not be written to the socket.
150   CHECK(socket_);
151   int rv = socket_->Write(packet_.get(), packet_->size(), write_callback_,
152                           kTrafficAnnotation);
153 
154   if (MaybeRetryAfterWriteError(rv))
155     return quic::WriteResult(quic::WRITE_STATUS_BLOCKED_DATA_BUFFERED,
156                              ERR_IO_PENDING);
157 
158   if (rv < 0 && rv != ERR_IO_PENDING && delegate_ != nullptr) {
159     // If write error, then call delegate's HandleWriteError, which
160     // may be able to migrate and rewrite packet on a new socket.
161     // HandleWriteError returns the outcome of that rewrite attempt.
162     rv = delegate_->HandleWriteError(rv, std::move(packet_));
163     DCHECK(packet_ == nullptr);
164   }
165 
166   quic::WriteStatus status = quic::WRITE_STATUS_OK;
167   if (rv < 0) {
168     if (rv != ERR_IO_PENDING) {
169       status = quic::WRITE_STATUS_ERROR;
170     } else {
171       status = quic::WRITE_STATUS_BLOCKED_DATA_BUFFERED;
172       write_in_progress_ = true;
173     }
174   }
175 
176   base::TimeDelta delta = base::TimeTicks::Now() - now;
177   if (status == quic::WRITE_STATUS_OK) {
178     UMA_HISTOGRAM_TIMES("Net.QuicSession.PacketWriteTime.Synchronous", delta);
179   } else if (quic::IsWriteBlockedStatus(status)) {
180     UMA_HISTOGRAM_TIMES("Net.QuicSession.PacketWriteTime.Asynchronous", delta);
181   }
182 
183   return quic::WriteResult(status, rv);
184 }
185 
RetryPacketAfterNoBuffers()186 void QuicChromiumPacketWriter::RetryPacketAfterNoBuffers() {
187   DCHECK_GT(retry_count_, 0);
188   if (socket_) {
189     quic::WriteResult result = WritePacketToSocketImpl();
190     if (result.error_code != ERR_IO_PENDING) {
191       OnWriteComplete(result.error_code);
192     }
193   }
194 }
195 
IsWriteBlocked() const196 bool QuicChromiumPacketWriter::IsWriteBlocked() const {
197   return (force_write_blocked_ || write_in_progress_);
198 }
199 
SetWritable()200 void QuicChromiumPacketWriter::SetWritable() {
201   write_in_progress_ = false;
202 }
203 
MessageTooBigErrorCode() const204 std::optional<int> QuicChromiumPacketWriter::MessageTooBigErrorCode() const {
205   return ERR_MSG_TOO_BIG;
206 }
207 
OnWriteComplete(int rv)208 void QuicChromiumPacketWriter::OnWriteComplete(int rv) {
209   DCHECK_NE(rv, ERR_IO_PENDING);
210   write_in_progress_ = false;
211   if (delegate_ == nullptr)
212     return;
213 
214   if (rv < 0) {
215     if (MaybeRetryAfterWriteError(rv))
216       return;
217 
218     // If write error, then call delegate's HandleWriteError, which
219     // may be able to migrate and rewrite packet on a new socket.
220     // HandleWriteError returns the outcome of that rewrite attempt.
221     rv = delegate_->HandleWriteError(rv, std::move(packet_));
222     DCHECK(packet_ == nullptr);
223     if (rv == ERR_IO_PENDING) {
224       // Set write blocked back as write error is encountered in this writer,
225       // delegate may be able to handle write error but this writer will never
226       // be used to write any new data.
227       write_in_progress_ = true;
228       return;
229     }
230   }
231   if (retry_count_ != 0) {
232     RecordRetryCount(retry_count_);
233     retry_count_ = 0;
234   }
235 
236   if (rv < 0)
237     delegate_->OnWriteError(rv);
238   else if (!force_write_blocked_)
239     delegate_->OnWriteUnblocked();
240 }
241 
MaybeRetryAfterWriteError(int rv)242 bool QuicChromiumPacketWriter::MaybeRetryAfterWriteError(int rv) {
243   if (rv != ERR_NO_BUFFER_SPACE)
244     return false;
245 
246   if (retry_count_ >= kMaxRetries) {
247     RecordRetryCount(retry_count_);
248     return false;
249   }
250 
251   retry_timer_.Start(
252       FROM_HERE, base::Milliseconds(UINT64_C(1) << retry_count_),
253       base::BindOnce(&QuicChromiumPacketWriter::RetryPacketAfterNoBuffers,
254                      weak_factory_.GetWeakPtr()));
255   retry_count_++;
256   write_in_progress_ = true;
257   return true;
258 }
259 
GetMaxPacketSize(const quic::QuicSocketAddress & peer_address) const260 quic::QuicByteCount QuicChromiumPacketWriter::GetMaxPacketSize(
261     const quic::QuicSocketAddress& peer_address) const {
262   return quic::kMaxOutgoingPacketSize;
263 }
264 
SupportsReleaseTime() const265 bool QuicChromiumPacketWriter::SupportsReleaseTime() const {
266   return false;
267 }
268 
IsBatchMode() const269 bool QuicChromiumPacketWriter::IsBatchMode() const {
270   return false;
271 }
272 
SupportsEcn() const273 bool QuicChromiumPacketWriter::SupportsEcn() const {
274   return false;
275 }
276 
GetNextWriteLocation(const quic::QuicIpAddress & self_address,const quic::QuicSocketAddress & peer_address)277 quic::QuicPacketBuffer QuicChromiumPacketWriter::GetNextWriteLocation(
278     const quic::QuicIpAddress& self_address,
279     const quic::QuicSocketAddress& peer_address) {
280   return {nullptr, nullptr};
281 }
282 
Flush()283 quic::WriteResult QuicChromiumPacketWriter::Flush() {
284   return quic::WriteResult(quic::WRITE_STATUS_OK, 0);
285 }
286 
OnSocketClosed(DatagramClientSocket * socket)287 bool QuicChromiumPacketWriter::OnSocketClosed(DatagramClientSocket* socket) {
288   if (socket_ == socket) {
289     socket_ = nullptr;
290     return true;
291   }
292   return false;
293 }
294 
295 }  // namespace net
296