• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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