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

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: Fix content_unittests 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
27 // Max of pending connections per WebSocketDispatcherHost
Adam Rice 2015/03/04 08:20:50 Nitpick: "Max number of"
hiroshige 2015/03/04 11:13:15 Done.
28 // used for per-renderer WebSocket throttling.
29 const int kMaxPendingWebSocketConnections = 255;
30
26 } // namespace 31 } // namespace
27 32
28 WebSocketDispatcherHost::WebSocketDispatcherHost( 33 WebSocketDispatcherHost::WebSocketDispatcherHost(
29 int process_id, 34 int process_id,
30 const GetRequestContextCallback& get_context_callback) 35 const GetRequestContextCallback& get_context_callback)
31 : BrowserMessageFilter(WebSocketMsgStart), 36 : BrowserMessageFilter(WebSocketMsgStart),
32 process_id_(process_id), 37 process_id_(process_id),
33 get_context_callback_(get_context_callback), 38 get_context_callback_(get_context_callback),
34 websocket_host_factory_( 39 websocket_host_factory_(
35 base::Bind(&WebSocketDispatcherHost::CreateWebSocketHost, 40 base::Bind(&WebSocketDispatcherHost::CreateWebSocketHost,
36 base::Unretained(this))) {} 41 base::Unretained(this))),
42 num_pending_connections_(0),
43 num_current_succeeded_connections_(0),
44 num_previous_succeeded_connections_(0),
45 num_current_failed_connections_(0),
46 num_previous_failed_connections_(0) {}
37 47
38 WebSocketDispatcherHost::WebSocketDispatcherHost( 48 WebSocketDispatcherHost::WebSocketDispatcherHost(
39 int process_id, 49 int process_id,
40 const GetRequestContextCallback& get_context_callback, 50 const GetRequestContextCallback& get_context_callback,
41 const WebSocketHostFactory& websocket_host_factory) 51 const WebSocketHostFactory& websocket_host_factory)
42 : BrowserMessageFilter(WebSocketMsgStart), 52 : BrowserMessageFilter(WebSocketMsgStart),
43 process_id_(process_id), 53 process_id_(process_id),
44 get_context_callback_(get_context_callback), 54 get_context_callback_(get_context_callback),
45 websocket_host_factory_(websocket_host_factory) {} 55 websocket_host_factory_(websocket_host_factory) {}
46 56
47 WebSocketHost* WebSocketDispatcherHost::CreateWebSocketHost(int routing_id) { 57 WebSocketHost* WebSocketDispatcherHost::CreateWebSocketHost(
48 return new WebSocketHost(routing_id, this, get_context_callback_.Run()); 58 int routing_id,
59 base::TimeDelta delay) {
60 return new WebSocketHost(
61 routing_id, this, get_context_callback_.Run(), delay);
49 } 62 }
50 63
51 bool WebSocketDispatcherHost::OnMessageReceived(const IPC::Message& message) { 64 bool WebSocketDispatcherHost::OnMessageReceived(const IPC::Message& message) {
52 switch (message.type()) { 65 switch (message.type()) {
53 case WebSocketHostMsg_AddChannelRequest::ID: 66 case WebSocketHostMsg_AddChannelRequest::ID:
54 case WebSocketMsg_SendFrame::ID: 67 case WebSocketMsg_SendFrame::ID:
55 case WebSocketMsg_FlowControl::ID: 68 case WebSocketMsg_FlowControl::ID:
56 case WebSocketMsg_DropChannel::ID: 69 case WebSocketMsg_DropChannel::ID:
57 break; 70 break;
58 71
59 default: 72 default:
60 // Every message that has not been handled by a previous filter passes 73 // 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. 74 // through here, so it is good to pass them on as efficiently as possible.
62 return false; 75 return false;
63 } 76 }
64 77
65 int routing_id = message.routing_id(); 78 int routing_id = message.routing_id();
66 WebSocketHost* host = GetHost(routing_id); 79 WebSocketHost* host = GetHost(routing_id);
67 if (message.type() == WebSocketHostMsg_AddChannelRequest::ID) { 80 if (message.type() == WebSocketHostMsg_AddChannelRequest::ID) {
68 if (host) { 81 if (host) {
69 DVLOG(1) << "routing_id=" << routing_id << " already in use."; 82 DVLOG(1) << "routing_id=" << routing_id << " already in use.";
70 // The websocket multiplexing spec says to should drop the physical 83 // The websocket multiplexing spec says to should drop the physical
71 // connection in this case, but there isn't a real physical connection 84 // 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 85 // 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. 86 // little extreme. So for now just ignore the bogus request.
74 return true; // We handled the message (by ignoring it). 87 return true; // We handled the message (by ignoring it).
75 } 88 }
76 host = websocket_host_factory_.Run(routing_id); 89 if (num_pending_connections_ > kMaxPendingWebSocketConnections) {
90 if(!Send(new WebSocketMsg_NotifyFailure(routing_id,
91 "Error in connection establishment: net::ERR_INSUFFICIENT_RESOURCES"
92 ))) {
93 DVLOG(1) << "Sending of message type "
94 << "WebSocketMsg_NotifyFailure failed.";
95 }
96 return true;
97 }
98 host = websocket_host_factory_.Run(routing_id, CalculateDelay());
77 hosts_.insert(WebSocketHostTable::value_type(routing_id, host)); 99 hosts_.insert(WebSocketHostTable::value_type(routing_id, host));
100 ++num_pending_connections_;
101 if (!timer_.IsRunning())
102 timer_.Start(FROM_HERE,
103 base::TimeDelta::FromMinutes(2),
104 this,
105 &WebSocketDispatcherHost::FinishThrottlingPeriod);
78 } 106 }
79 if (!host) { 107 if (!host) {
80 DVLOG(1) << "Received invalid routing ID " << routing_id 108 DVLOG(1) << "Received invalid routing ID " << routing_id
81 << " from renderer."; 109 << " from renderer.";
82 return true; // We handled the message (by ignoring it). 110 return true; // We handled the message (by ignoring it).
83 } 111 }
84 return host->OnMessageReceived(message); 112 return host->OnMessageReceived(message);
85 } 113 }
86 114
87 bool WebSocketDispatcherHost::CanReadRawCookies() const { 115 bool WebSocketDispatcherHost::CanReadRawCookies() const {
(...skipping 26 matching lines...) Expand all
114 const std::string& selected_protocol, 142 const std::string& selected_protocol,
115 const std::string& extensions) { 143 const std::string& extensions) {
116 if (SendOrDrop(new WebSocketMsg_AddChannelResponse( 144 if (SendOrDrop(new WebSocketMsg_AddChannelResponse(
117 routing_id, fail, selected_protocol, extensions)) == 145 routing_id, fail, selected_protocol, extensions)) ==
118 WEBSOCKET_HOST_DELETED) 146 WEBSOCKET_HOST_DELETED)
119 return WEBSOCKET_HOST_DELETED; 147 return WEBSOCKET_HOST_DELETED;
120 if (fail) { 148 if (fail) {
121 DeleteWebSocketHost(routing_id); 149 DeleteWebSocketHost(routing_id);
122 return WEBSOCKET_HOST_DELETED; 150 return WEBSOCKET_HOST_DELETED;
123 } 151 }
152
153 // Update throttling counters (success).
154 WebSocketHost* host = GetHost(routing_id);
155 DCHECK(host);
156 host->OnHandshakeSucceeded();
157 --num_pending_connections_;
158 DCHECK_GE(num_pending_connections_, 0);
159 ++num_current_succeeded_connections_;
160 DCHECK_LT(num_current_succeeded_connections_, kint64max / 2);
Adam Rice 2015/03/04 08:20:50 I don't think the "/ 2" is doing anything useful h
hiroshige 2015/03/04 11:13:15 I added "/ 2" here because this variable is used l
Adam Rice 2015/03/05 07:48:27 Yes, just remove it. Even if there is a bug in the
hiroshige 2015/03/06 06:04:29 Done.
161
124 return WEBSOCKET_HOST_ALIVE; 162 return WEBSOCKET_HOST_ALIVE;
125 } 163 }
126 164
127 WebSocketHostState WebSocketDispatcherHost::SendFrame( 165 WebSocketHostState WebSocketDispatcherHost::SendFrame(
128 int routing_id, 166 int routing_id,
129 bool fin, 167 bool fin,
130 WebSocketMessageType type, 168 WebSocketMessageType type,
131 const std::vector<char>& data) { 169 const std::vector<char>& data) {
132 return SendOrDrop(new WebSocketMsg_SendFrame(routing_id, fin, type, data)); 170 return SendOrDrop(new WebSocketMsg_SendFrame(routing_id, fin, type, data));
133 } 171 }
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
193 hosts[i]->GoAway(); 231 hosts[i]->GoAway();
194 hosts[i] = NULL; 232 hosts[i] = NULL;
195 } 233 }
196 234
197 STLDeleteContainerPairSecondPointers(hosts_.begin(), hosts_.end()); 235 STLDeleteContainerPairSecondPointers(hosts_.begin(), hosts_.end());
198 } 236 }
199 237
200 void WebSocketDispatcherHost::DeleteWebSocketHost(int routing_id) { 238 void WebSocketDispatcherHost::DeleteWebSocketHost(int routing_id) {
201 WebSocketHostTable::iterator it = hosts_.find(routing_id); 239 WebSocketHostTable::iterator it = hosts_.find(routing_id);
202 DCHECK(it != hosts_.end()); 240 DCHECK(it != hosts_.end());
241 DCHECK(it->second);
242 if (!it->second->handshake_succeeded()) {
243 // Update throttling counters (failure).
244 --num_pending_connections_;
245 DCHECK_GE(num_pending_connections_, 0);
246 ++num_current_failed_connections_;
247 DCHECK_LT(num_current_failed_connections_, kint64max / 2);
248 }
249
203 delete it->second; 250 delete it->second;
204 hosts_.erase(it); 251 hosts_.erase(it);
252
253 DCHECK(static_cast<size_t>(num_pending_connections_) <= hosts_.size());
Adam Rice 2015/03/04 08:20:49 DCHECK_LE. Also, you should use checked_cast<> fr
hiroshige 2015/03/04 11:13:15 Done.
254 }
255
256 // Calculate delay according to
Adam Rice 2015/03/04 08:20:49 I think "as described in" sounds nicer than "accor
hiroshige 2015/03/04 11:13:15 Done.
257 // the per-renderer WebSocket throttling design doc:
258 // https://docs.google.com/document/d/1aw2oN5PKfk-1gLnBrlv1OwLA8K3-ykM2ckwX2lubT g4/edit?usp=sharing
259 base::TimeDelta WebSocketDispatcherHost::CalculateDelay() const {
260 int64_t f = num_previous_failed_connections_ +
261 num_current_failed_connections_;
262 int64_t s = num_previous_succeeded_connections_ +
263 num_current_succeeded_connections_;
264 int p = num_pending_connections_;
265 return base::TimeDelta::FromMilliseconds(
266 base::RandInt(1000, 5000) *
267 (1 << std::min(p + f / (s + 1), static_cast<int64_t>(16))) / 65536);
Adam Rice 2015/03/04 08:20:49 I think it is probably clearer to write 16LL rathe
hiroshige 2015/03/04 11:13:14 Sadly 16LL doesn't pass compilation; std::min requ
Adam Rice 2015/03/05 07:48:27 Sorry. I just remembered how to do this properly.
hiroshige 2015/03/06 06:04:29 Done.
268 }
269
270 void WebSocketDispatcherHost::FinishThrottlingPeriod() {
271 num_previous_failed_connections_ = num_current_failed_connections_;
272 num_current_failed_connections_ = 0;
273
274 num_previous_succeeded_connections_ = num_current_succeeded_connections_;
275 num_current_succeeded_connections_ = 0;
276
277 if (num_pending_connections_ == 0 &&
278 num_previous_failed_connections_ == 0 &&
279 num_previous_succeeded_connections_ == 0) {
280 timer_.Stop();
281 }
205 } 282 }
206 283
207 } // namespace content 284 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698