Rewrite AndroidSyncSettings to be significantly simpler.
[chromium-blink-merge.git] / net / quic / quic_time_wait_list_manager.cc
blob2e3e16d5a58b0ba72e2d3392e7a6c2621fc53754
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/quic/quic_time_wait_list_manager.h"
7 #include <errno.h>
9 #include "base/containers/hash_tables.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/stl_util.h"
12 #include "net/base/ip_endpoint.h"
13 #include "net/quic/crypto/crypto_protocol.h"
14 #include "net/quic/crypto/quic_decrypter.h"
15 #include "net/quic/crypto/quic_encrypter.h"
16 #include "net/quic/quic_clock.h"
17 #include "net/quic/quic_connection_helper.h"
18 #include "net/quic/quic_flags.h"
19 #include "net/quic/quic_framer.h"
20 #include "net/quic/quic_protocol.h"
21 #include "net/quic/quic_server_session.h"
22 #include "net/quic/quic_utils.h"
24 using base::StringPiece;
26 namespace net {
28 // A very simple alarm that just informs the QuicTimeWaitListManager to clean
29 // up old connection_ids. This alarm should be unregistered and deleted before
30 // the QuicTimeWaitListManager is deleted.
31 class ConnectionIdCleanUpAlarm : public QuicAlarm::Delegate {
32 public:
33 explicit ConnectionIdCleanUpAlarm(
34 QuicTimeWaitListManager* time_wait_list_manager)
35 : time_wait_list_manager_(time_wait_list_manager) {
38 QuicTime OnAlarm() override {
39 time_wait_list_manager_->CleanUpOldConnectionIds();
40 // Let the time wait manager register the alarm at appropriate time.
41 return QuicTime::Zero();
44 private:
45 // Not owned.
46 QuicTimeWaitListManager* time_wait_list_manager_;
49 // This class stores pending public reset packets to be sent to clients.
50 // server_address - server address on which a packet what was received for
51 // a connection_id in time wait state.
52 // client_address - address of the client that sent that packet. Needed to send
53 // the public reset packet back to the client.
54 // packet - the pending public reset packet that is to be sent to the client.
55 // created instance takes the ownership of this packet.
56 class QuicTimeWaitListManager::QueuedPacket {
57 public:
58 QueuedPacket(const IPEndPoint& server_address,
59 const IPEndPoint& client_address,
60 QuicEncryptedPacket* packet)
61 : server_address_(server_address),
62 client_address_(client_address),
63 packet_(packet) {
66 const IPEndPoint& server_address() const { return server_address_; }
67 const IPEndPoint& client_address() const { return client_address_; }
68 QuicEncryptedPacket* packet() { return packet_.get(); }
70 private:
71 const IPEndPoint server_address_;
72 const IPEndPoint client_address_;
73 scoped_ptr<QuicEncryptedPacket> packet_;
75 DISALLOW_COPY_AND_ASSIGN(QueuedPacket);
78 QuicTimeWaitListManager::QuicTimeWaitListManager(
79 QuicPacketWriter* writer,
80 QuicServerSessionVisitor* visitor,
81 QuicConnectionHelperInterface* helper,
82 const QuicVersionVector& supported_versions)
83 : helper_(helper),
84 kTimeWaitPeriod_(
85 QuicTime::Delta::FromSeconds(FLAGS_quic_time_wait_list_seconds)),
86 connection_id_clean_up_alarm_(
87 helper_->CreateAlarm(new ConnectionIdCleanUpAlarm(this))),
88 writer_(writer),
89 visitor_(visitor) {
90 SetConnectionIdCleanUpAlarm();
93 QuicTimeWaitListManager::~QuicTimeWaitListManager() {
94 connection_id_clean_up_alarm_->Cancel();
95 STLDeleteElements(&pending_packets_queue_);
96 for (ConnectionIdMap::iterator it = connection_id_map_.begin();
97 it != connection_id_map_.end();
98 ++it) {
99 delete it->second.close_packet;
103 void QuicTimeWaitListManager::AddConnectionIdToTimeWait(
104 QuicConnectionId connection_id,
105 QuicVersion version,
106 QuicEncryptedPacket* close_packet) {
107 int num_packets = 0;
108 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
109 const bool new_connection_id = it == connection_id_map_.end();
110 if (!new_connection_id) { // Replace record if it is reinserted.
111 num_packets = it->second.num_packets;
112 delete it->second.close_packet;
113 connection_id_map_.erase(it);
115 TrimTimeWaitListIfNeeded();
116 if (FLAGS_quic_limit_time_wait_list_size) {
117 DCHECK_LT(num_connections(),
118 static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections));
120 ConnectionIdData data(num_packets,
121 version,
122 helper_->GetClock()->ApproximateNow(),
123 close_packet);
124 connection_id_map_.insert(std::make_pair(connection_id, data));
125 if (new_connection_id) {
126 visitor_->OnConnectionAddedToTimeWaitList(connection_id);
130 bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
131 QuicConnectionId connection_id) const {
132 return ContainsKey(connection_id_map_, connection_id);
135 QuicVersion QuicTimeWaitListManager::GetQuicVersionFromConnectionId(
136 QuicConnectionId connection_id) {
137 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
138 DCHECK(it != connection_id_map_.end());
139 return (it->second).version;
142 void QuicTimeWaitListManager::OnCanWrite() {
143 while (!pending_packets_queue_.empty()) {
144 QueuedPacket* queued_packet = pending_packets_queue_.front();
145 if (!WriteToWire(queued_packet)) {
146 return;
148 pending_packets_queue_.pop_front();
149 delete queued_packet;
153 void QuicTimeWaitListManager::ProcessPacket(
154 const IPEndPoint& server_address,
155 const IPEndPoint& client_address,
156 QuicConnectionId connection_id,
157 QuicPacketSequenceNumber sequence_number,
158 const QuicEncryptedPacket& /*packet*/) {
159 DCHECK(IsConnectionIdInTimeWait(connection_id));
160 DVLOG(1) << "Processing " << connection_id << " in time wait state.";
161 // TODO(satyamshekhar): Think about handling packets from different client
162 // addresses.
163 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
164 DCHECK(it != connection_id_map_.end());
165 // Increment the received packet count.
166 ++((it->second).num_packets);
167 if (!ShouldSendResponse((it->second).num_packets)) {
168 return;
170 if (it->second.close_packet) {
171 QueuedPacket* queued_packet =
172 new QueuedPacket(server_address,
173 client_address,
174 it->second.close_packet->Clone());
175 // Takes ownership of the packet.
176 SendOrQueuePacket(queued_packet);
177 } else {
178 SendPublicReset(server_address,
179 client_address,
180 connection_id,
181 sequence_number);
185 // Returns true if the number of packets received for this connection_id is a
186 // power of 2 to throttle the number of public reset packets we send to a
187 // client.
188 bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) {
189 return (received_packet_count & (received_packet_count - 1)) == 0;
192 void QuicTimeWaitListManager::SendPublicReset(
193 const IPEndPoint& server_address,
194 const IPEndPoint& client_address,
195 QuicConnectionId connection_id,
196 QuicPacketSequenceNumber rejected_sequence_number) {
197 QuicPublicResetPacket packet;
198 packet.public_header.connection_id = connection_id;
199 packet.public_header.reset_flag = true;
200 packet.public_header.version_flag = false;
201 packet.rejected_sequence_number = rejected_sequence_number;
202 // TODO(satyamshekhar): generate a valid nonce for this connection_id.
203 packet.nonce_proof = 1010101;
204 packet.client_address = client_address;
205 QueuedPacket* queued_packet = new QueuedPacket(
206 server_address,
207 client_address,
208 BuildPublicReset(packet));
209 // Takes ownership of the packet.
210 SendOrQueuePacket(queued_packet);
213 QuicEncryptedPacket* QuicTimeWaitListManager::BuildPublicReset(
214 const QuicPublicResetPacket& packet) {
215 return QuicFramer::BuildPublicResetPacket(packet);
218 // Either sends the packet and deletes it or makes pending queue the
219 // owner of the packet.
220 void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) {
221 if (WriteToWire(packet)) {
222 delete packet;
223 } else {
224 // pending_packets_queue takes the ownership of the queued packet.
225 pending_packets_queue_.push_back(packet);
229 bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
230 if (writer_->IsWriteBlocked()) {
231 visitor_->OnWriteBlocked(this);
232 return false;
234 WriteResult result = writer_->WritePacket(
235 queued_packet->packet()->data(),
236 queued_packet->packet()->length(),
237 queued_packet->server_address().address(),
238 queued_packet->client_address());
239 if (result.status == WRITE_STATUS_BLOCKED) {
240 // If blocked and unbuffered, return false to retry sending.
241 DCHECK(writer_->IsWriteBlocked());
242 visitor_->OnWriteBlocked(this);
243 return writer_->IsWriteBlockedDataBuffered();
244 } else if (result.status == WRITE_STATUS_ERROR) {
245 LOG(WARNING) << "Received unknown error while sending reset packet to "
246 << queued_packet->client_address().ToString() << ": "
247 << strerror(result.error_code);
249 return true;
252 void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
253 connection_id_clean_up_alarm_->Cancel();
254 QuicTime now = helper_->GetClock()->ApproximateNow();
255 QuicTime next_alarm_time = now;
256 if (!connection_id_map_.empty()) {
257 QuicTime oldest_connection_id =
258 connection_id_map_.begin()->second.time_added;
259 if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
260 next_alarm_time = oldest_connection_id.Add(kTimeWaitPeriod_);
261 } else {
262 LOG(ERROR) << "ConnectionId lingered for longer than kTimeWaitPeriod";
264 } else {
265 // No connection_ids added so none will expire before kTimeWaitPeriod_.
266 next_alarm_time = now.Add(kTimeWaitPeriod_);
269 connection_id_clean_up_alarm_->Set(next_alarm_time);
272 bool QuicTimeWaitListManager::MaybeExpireOldestConnection(
273 QuicTime expiration_time) {
274 if (connection_id_map_.empty()) {
275 return false;
277 ConnectionIdMap::iterator it = connection_id_map_.begin();
278 QuicTime oldest_connection_id_time = it->second.time_added;
279 if (oldest_connection_id_time > expiration_time) {
280 // Too recent, don't retire.
281 return false;
283 // This connection_id has lived its age, retire it now.
284 const QuicConnectionId connection_id = it->first;
285 delete it->second.close_packet;
286 connection_id_map_.erase(it);
287 visitor_->OnConnectionRemovedFromTimeWaitList(connection_id);
288 return true;
291 void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
292 QuicTime now = helper_->GetClock()->ApproximateNow();
293 QuicTime expiration = now.Subtract(kTimeWaitPeriod_);
294 if (FLAGS_quic_limit_time_wait_list_size) {
295 while (MaybeExpireOldestConnection(expiration)) {
297 } else {
298 while (!connection_id_map_.empty()) {
299 ConnectionIdMap::iterator it = connection_id_map_.begin();
300 QuicTime oldest_connection_id = it->second.time_added;
301 if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
302 break;
304 const QuicConnectionId connection_id = it->first;
305 // This connection_id has lived its age, retire it now.
306 delete it->second.close_packet;
307 connection_id_map_.erase(it);
308 visitor_->OnConnectionRemovedFromTimeWaitList(connection_id);
312 SetConnectionIdCleanUpAlarm();
315 void QuicTimeWaitListManager::TrimTimeWaitListIfNeeded() {
316 if (FLAGS_quic_limit_time_wait_list_size) {
317 if (FLAGS_quic_time_wait_list_max_connections < 0) {
318 return;
320 while (num_connections() >=
321 static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections)) {
322 MaybeExpireOldestConnection(QuicTime::Infinite());
327 } // namespace net