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