1 // Copyright 2015 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_reader.h"
6
7 #include "base/functional/bind.h"
8 #include "base/location.h"
9 #include "base/metrics/histogram_macros.h"
10 #include "base/task/single_thread_task_runner.h"
11 #include "net/base/net_errors.h"
12 #include "net/quic/address_utils.h"
13 #include "net/third_party/quiche/src/quiche/quic/core/quic_clock.h"
14
15 namespace net {
16
17 namespace {
18 // Add 1 because some of our UDP socket implementations do not read successfully
19 // when the packet length is equal to the read buffer size.
20 const size_t kReadBufferSize =
21 static_cast<size_t>(quic::kMaxIncomingPacketSize + 1);
22 } // namespace
23
QuicChromiumPacketReader(std::unique_ptr<DatagramClientSocket> socket,const quic::QuicClock * clock,Visitor * visitor,int yield_after_packets,quic::QuicTime::Delta yield_after_duration,const NetLogWithSource & net_log)24 QuicChromiumPacketReader::QuicChromiumPacketReader(
25 std::unique_ptr<DatagramClientSocket> socket,
26 const quic::QuicClock* clock,
27 Visitor* visitor,
28 int yield_after_packets,
29 quic::QuicTime::Delta yield_after_duration,
30 const NetLogWithSource& net_log)
31 : socket_(std::move(socket)),
32 visitor_(visitor),
33 clock_(clock),
34 yield_after_packets_(yield_after_packets),
35 yield_after_duration_(yield_after_duration),
36 yield_after_(quic::QuicTime::Infinite()),
37 read_buffer_(base::MakeRefCounted<IOBufferWithSize>(kReadBufferSize)),
38 net_log_(net_log) {}
39
40 QuicChromiumPacketReader::~QuicChromiumPacketReader() = default;
41
StartReading()42 void QuicChromiumPacketReader::StartReading() {
43 for (;;) {
44 if (read_pending_)
45 return;
46
47 if (num_packets_read_ == 0)
48 yield_after_ = clock_->Now() + yield_after_duration_;
49
50 CHECK(socket_);
51 read_pending_ = true;
52 int rv =
53 socket_->Read(read_buffer_.get(), read_buffer_->size(),
54 base::BindOnce(&QuicChromiumPacketReader::OnReadComplete,
55 weak_factory_.GetWeakPtr()));
56 UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.AsyncRead", rv == ERR_IO_PENDING);
57 if (rv == ERR_IO_PENDING) {
58 num_packets_read_ = 0;
59 return;
60 }
61
62 if (++num_packets_read_ > yield_after_packets_ ||
63 clock_->Now() > yield_after_) {
64 num_packets_read_ = 0;
65 // Data was read, process it.
66 // Schedule the work through the message loop to 1) prevent infinite
67 // recursion and 2) avoid blocking the thread for too long.
68 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
69 FROM_HERE, base::BindOnce(&QuicChromiumPacketReader::OnReadComplete,
70 weak_factory_.GetWeakPtr(), rv));
71 } else {
72 if (!ProcessReadResult(rv)) {
73 return;
74 }
75 }
76 }
77 }
78
CloseSocket()79 void QuicChromiumPacketReader::CloseSocket() {
80 socket_->Close();
81 }
82
ProcessReadResult(int result)83 bool QuicChromiumPacketReader::ProcessReadResult(int result) {
84 read_pending_ = false;
85 if (result <= 0 && net_log_.IsCapturing()) {
86 net_log_.AddEventWithIntParams(NetLogEventType::QUIC_READ_ERROR,
87 "net_error", result);
88 }
89 if (result == 0) {
90 // 0-length UDP packets are legal but useless, ignore them.
91 return true;
92 }
93 if (result == ERR_MSG_TOO_BIG) {
94 // This indicates that we received a UDP packet larger than our receive
95 // buffer, ignore it.
96 return true;
97 }
98 if (result < 0) {
99 // Report all other errors to the visitor.
100 return visitor_->OnReadError(result, socket_.get());
101 }
102
103 quic::QuicReceivedPacket packet(read_buffer_->data(), result, clock_->Now());
104 IPEndPoint local_address;
105 IPEndPoint peer_address;
106 socket_->GetLocalAddress(&local_address);
107 socket_->GetPeerAddress(&peer_address);
108 auto self = weak_factory_.GetWeakPtr();
109 // Notifies the visitor that |this| reader gets a new packet, which may delete
110 // |this| if |this| is a connectivity probing reader.
111 return visitor_->OnPacket(packet, ToQuicSocketAddress(local_address),
112 ToQuicSocketAddress(peer_address)) &&
113 self;
114 }
115
OnReadComplete(int result)116 void QuicChromiumPacketReader::OnReadComplete(int result) {
117 if (ProcessReadResult(result))
118 StartReading();
119 }
120
121 } // namespace net
122