base: Make it possible to replace the MessageLoop's task runner
[chromium-blink-merge.git] / net / tools / quic / quic_time_wait_list_manager.cc
blobc9405e550dd9760bc0f55ba2beea5243c577cc06
1 // Copyright (c) 2012 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/tools/quic/quic_time_wait_list_manager.h"
7 #include <errno.h>
9 #include "base/logging.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_flags.h"
18 #include "net/quic/quic_framer.h"
19 #include "net/quic/quic_protocol.h"
20 #include "net/quic/quic_utils.h"
21 #include "net/tools/quic/quic_server_session.h"
23 using base::StringPiece;
25 namespace net {
26 namespace tools {
28 // A very simple alarm that just informs the QuicTimeWaitListManager to clean
29 // up old connection_ids. This alarm should be cancelled 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_;
48 DISALLOW_COPY_AND_ASSIGN(ConnectionIdCleanUpAlarm);
52 // This class stores pending public reset packets to be sent to clients.
53 // server_address - server address on which a packet what was received for
54 // a connection_id in time wait state.
55 // client_address - address of the client that sent that packet. Needed to send
56 // the public reset packet back to the client.
57 // packet - the pending public reset packet that is to be sent to the client.
58 // created instance takes the ownership of this packet.
59 class QuicTimeWaitListManager::QueuedPacket {
60 public:
61 QueuedPacket(const IPEndPoint& server_address,
62 const IPEndPoint& client_address,
63 QuicEncryptedPacket* packet)
64 : server_address_(server_address),
65 client_address_(client_address),
66 packet_(packet) {
69 const IPEndPoint& server_address() const { return server_address_; }
70 const IPEndPoint& client_address() const { return client_address_; }
71 QuicEncryptedPacket* packet() { return packet_.get(); }
73 private:
74 const IPEndPoint server_address_;
75 const IPEndPoint client_address_;
76 scoped_ptr<QuicEncryptedPacket> packet_;
78 DISALLOW_COPY_AND_ASSIGN(QueuedPacket);
81 QuicTimeWaitListManager::QuicTimeWaitListManager(
82 QuicPacketWriter* writer,
83 QuicServerSessionVisitor* visitor,
84 QuicConnectionHelperInterface* helper,
85 const QuicVersionVector& supported_versions)
86 : time_wait_period_(
87 QuicTime::Delta::FromSeconds(FLAGS_quic_time_wait_list_seconds)),
88 connection_id_clean_up_alarm_(
89 helper->CreateAlarm(new ConnectionIdCleanUpAlarm(this))),
90 clock_(helper->GetClock()),
91 writer_(writer),
92 visitor_(visitor) {
93 SetConnectionIdCleanUpAlarm();
96 QuicTimeWaitListManager::~QuicTimeWaitListManager() {
97 connection_id_clean_up_alarm_->Cancel();
98 STLDeleteElements(&pending_packets_queue_);
99 for (ConnectionIdMap::iterator it = connection_id_map_.begin();
100 it != connection_id_map_.end();
101 ++it) {
102 delete it->second.close_packet;
106 void QuicTimeWaitListManager::AddConnectionIdToTimeWait(
107 QuicConnectionId connection_id,
108 QuicVersion version,
109 bool connection_rejected_statelessly,
110 QuicEncryptedPacket* close_packet) {
111 DCHECK(!connection_rejected_statelessly || !close_packet)
112 << "Connections that were rejected statelessly should not "
113 << "have a close packet. connection_id = " << connection_id;
114 int num_packets = 0;
115 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
116 const bool new_connection_id = it == connection_id_map_.end();
117 if (!new_connection_id) { // Replace record if it is reinserted.
118 num_packets = it->second.num_packets;
119 delete it->second.close_packet;
120 connection_id_map_.erase(it);
122 TrimTimeWaitListIfNeeded();
123 DCHECK_LT(num_connections(),
124 static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections));
125 ConnectionIdData data(num_packets, version, clock_->ApproximateNow(),
126 close_packet, connection_rejected_statelessly);
127 connection_id_map_.insert(std::make_pair(connection_id, data));
128 if (new_connection_id) {
129 visitor_->OnConnectionAddedToTimeWaitList(connection_id);
133 bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
134 QuicConnectionId connection_id) const {
135 return ContainsKey(connection_id_map_, connection_id);
138 QuicVersion QuicTimeWaitListManager::GetQuicVersionFromConnectionId(
139 QuicConnectionId connection_id) {
140 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
141 DCHECK(it != connection_id_map_.end());
142 return (it->second).version;
145 void QuicTimeWaitListManager::OnCanWrite() {
146 while (!pending_packets_queue_.empty()) {
147 QueuedPacket* queued_packet = pending_packets_queue_.front();
148 if (!WriteToWire(queued_packet)) {
149 return;
151 pending_packets_queue_.pop_front();
152 delete queued_packet;
156 void QuicTimeWaitListManager::ProcessPacket(
157 const IPEndPoint& server_address,
158 const IPEndPoint& client_address,
159 QuicConnectionId connection_id,
160 QuicPacketSequenceNumber sequence_number,
161 const QuicEncryptedPacket& /*packet*/) {
162 DCHECK(IsConnectionIdInTimeWait(connection_id));
163 DVLOG(1) << "Processing " << connection_id << " in time wait state.";
164 // TODO(satyamshekhar): Think about handling packets from different client
165 // addresses.
166 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
167 DCHECK(it != connection_id_map_.end());
168 // Increment the received packet count.
169 ConnectionIdData* connection_data = &it->second;
170 ++(connection_data->num_packets);
171 if (!ShouldSendResponse(connection_data->num_packets)) {
172 return;
174 if (connection_data->close_packet) {
175 QueuedPacket* queued_packet = new QueuedPacket(
176 server_address, client_address, connection_data->close_packet->Clone());
177 // Takes ownership of the packet.
178 SendOrQueuePacket(queued_packet);
179 } else if (!connection_data->connection_rejected_statelessly) {
180 SendPublicReset(server_address,
181 client_address,
182 connection_id,
183 sequence_number);
184 } else {
185 DVLOG(3) << "Time wait list not sending response for connection "
186 << connection_id << " due to previous stateless reject.";
190 // Returns true if the number of packets received for this connection_id is a
191 // power of 2 to throttle the number of public reset packets we send to a
192 // client.
193 bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) {
194 return (received_packet_count & (received_packet_count - 1)) == 0;
197 void QuicTimeWaitListManager::SendPublicReset(
198 const IPEndPoint& server_address,
199 const IPEndPoint& client_address,
200 QuicConnectionId connection_id,
201 QuicPacketSequenceNumber rejected_sequence_number) {
202 QuicPublicResetPacket packet;
203 packet.public_header.connection_id = connection_id;
204 packet.public_header.reset_flag = true;
205 packet.public_header.version_flag = false;
206 packet.rejected_sequence_number = rejected_sequence_number;
207 // TODO(satyamshekhar): generate a valid nonce for this connection_id.
208 packet.nonce_proof = 1010101;
209 packet.client_address = client_address;
210 QueuedPacket* queued_packet = new QueuedPacket(
211 server_address,
212 client_address,
213 BuildPublicReset(packet));
214 // Takes ownership of the packet.
215 SendOrQueuePacket(queued_packet);
218 QuicEncryptedPacket* QuicTimeWaitListManager::BuildPublicReset(
219 const QuicPublicResetPacket& packet) {
220 return QuicFramer::BuildPublicResetPacket(packet);
223 // Either sends the packet and deletes it or makes pending queue the
224 // owner of the packet.
225 void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) {
226 if (WriteToWire(packet)) {
227 delete packet;
228 } else {
229 // pending_packets_queue takes the ownership of the queued packet.
230 pending_packets_queue_.push_back(packet);
234 bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
235 if (writer_->IsWriteBlocked()) {
236 visitor_->OnWriteBlocked(this);
237 return false;
239 WriteResult result = writer_->WritePacket(
240 queued_packet->packet()->data(),
241 queued_packet->packet()->length(),
242 queued_packet->server_address().address(),
243 queued_packet->client_address());
244 if (result.status == WRITE_STATUS_BLOCKED) {
245 // If blocked and unbuffered, return false to retry sending.
246 DCHECK(writer_->IsWriteBlocked());
247 visitor_->OnWriteBlocked(this);
248 return writer_->IsWriteBlockedDataBuffered();
249 } else if (result.status == WRITE_STATUS_ERROR) {
250 LOG(WARNING) << "Received unknown error while sending reset packet to "
251 << queued_packet->client_address().ToString() << ": "
252 << strerror(result.error_code);
254 return true;
257 void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
258 connection_id_clean_up_alarm_->Cancel();
259 QuicTime::Delta next_alarm_interval = QuicTime::Delta::Zero();
260 if (!connection_id_map_.empty()) {
261 QuicTime oldest_connection_id =
262 connection_id_map_.begin()->second.time_added;
263 QuicTime now = clock_->ApproximateNow();
264 if (now.Subtract(oldest_connection_id) < time_wait_period_) {
265 next_alarm_interval = oldest_connection_id.Add(time_wait_period_)
266 .Subtract(now);
267 } else {
268 LOG(ERROR) << "ConnectionId lingered for longer than time_wait_period_";
270 } else {
271 // No connection_ids added so none will expire before time_wait_period_.
272 next_alarm_interval = time_wait_period_;
275 connection_id_clean_up_alarm_->Set(
276 clock_->ApproximateNow().Add(next_alarm_interval));
279 bool QuicTimeWaitListManager::MaybeExpireOldestConnection(
280 QuicTime expiration_time) {
281 if (connection_id_map_.empty()) {
282 return false;
284 ConnectionIdMap::iterator it = connection_id_map_.begin();
285 QuicTime oldest_connection_id_time = it->second.time_added;
286 if (oldest_connection_id_time > expiration_time) {
287 // Too recent, don't retire.
288 return false;
290 // This connection_id has lived its age, retire it now.
291 const QuicConnectionId connection_id = it->first;
292 delete it->second.close_packet;
293 connection_id_map_.erase(it);
294 visitor_->OnConnectionRemovedFromTimeWaitList(connection_id);
295 return true;
298 void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
299 QuicTime now = clock_->ApproximateNow();
300 QuicTime expiration = now.Subtract(time_wait_period_);
302 while (MaybeExpireOldestConnection(expiration)) {
305 SetConnectionIdCleanUpAlarm();
308 void QuicTimeWaitListManager::TrimTimeWaitListIfNeeded() {
309 if (FLAGS_quic_time_wait_list_max_connections < 0) {
310 return;
312 while (num_connections() >=
313 static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections)) {
314 MaybeExpireOldestConnection(QuicTime::Infinite());
318 } // namespace tools
319 } // namespace net