Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(15)

Side by Side Diff: content/browser/renderer_host/websocket_dispatcher_host.cc

Issue 972963002: Per-renderer WebSocket throttling (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Style fixes, bug fixes. Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/renderer_host/websocket_dispatcher_host.h" 5 #include "content/browser/renderer_host/websocket_dispatcher_host.h"
6 6
7 #include <string> 7 #include <string>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/callback.h" 10 #include "base/callback.h"
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/rand_util.h"
12 #include "base/stl_util.h" 13 #include "base/stl_util.h"
13 #include "content/browser/child_process_security_policy_impl.h" 14 #include "content/browser/child_process_security_policy_impl.h"
14 #include "content/browser/renderer_host/websocket_host.h" 15 #include "content/browser/renderer_host/websocket_host.h"
15 #include "content/common/websocket_messages.h" 16 #include "content/common/websocket_messages.h"
16 17
17 namespace content { 18 namespace content {
18 19
19 namespace { 20 namespace {
20 21
21 // Many methods defined in this file return a WebSocketHostState enum 22 // Many methods defined in this file return a WebSocketHostState enum
22 // value. Make WebSocketHostState visible at file scope so it doesn't have to be 23 // value. Make WebSocketHostState visible at file scope so it doesn't have to be
23 // fully-qualified every time. 24 // fully-qualified every time.
24 typedef WebSocketDispatcherHost::WebSocketHostState WebSocketHostState; 25 typedef WebSocketDispatcherHost::WebSocketHostState WebSocketHostState;
25 26
26 } // namespace 27 } // namespace
27 28
28 WebSocketDispatcherHost::WebSocketDispatcherHost( 29 WebSocketDispatcherHost::WebSocketDispatcherHost(
29 int process_id, 30 int process_id,
30 const GetRequestContextCallback& get_context_callback) 31 const GetRequestContextCallback& get_context_callback)
31 : BrowserMessageFilter(WebSocketMsgStart), 32 : BrowserMessageFilter(WebSocketMsgStart),
32 process_id_(process_id), 33 process_id_(process_id),
33 get_context_callback_(get_context_callback), 34 get_context_callback_(get_context_callback),
34 websocket_host_factory_( 35 websocket_host_factory_(
35 base::Bind(&WebSocketDispatcherHost::CreateWebSocketHost, 36 base::Bind(&WebSocketDispatcherHost::CreateWebSocketHost,
36 base::Unretained(this))) {} 37 base::Unretained(this))),
38 num_pending_connections_(0),
39 num_current_succeeded_connections_(0),
40 num_previous_succeeded_connections_(0),
41 num_current_failed_connections_(0),
42 num_previous_failed_connections_(0) {}
37 43
38 WebSocketDispatcherHost::WebSocketDispatcherHost( 44 WebSocketDispatcherHost::WebSocketDispatcherHost(
39 int process_id, 45 int process_id,
40 const GetRequestContextCallback& get_context_callback, 46 const GetRequestContextCallback& get_context_callback,
41 const WebSocketHostFactory& websocket_host_factory) 47 const WebSocketHostFactory& websocket_host_factory)
42 : BrowserMessageFilter(WebSocketMsgStart), 48 : BrowserMessageFilter(WebSocketMsgStart),
43 process_id_(process_id), 49 process_id_(process_id),
44 get_context_callback_(get_context_callback), 50 get_context_callback_(get_context_callback),
45 websocket_host_factory_(websocket_host_factory) {} 51 websocket_host_factory_(websocket_host_factory) {}
46 52
47 WebSocketHost* WebSocketDispatcherHost::CreateWebSocketHost(int routing_id) { 53 WebSocketHost* WebSocketDispatcherHost::CreateWebSocketHost(int routing_id,
48 return new WebSocketHost(routing_id, this, get_context_callback_.Run()); 54 int delay_in_ms) {
55 return new WebSocketHost(routing_id, this, get_context_callback_.Run(),
56 delay_in_ms);
49 } 57 }
50 58
51 bool WebSocketDispatcherHost::OnMessageReceived(const IPC::Message& message) { 59 bool WebSocketDispatcherHost::OnMessageReceived(const IPC::Message& message) {
52 switch (message.type()) { 60 switch (message.type()) {
53 case WebSocketHostMsg_AddChannelRequest::ID: 61 case WebSocketHostMsg_AddChannelRequest::ID:
54 case WebSocketMsg_SendFrame::ID: 62 case WebSocketMsg_SendFrame::ID:
55 case WebSocketMsg_FlowControl::ID: 63 case WebSocketMsg_FlowControl::ID:
56 case WebSocketMsg_DropChannel::ID: 64 case WebSocketMsg_DropChannel::ID:
57 break; 65 break;
58 66
59 default: 67 default:
60 // Every message that has not been handled by a previous filter passes 68 // Every message that has not been handled by a previous filter passes
61 // through here, so it is good to pass them on as efficiently as possible. 69 // through here, so it is good to pass them on as efficiently as possible.
62 return false; 70 return false;
63 } 71 }
64 72
65 int routing_id = message.routing_id(); 73 int routing_id = message.routing_id();
66 WebSocketHost* host = GetHost(routing_id); 74 WebSocketHost* host = GetHost(routing_id);
67 if (message.type() == WebSocketHostMsg_AddChannelRequest::ID) { 75 if (message.type() == WebSocketHostMsg_AddChannelRequest::ID) {
68 if (host) { 76 if (host) {
69 DVLOG(1) << "routing_id=" << routing_id << " already in use."; 77 DVLOG(1) << "routing_id=" << routing_id << " already in use.";
70 // The websocket multiplexing spec says to should drop the physical 78 // The websocket multiplexing spec says to should drop the physical
71 // connection in this case, but there isn't a real physical connection 79 // connection in this case, but there isn't a real physical connection
72 // to the renderer, and killing the renderer for this would seem to be a 80 // to the renderer, and killing the renderer for this would seem to be a
73 // little extreme. So for now just ignore the bogus request. 81 // little extreme. So for now just ignore the bogus request.
74 return true; // We handled the message (by ignoring it). 82 return true; // We handled the message (by ignoring it).
75 } 83 }
76 host = websocket_host_factory_.Run(routing_id); 84 if (num_pending_connections_ > 255) { // FIXME: 255
Adam Rice 2015/03/03 15:10:02 Nitpick: use "TODO(hiroshige)" rather than "FIXME"
hiroshige 2015/03/04 06:02:37 Done.
85 if(!Send(new WebSocketMsg_NotifyFailure(routing_id,
86 "Error in connection establishment: net::ERR_INSUFFICIENT_RESOURCES"
87 ))) {
88 DVLOG(1) << "Sending of message type "
89 << "WebSocketMsg_NotifyFailure failed.";
90 }
91 return true;
92 }
93 host = websocket_host_factory_.Run(routing_id, CalculateDelayInMs());
77 hosts_.insert(WebSocketHostTable::value_type(routing_id, host)); 94 hosts_.insert(WebSocketHostTable::value_type(routing_id, host));
95 ++num_pending_connections_;
96 if (!timer_.IsRunning())
97 timer_.Start(FROM_HERE,
98 base::TimeDelta::FromMinutes(2),
99 this,
100 &WebSocketDispatcherHost::FinishThrottlingPeriod);
78 } 101 }
79 if (!host) { 102 if (!host) {
80 DVLOG(1) << "Received invalid routing ID " << routing_id 103 DVLOG(1) << "Received invalid routing ID " << routing_id
81 << " from renderer."; 104 << " from renderer.";
82 return true; // We handled the message (by ignoring it). 105 return true; // We handled the message (by ignoring it).
83 } 106 }
84 return host->OnMessageReceived(message); 107 return host->OnMessageReceived(message);
85 } 108 }
86 109
87 bool WebSocketDispatcherHost::CanReadRawCookies() const { 110 bool WebSocketDispatcherHost::CanReadRawCookies() const {
(...skipping 26 matching lines...) Expand all
114 const std::string& selected_protocol, 137 const std::string& selected_protocol,
115 const std::string& extensions) { 138 const std::string& extensions) {
116 if (SendOrDrop(new WebSocketMsg_AddChannelResponse( 139 if (SendOrDrop(new WebSocketMsg_AddChannelResponse(
117 routing_id, fail, selected_protocol, extensions)) == 140 routing_id, fail, selected_protocol, extensions)) ==
118 WEBSOCKET_HOST_DELETED) 141 WEBSOCKET_HOST_DELETED)
119 return WEBSOCKET_HOST_DELETED; 142 return WEBSOCKET_HOST_DELETED;
120 if (fail) { 143 if (fail) {
121 DeleteWebSocketHost(routing_id); 144 DeleteWebSocketHost(routing_id);
122 return WEBSOCKET_HOST_DELETED; 145 return WEBSOCKET_HOST_DELETED;
123 } 146 }
147
148 // Update throttling counters (success).
149 WebSocketHost* host = GetHost(routing_id);
150 DCHECK(host);
151 host->OnHandshakeSucceeded();
152 --num_pending_connections_;
153 DCHECK(num_pending_connections_ >= 0);
Adam Rice 2015/03/03 15:10:02 Nitpick: Use DCHECK_GE().
hiroshige 2015/03/04 06:02:37 Done.
154 ++num_current_succeeded_connections_;
Adam Rice 2015/03/03 15:10:02 Are we sure that num_current_succeeded_connections
hiroshige 2015/03/04 06:02:37 About >9M successful (or failed) handshakes per se
Adam Rice 2015/03/04 08:20:49 9M successes per second would never be possible wi
Adam Rice 2015/03/04 08:35:10 Another approach would be to stop incrementing num
155
124 return WEBSOCKET_HOST_ALIVE; 156 return WEBSOCKET_HOST_ALIVE;
125 } 157 }
126 158
127 WebSocketHostState WebSocketDispatcherHost::SendFrame( 159 WebSocketHostState WebSocketDispatcherHost::SendFrame(
128 int routing_id, 160 int routing_id,
129 bool fin, 161 bool fin,
130 WebSocketMessageType type, 162 WebSocketMessageType type,
131 const std::vector<char>& data) { 163 const std::vector<char>& data) {
132 return SendOrDrop(new WebSocketMsg_SendFrame(routing_id, fin, type, data)); 164 return SendOrDrop(new WebSocketMsg_SendFrame(routing_id, fin, type, data));
133 } 165 }
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
193 hosts[i]->GoAway(); 225 hosts[i]->GoAway();
194 hosts[i] = NULL; 226 hosts[i] = NULL;
195 } 227 }
196 228
197 STLDeleteContainerPairSecondPointers(hosts_.begin(), hosts_.end()); 229 STLDeleteContainerPairSecondPointers(hosts_.begin(), hosts_.end());
198 } 230 }
199 231
200 void WebSocketDispatcherHost::DeleteWebSocketHost(int routing_id) { 232 void WebSocketDispatcherHost::DeleteWebSocketHost(int routing_id) {
201 WebSocketHostTable::iterator it = hosts_.find(routing_id); 233 WebSocketHostTable::iterator it = hosts_.find(routing_id);
202 DCHECK(it != hosts_.end()); 234 DCHECK(it != hosts_.end());
235
236 if (!it->second->handshakeSucceeded()) {
237 // Update throttling counters (failure).
238 --num_pending_connections_;
239 DCHECK(num_pending_connections_ >= 0);
240 ++num_current_failed_connections_;
241 }
242
203 delete it->second; 243 delete it->second;
204 hosts_.erase(it); 244 hosts_.erase(it);
245
246 DCHECK(static_cast<size_t>(num_pending_connections_) <= hosts_.size());
247 }
248
249 int64 WebSocketDispatcherHost::CalculateDelayInMs() const {
Adam Rice 2015/03/03 15:10:02 This would be a good place to have a link to the d
hiroshige 2015/03/04 06:02:37 Done.
250 int f = num_previous_failed_connections_ +
251 num_current_failed_connections_;
252 int s = num_previous_succeeded_connections_ +
253 num_current_succeeded_connections_;
254 int p = num_pending_connections_;
255 return base::RandInt(1000, 5000) *
256 (1 << std::min(p + f / (s + 1), 16)) / 65536;
257 }
258
259 void WebSocketDispatcherHost::FinishThrottlingPeriod() {
260 num_previous_failed_connections_ = num_current_failed_connections_;
261 num_current_failed_connections_ = 0;
262
263 num_previous_succeeded_connections_ = num_current_succeeded_connections_;
264 num_current_succeeded_connections_ = 0;
265
266 if (num_pending_connections_ == 0 &&
267 num_previous_failed_connections_ == 0 &&
268 num_previous_succeeded_connections_ == 0) {
269 timer_.Stop();
270 }
205 } 271 }
206 272
207 } // namespace content 273 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698