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

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: Add a comment / blank lines. 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/numerics/safe_conversions.h"
13 #include "base/rand_util.h"
12 #include "base/stl_util.h" 14 #include "base/stl_util.h"
13 #include "content/browser/child_process_security_policy_impl.h" 15 #include "content/browser/child_process_security_policy_impl.h"
14 #include "content/browser/renderer_host/websocket_host.h" 16 #include "content/browser/renderer_host/websocket_host.h"
15 #include "content/common/websocket_messages.h" 17 #include "content/common/websocket_messages.h"
16 18
17 namespace content { 19 namespace content {
18 20
19 namespace { 21 namespace {
20 22
21 // Many methods defined in this file return a WebSocketHostState enum 23 // 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 24 // value. Make WebSocketHostState visible at file scope so it doesn't have to be
23 // fully-qualified every time. 25 // fully-qualified every time.
24 typedef WebSocketDispatcherHost::WebSocketHostState WebSocketHostState; 26 typedef WebSocketDispatcherHost::WebSocketHostState WebSocketHostState;
25 27
28 // Max number of pending connections per WebSocketDispatcherHost
29 // used for per-renderer WebSocket throttling.
30 const int kMaxPendingWebSocketConnections = 255;
31
26 } // namespace 32 } // namespace
27 33
28 WebSocketDispatcherHost::WebSocketDispatcherHost( 34 WebSocketDispatcherHost::WebSocketDispatcherHost(
29 int process_id, 35 int process_id,
30 const GetRequestContextCallback& get_context_callback) 36 const GetRequestContextCallback& get_context_callback)
31 : BrowserMessageFilter(WebSocketMsgStart), 37 : BrowserMessageFilter(WebSocketMsgStart),
32 process_id_(process_id), 38 process_id_(process_id),
33 get_context_callback_(get_context_callback), 39 get_context_callback_(get_context_callback),
34 websocket_host_factory_( 40 websocket_host_factory_(
35 base::Bind(&WebSocketDispatcherHost::CreateWebSocketHost, 41 base::Bind(&WebSocketDispatcherHost::CreateWebSocketHost,
36 base::Unretained(this))) {} 42 base::Unretained(this))),
43 num_pending_connections_(0),
44 num_current_succeeded_connections_(0),
45 num_previous_succeeded_connections_(0),
46 num_current_failed_connections_(0),
47 num_previous_failed_connections_(0) {}
37 48
38 WebSocketDispatcherHost::WebSocketDispatcherHost( 49 WebSocketDispatcherHost::WebSocketDispatcherHost(
39 int process_id, 50 int process_id,
40 const GetRequestContextCallback& get_context_callback, 51 const GetRequestContextCallback& get_context_callback,
41 const WebSocketHostFactory& websocket_host_factory) 52 const WebSocketHostFactory& websocket_host_factory)
42 : BrowserMessageFilter(WebSocketMsgStart), 53 : BrowserMessageFilter(WebSocketMsgStart),
43 process_id_(process_id), 54 process_id_(process_id),
44 get_context_callback_(get_context_callback), 55 get_context_callback_(get_context_callback),
45 websocket_host_factory_(websocket_host_factory) {} 56 websocket_host_factory_(websocket_host_factory),
57 num_pending_connections_(0),
58 num_current_succeeded_connections_(0),
59 num_previous_succeeded_connections_(0),
60 num_current_failed_connections_(0),
61 num_previous_failed_connections_(0) {}
46 62
47 WebSocketHost* WebSocketDispatcherHost::CreateWebSocketHost(int routing_id) { 63 WebSocketHost* WebSocketDispatcherHost::CreateWebSocketHost(
48 return new WebSocketHost(routing_id, this, get_context_callback_.Run()); 64 int routing_id,
65 base::TimeDelta delay) {
66 return new WebSocketHost(
67 routing_id, this, get_context_callback_.Run(), delay);
49 } 68 }
50 69
51 bool WebSocketDispatcherHost::OnMessageReceived(const IPC::Message& message) { 70 bool WebSocketDispatcherHost::OnMessageReceived(const IPC::Message& message) {
52 switch (message.type()) { 71 switch (message.type()) {
53 case WebSocketHostMsg_AddChannelRequest::ID: 72 case WebSocketHostMsg_AddChannelRequest::ID:
54 case WebSocketMsg_SendFrame::ID: 73 case WebSocketMsg_SendFrame::ID:
55 case WebSocketMsg_FlowControl::ID: 74 case WebSocketMsg_FlowControl::ID:
56 case WebSocketMsg_DropChannel::ID: 75 case WebSocketMsg_DropChannel::ID:
57 break; 76 break;
58 77
59 default: 78 default:
60 // Every message that has not been handled by a previous filter passes 79 // 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. 80 // through here, so it is good to pass them on as efficiently as possible.
62 return false; 81 return false;
63 } 82 }
64 83
65 int routing_id = message.routing_id(); 84 int routing_id = message.routing_id();
66 WebSocketHost* host = GetHost(routing_id); 85 WebSocketHost* host = GetHost(routing_id);
67 if (message.type() == WebSocketHostMsg_AddChannelRequest::ID) { 86 if (message.type() == WebSocketHostMsg_AddChannelRequest::ID) {
68 if (host) { 87 if (host) {
69 DVLOG(1) << "routing_id=" << routing_id << " already in use."; 88 DVLOG(1) << "routing_id=" << routing_id << " already in use.";
70 // The websocket multiplexing spec says to should drop the physical 89 // The websocket multiplexing spec says to should drop the physical
71 // connection in this case, but there isn't a real physical connection 90 // 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 91 // 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. 92 // little extreme. So for now just ignore the bogus request.
74 return true; // We handled the message (by ignoring it). 93 return true; // We handled the message (by ignoring it).
75 } 94 }
76 host = websocket_host_factory_.Run(routing_id); 95 if (num_pending_connections_ >= kMaxPendingWebSocketConnections) {
96 if(!Send(new WebSocketMsg_NotifyFailure(routing_id,
97 "Error in connection establishment: net::ERR_INSUFFICIENT_RESOURCES"
98 ))) {
99 DVLOG(1) << "Sending of message type "
100 << "WebSocketMsg_NotifyFailure failed.";
101 }
102 return true;
103 }
104 host = websocket_host_factory_.Run(routing_id, CalculateDelay());
77 hosts_.insert(WebSocketHostTable::value_type(routing_id, host)); 105 hosts_.insert(WebSocketHostTable::value_type(routing_id, host));
106 ++num_pending_connections_;
107 if (!throttling_period_timer_.IsRunning())
108 throttling_period_timer_.Start(
109 FROM_HERE,
110 base::TimeDelta::FromMinutes(2),
111 this,
112 &WebSocketDispatcherHost::ThrottlingPeriodTimerCallback);
78 } 113 }
79 if (!host) { 114 if (!host) {
80 DVLOG(1) << "Received invalid routing ID " << routing_id 115 DVLOG(1) << "Received invalid routing ID " << routing_id
81 << " from renderer."; 116 << " from renderer.";
82 return true; // We handled the message (by ignoring it). 117 return true; // We handled the message (by ignoring it).
83 } 118 }
84 return host->OnMessageReceived(message); 119 return host->OnMessageReceived(message);
85 } 120 }
86 121
87 bool WebSocketDispatcherHost::CanReadRawCookies() const { 122 bool WebSocketDispatcherHost::CanReadRawCookies() const {
(...skipping 17 matching lines...) Expand all
105 DeleteWebSocketHost(message_routing_id); 140 DeleteWebSocketHost(message_routing_id);
106 return WEBSOCKET_HOST_DELETED; 141 return WEBSOCKET_HOST_DELETED;
107 } 142 }
108 return WEBSOCKET_HOST_ALIVE; 143 return WEBSOCKET_HOST_ALIVE;
109 } 144 }
110 145
111 WebSocketHostState WebSocketDispatcherHost::SendAddChannelResponse( 146 WebSocketHostState WebSocketDispatcherHost::SendAddChannelResponse(
112 int routing_id, 147 int routing_id,
113 const std::string& selected_protocol, 148 const std::string& selected_protocol,
114 const std::string& extensions) { 149 const std::string& extensions) {
150 // Update throttling counters (success).
151 WebSocketHost* host = GetHost(routing_id);
152 DCHECK(host);
153 host->OnHandshakeSucceeded();
154 --num_pending_connections_;
155 DCHECK_GE(num_pending_connections_, 0);
156 ++num_current_succeeded_connections_;
157
115 return SendOrDrop(new WebSocketMsg_AddChannelResponse( 158 return SendOrDrop(new WebSocketMsg_AddChannelResponse(
116 routing_id, selected_protocol, extensions)); 159 routing_id, selected_protocol, extensions));
117 } 160 }
118 161
119 WebSocketHostState WebSocketDispatcherHost::SendFrame( 162 WebSocketHostState WebSocketDispatcherHost::SendFrame(
120 int routing_id, 163 int routing_id,
121 bool fin, 164 bool fin,
122 WebSocketMessageType type, 165 WebSocketMessageType type,
123 const std::vector<char>& data) { 166 const std::vector<char>& data) {
124 return SendOrDrop(new WebSocketMsg_SendFrame(routing_id, fin, type, data)); 167 return SendOrDrop(new WebSocketMsg_SendFrame(routing_id, fin, type, data));
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
185 hosts[i]->GoAway(); 228 hosts[i]->GoAway();
186 hosts[i] = NULL; 229 hosts[i] = NULL;
187 } 230 }
188 231
189 STLDeleteContainerPairSecondPointers(hosts_.begin(), hosts_.end()); 232 STLDeleteContainerPairSecondPointers(hosts_.begin(), hosts_.end());
190 } 233 }
191 234
192 void WebSocketDispatcherHost::DeleteWebSocketHost(int routing_id) { 235 void WebSocketDispatcherHost::DeleteWebSocketHost(int routing_id) {
193 WebSocketHostTable::iterator it = hosts_.find(routing_id); 236 WebSocketHostTable::iterator it = hosts_.find(routing_id);
194 DCHECK(it != hosts_.end()); 237 DCHECK(it != hosts_.end());
238 DCHECK(it->second);
239 if (!it->second->handshake_succeeded()) {
240 // Update throttling counters (failure).
241 --num_pending_connections_;
242 DCHECK_GE(num_pending_connections_, 0);
243 ++num_current_failed_connections_;
244 }
245
195 delete it->second; 246 delete it->second;
196 hosts_.erase(it); 247 hosts_.erase(it);
248
249 DCHECK_LE(base::checked_cast<size_t>(num_pending_connections_),
250 hosts_.size());
251 }
252
253 int64_t WebSocketDispatcherHost::num_failed_connections() const {
254 return num_previous_failed_connections_ +
255 num_current_failed_connections_;
256 }
257
258 int64_t WebSocketDispatcherHost::num_succeeded_connections() const {
259 return num_previous_succeeded_connections_ +
260 num_current_succeeded_connections_;
261 }
262
263 // Calculate delay as described in
264 // the per-renderer WebSocket throttling design doc:
265 // https://docs.google.com/document/d/1aw2oN5PKfk-1gLnBrlv1OwLA8K3-ykM2ckwX2lubT g4/edit?usp=sharing
266 base::TimeDelta WebSocketDispatcherHost::CalculateDelay() const {
267 int64_t f = num_failed_connections();
268 int64_t s = num_succeeded_connections();
269 int p = num_pending_connections();
270 return base::TimeDelta::FromMilliseconds(
271 base::RandInt(1000, 5000) *
272 (1 << std::min(p + f / (s + 1), INT64_C(16))) / 65536);
273 }
274
275 void WebSocketDispatcherHost::ThrottlingPeriodTimerCallback() {
276 num_previous_failed_connections_ = num_current_failed_connections_;
277 num_current_failed_connections_ = 0;
278
279 num_previous_succeeded_connections_ = num_current_succeeded_connections_;
280 num_current_succeeded_connections_ = 0;
281
282 if (num_pending_connections_ == 0 &&
283 num_previous_failed_connections_ == 0 &&
284 num_previous_succeeded_connections_ == 0) {
285 throttling_period_timer_.Stop();
286 }
197 } 287 }
198 288
199 } // namespace content 289 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698