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/websockets/websocket_throttle.h"
12 #include "base/memory/singleton.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "net/base/io_buffer.h"
18 #include "net/socket_stream/socket_stream.h"
19 #include "net/websockets/websocket_job.h"
25 const size_t kMaxWebSocketJobsThrottled
= 1024;
29 WebSocketThrottle::WebSocketThrottle() {
32 WebSocketThrottle::~WebSocketThrottle() {
33 DCHECK(queue_
.empty());
34 DCHECK(addr_map_
.empty());
38 WebSocketThrottle
* WebSocketThrottle::GetInstance() {
39 return Singleton
<WebSocketThrottle
>::get();
42 bool WebSocketThrottle::PutInQueue(WebSocketJob
* job
) {
43 if (queue_
.size() >= kMaxWebSocketJobsThrottled
)
46 queue_
.push_back(job
);
47 const AddressList
& address_list
= job
->address_list();
48 std::set
<IPEndPoint
> address_set
;
49 for (AddressList::const_iterator addr_iter
= address_list
.begin();
50 addr_iter
!= address_list
.end();
52 const IPEndPoint
& address
= *addr_iter
;
53 // If |address| is already processed, don't do it again.
54 if (!address_set
.insert(address
).second
)
57 ConnectingAddressMap::iterator iter
= addr_map_
.find(address
);
58 if (iter
== addr_map_
.end()) {
59 ConnectingAddressMap::iterator new_queue
=
60 addr_map_
.insert(make_pair(address
, ConnectingQueue())).first
;
61 new_queue
->second
.push_back(job
);
63 DCHECK(!iter
->second
.empty());
64 iter
->second
.push_back(job
);
66 DVLOG(1) << "Waiting on " << address
.ToString();
73 void WebSocketThrottle::RemoveFromQueue(WebSocketJob
* job
) {
74 ConnectingQueue::iterator queue_iter
=
75 std::find(queue_
.begin(), queue_
.end(), job
);
76 if (queue_iter
== queue_
.end())
78 queue_
.erase(queue_iter
);
80 std::set
<WebSocketJob
*> wakeup_candidates
;
82 const AddressList
& resolved_address_list
= job
->address_list();
83 std::set
<IPEndPoint
> address_set
;
84 for (AddressList::const_iterator addr_iter
= resolved_address_list
.begin();
85 addr_iter
!= resolved_address_list
.end();
87 const IPEndPoint
& address
= *addr_iter
;
88 // If |address| is already processed, don't do it again.
89 if (!address_set
.insert(address
).second
)
92 ConnectingAddressMap::iterator map_iter
= addr_map_
.find(address
);
93 DCHECK(map_iter
!= addr_map_
.end());
95 ConnectingQueue
& per_address_queue
= map_iter
->second
;
96 DCHECK(!per_address_queue
.empty());
97 // Job may not be front of the queue if the socket is closed while waiting.
98 ConnectingQueue::iterator per_address_queue_iter
=
99 std::find(per_address_queue
.begin(), per_address_queue
.end(), job
);
100 bool was_front
= false;
101 if (per_address_queue_iter
!= per_address_queue
.end()) {
102 was_front
= (per_address_queue_iter
== per_address_queue
.begin());
103 per_address_queue
.erase(per_address_queue_iter
);
105 if (per_address_queue
.empty()) {
106 addr_map_
.erase(map_iter
);
107 } else if (was_front
) {
108 // The new front is a wake-up candidate.
109 wakeup_candidates
.insert(per_address_queue
.front());
113 WakeupSocketIfNecessary(wakeup_candidates
);
116 void WebSocketThrottle::WakeupSocketIfNecessary(
117 const std::set
<WebSocketJob
*>& wakeup_candidates
) {
118 for (std::set
<WebSocketJob
*>::const_iterator iter
= wakeup_candidates
.begin();
119 iter
!= wakeup_candidates
.end();
121 WebSocketJob
* job
= *iter
;
122 if (!job
->IsWaiting())
125 bool should_wakeup
= true;
126 const AddressList
& resolved_address_list
= job
->address_list();
127 for (AddressList::const_iterator addr_iter
= resolved_address_list
.begin();
128 addr_iter
!= resolved_address_list
.end();
130 const IPEndPoint
& address
= *addr_iter
;
131 ConnectingAddressMap::iterator map_iter
= addr_map_
.find(address
);
132 DCHECK(map_iter
!= addr_map_
.end());
133 const ConnectingQueue
& per_address_queue
= map_iter
->second
;
134 if (job
!= per_address_queue
.front()) {
135 should_wakeup
= false;