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 : IOBuffer(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 *)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 DCHECK(!IsWriteBlocked());
130 SetPacket(buffer, buf_len);
131 return WritePacketToSocketImpl();
132 }
133
WritePacketToSocket(scoped_refptr<ReusableIOBuffer> packet)134 void QuicChromiumPacketWriter::WritePacketToSocket(
135 scoped_refptr<ReusableIOBuffer> packet) {
136 DCHECK(!force_write_blocked_);
137 packet_ = std::move(packet);
138 quic::WriteResult result = WritePacketToSocketImpl();
139 if (result.error_code != ERR_IO_PENDING)
140 OnWriteComplete(result.error_code);
141 }
142
WritePacketToSocketImpl()143 quic::WriteResult QuicChromiumPacketWriter::WritePacketToSocketImpl() {
144 base::TimeTicks now = base::TimeTicks::Now();
145
146 int rv = socket_->Write(packet_.get(), packet_->size(), write_callback_,
147 kTrafficAnnotation);
148
149 if (MaybeRetryAfterWriteError(rv))
150 return quic::WriteResult(quic::WRITE_STATUS_BLOCKED_DATA_BUFFERED,
151 ERR_IO_PENDING);
152
153 if (rv < 0 && rv != ERR_IO_PENDING && delegate_ != nullptr) {
154 // If write error, then call delegate's HandleWriteError, which
155 // may be able to migrate and rewrite packet on a new socket.
156 // HandleWriteError returns the outcome of that rewrite attempt.
157 rv = delegate_->HandleWriteError(rv, std::move(packet_));
158 DCHECK(packet_ == nullptr);
159 }
160
161 quic::WriteStatus status = quic::WRITE_STATUS_OK;
162 if (rv < 0) {
163 if (rv != ERR_IO_PENDING) {
164 status = quic::WRITE_STATUS_ERROR;
165 } else {
166 status = quic::WRITE_STATUS_BLOCKED_DATA_BUFFERED;
167 write_in_progress_ = true;
168 }
169 }
170
171 base::TimeDelta delta = base::TimeTicks::Now() - now;
172 if (status == quic::WRITE_STATUS_OK) {
173 UMA_HISTOGRAM_TIMES("Net.QuicSession.PacketWriteTime.Synchronous", delta);
174 } else if (quic::IsWriteBlockedStatus(status)) {
175 UMA_HISTOGRAM_TIMES("Net.QuicSession.PacketWriteTime.Asynchronous", delta);
176 }
177
178 return quic::WriteResult(status, rv);
179 }
180
RetryPacketAfterNoBuffers()181 void QuicChromiumPacketWriter::RetryPacketAfterNoBuffers() {
182 DCHECK_GT(retry_count_, 0);
183 quic::WriteResult result = WritePacketToSocketImpl();
184 if (result.error_code != ERR_IO_PENDING)
185 OnWriteComplete(result.error_code);
186 }
187
IsWriteBlocked() const188 bool QuicChromiumPacketWriter::IsWriteBlocked() const {
189 return (force_write_blocked_ || write_in_progress_);
190 }
191
SetWritable()192 void QuicChromiumPacketWriter::SetWritable() {
193 write_in_progress_ = false;
194 }
195
MessageTooBigErrorCode() const196 absl::optional<int> QuicChromiumPacketWriter::MessageTooBigErrorCode() const {
197 return ERR_MSG_TOO_BIG;
198 }
199
OnWriteComplete(int rv)200 void QuicChromiumPacketWriter::OnWriteComplete(int rv) {
201 DCHECK_NE(rv, ERR_IO_PENDING);
202 write_in_progress_ = false;
203 if (delegate_ == nullptr)
204 return;
205
206 if (rv < 0) {
207 if (MaybeRetryAfterWriteError(rv))
208 return;
209
210 // If write error, then call delegate's HandleWriteError, which
211 // may be able to migrate and rewrite packet on a new socket.
212 // HandleWriteError returns the outcome of that rewrite attempt.
213 rv = delegate_->HandleWriteError(rv, std::move(packet_));
214 DCHECK(packet_ == nullptr);
215 if (rv == ERR_IO_PENDING) {
216 // Set write blocked back as write error is encountered in this writer,
217 // delegate may be able to handle write error but this writer will never
218 // be used to write any new data.
219 write_in_progress_ = true;
220 return;
221 }
222 }
223 if (retry_count_ != 0) {
224 RecordRetryCount(retry_count_);
225 retry_count_ = 0;
226 }
227
228 if (rv < 0)
229 delegate_->OnWriteError(rv);
230 else if (!force_write_blocked_)
231 delegate_->OnWriteUnblocked();
232 }
233
MaybeRetryAfterWriteError(int rv)234 bool QuicChromiumPacketWriter::MaybeRetryAfterWriteError(int rv) {
235 if (rv != ERR_NO_BUFFER_SPACE)
236 return false;
237
238 if (retry_count_ >= kMaxRetries) {
239 RecordRetryCount(retry_count_);
240 return false;
241 }
242
243 retry_timer_.Start(
244 FROM_HERE, base::Milliseconds(UINT64_C(1) << retry_count_),
245 base::BindOnce(&QuicChromiumPacketWriter::RetryPacketAfterNoBuffers,
246 weak_factory_.GetWeakPtr()));
247 retry_count_++;
248 write_in_progress_ = true;
249 return true;
250 }
251
GetMaxPacketSize(const quic::QuicSocketAddress & peer_address) const252 quic::QuicByteCount QuicChromiumPacketWriter::GetMaxPacketSize(
253 const quic::QuicSocketAddress& peer_address) const {
254 return quic::kMaxOutgoingPacketSize;
255 }
256
SupportsReleaseTime() const257 bool QuicChromiumPacketWriter::SupportsReleaseTime() const {
258 return false;
259 }
260
IsBatchMode() const261 bool QuicChromiumPacketWriter::IsBatchMode() const {
262 return false;
263 }
264
GetNextWriteLocation(const quic::QuicIpAddress & self_address,const quic::QuicSocketAddress & peer_address)265 quic::QuicPacketBuffer QuicChromiumPacketWriter::GetNextWriteLocation(
266 const quic::QuicIpAddress& self_address,
267 const quic::QuicSocketAddress& peer_address) {
268 return {nullptr, nullptr};
269 }
270
Flush()271 quic::WriteResult QuicChromiumPacketWriter::Flush() {
272 return quic::WriteResult(quic::WRITE_STATUS_OK, 0);
273 }
274
275 } // namespace net
276