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

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

Issue 2119973002: Port WebSockets to Mojo IPC (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix compile error Created 4 years, 4 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "content/browser/renderer_host/websocket_dispatcher_host.h"
6
7 #include <stddef.h>
8
9 #include <algorithm>
10 #include <string>
11 #include <vector>
12
13 #include "base/callback.h"
14 #include "base/logging.h"
15 #include "base/numerics/safe_conversions.h"
16 #include "base/rand_util.h"
17 #include "base/stl_util.h"
18 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
19 #include "content/browser/child_process_security_policy_impl.h"
20 #include "content/browser/renderer_host/websocket_host.h"
21 #include "content/common/websocket_messages.h"
22
23 namespace content {
24
25 namespace {
26
27 // Many methods defined in this file return a WebSocketHostState enum
28 // value. Make WebSocketHostState visible at file scope so it doesn't have to be
29 // fully-qualified every time.
30 typedef WebSocketDispatcherHost::WebSocketHostState WebSocketHostState;
31
32 // Max number of pending connections per WebSocketDispatcherHost
33 // used for per-renderer WebSocket throttling.
34 const int kMaxPendingWebSocketConnections = 255;
35
36 } // namespace
37
38 WebSocketDispatcherHost::WebSocketDispatcherHost(
39 int process_id,
40 const GetRequestContextCallback& get_context_callback,
41 ChromeBlobStorageContext* blob_storage_context,
42 StoragePartition* storage_partition)
43 : BrowserMessageFilter(WebSocketMsgStart),
44 process_id_(process_id),
45 get_context_callback_(get_context_callback),
46 websocket_host_factory_(
47 base::Bind(&WebSocketDispatcherHost::CreateWebSocketHost,
48 base::Unretained(this))),
49 num_pending_connections_(0),
50 num_current_succeeded_connections_(0),
51 num_previous_succeeded_connections_(0),
52 num_current_failed_connections_(0),
53 num_previous_failed_connections_(0),
54 blob_storage_context_(blob_storage_context),
55 storage_partition_(storage_partition) {}
56
57 WebSocketDispatcherHost::WebSocketDispatcherHost(
58 int process_id,
59 const GetRequestContextCallback& get_context_callback,
60 const WebSocketHostFactory& websocket_host_factory)
61 : BrowserMessageFilter(WebSocketMsgStart),
62 process_id_(process_id),
63 get_context_callback_(get_context_callback),
64 websocket_host_factory_(websocket_host_factory),
65 num_pending_connections_(0),
66 num_current_succeeded_connections_(0),
67 num_previous_succeeded_connections_(0),
68 num_current_failed_connections_(0),
69 num_previous_failed_connections_(0),
70 storage_partition_(nullptr) {}
71
72 WebSocketHost* WebSocketDispatcherHost::CreateWebSocketHost(
73 int routing_id,
74 base::TimeDelta delay) {
75 return new WebSocketHost(
76 routing_id, this, get_context_callback_.Run(), delay);
77 }
78
79 bool WebSocketDispatcherHost::OnMessageReceived(const IPC::Message& message) {
80 switch (message.type()) {
81 case WebSocketHostMsg_AddChannelRequest::ID:
82 case WebSocketHostMsg_SendBlob::ID:
83 case WebSocketMsg_SendFrame::ID:
84 case WebSocketMsg_FlowControl::ID:
85 case WebSocketMsg_DropChannel::ID:
86 break;
87
88 default:
89 // Every message that has not been handled by a previous filter passes
90 // through here, so it is good to pass them on as efficiently as possible.
91 return false;
92 }
93
94 int routing_id = message.routing_id();
95 WebSocketHost* host = GetHost(routing_id);
96 if (message.type() == WebSocketHostMsg_AddChannelRequest::ID) {
97 if (host) {
98 DVLOG(1) << "routing_id=" << routing_id << " already in use.";
99 // The websocket multiplexing spec says to should drop the physical
100 // connection in this case, but there isn't a real physical connection
101 // to the renderer, and killing the renderer for this would seem to be a
102 // little extreme. So for now just ignore the bogus request.
103 return true; // We handled the message (by ignoring it).
104 }
105 if (num_pending_connections_ >= kMaxPendingWebSocketConnections) {
106 if (!Send(new WebSocketMsg_NotifyFailure(
107 routing_id,
108 "Error in connection establishment: "
109 "net::ERR_INSUFFICIENT_RESOURCES"))) {
110 DVLOG(1) << "Sending of message type "
111 << "WebSocketMsg_NotifyFailure failed.";
112 }
113 return true;
114 }
115 host = websocket_host_factory_.Run(routing_id, CalculateDelay());
116 hosts_.insert(WebSocketHostTable::value_type(routing_id, host));
117 ++num_pending_connections_;
118 if (!throttling_period_timer_.IsRunning())
119 throttling_period_timer_.Start(
120 FROM_HERE,
121 base::TimeDelta::FromMinutes(2),
122 this,
123 &WebSocketDispatcherHost::ThrottlingPeriodTimerCallback);
124 }
125 if (!host) {
126 DVLOG(1) << "Received invalid routing ID " << routing_id
127 << " from renderer.";
128 return true; // We handled the message (by ignoring it).
129 }
130 return host->OnMessageReceived(message);
131 }
132
133 bool WebSocketDispatcherHost::CanReadRawCookies() const {
134 ChildProcessSecurityPolicyImpl* policy =
135 ChildProcessSecurityPolicyImpl::GetInstance();
136 return policy->CanReadRawCookies(process_id_);
137 }
138
139 storage::BlobStorageContext* WebSocketDispatcherHost::blob_storage_context()
140 const {
141 DCHECK(blob_storage_context_);
142 return blob_storage_context_->context();
143 }
144
145 WebSocketHost* WebSocketDispatcherHost::GetHost(int routing_id) const {
146 WebSocketHostTable::const_iterator it = hosts_.find(routing_id);
147 return it == hosts_.end() ? NULL : it->second;
148 }
149
150 WebSocketHostState WebSocketDispatcherHost::SendOrDrop(IPC::Message* message) {
151 const uint32_t message_type = message->type();
152 const int32_t message_routing_id = message->routing_id();
153 if (!Send(message)) {
154 message = NULL;
155 DVLOG(1) << "Sending of message type " << message_type
156 << " failed. Dropping channel.";
157 DeleteWebSocketHost(message_routing_id);
158 return WEBSOCKET_HOST_DELETED;
159 }
160 return WEBSOCKET_HOST_ALIVE;
161 }
162
163 WebSocketHostState WebSocketDispatcherHost::SendAddChannelResponse(
164 int routing_id,
165 const std::string& selected_protocol,
166 const std::string& extensions) {
167 // Update throttling counters (success).
168 WebSocketHost* host = GetHost(routing_id);
169 DCHECK(host);
170 host->OnHandshakeSucceeded();
171 --num_pending_connections_;
172 DCHECK_GE(num_pending_connections_, 0);
173 ++num_current_succeeded_connections_;
174
175 return SendOrDrop(new WebSocketMsg_AddChannelResponse(
176 routing_id, selected_protocol, extensions));
177 }
178
179 WebSocketHostState WebSocketDispatcherHost::SendFrame(
180 int routing_id,
181 bool fin,
182 WebSocketMessageType type,
183 const std::vector<char>& data) {
184 return SendOrDrop(new WebSocketMsg_SendFrame(routing_id, fin, type, data));
185 }
186
187 WebSocketHostState WebSocketDispatcherHost::SendFlowControl(int routing_id,
188 int64_t quota) {
189 return SendOrDrop(new WebSocketMsg_FlowControl(routing_id, quota));
190 }
191
192 WebSocketHostState WebSocketDispatcherHost::NotifyClosingHandshake(
193 int routing_id) {
194 return SendOrDrop(new WebSocketMsg_NotifyClosing(routing_id));
195 }
196
197 WebSocketHostState WebSocketDispatcherHost::NotifyStartOpeningHandshake(
198 int routing_id, const WebSocketHandshakeRequest& request) {
199 return SendOrDrop(new WebSocketMsg_NotifyStartOpeningHandshake(
200 routing_id, request));
201 }
202
203 WebSocketHostState WebSocketDispatcherHost::NotifyFinishOpeningHandshake(
204 int routing_id, const WebSocketHandshakeResponse& response) {
205 return SendOrDrop(new WebSocketMsg_NotifyFinishOpeningHandshake(
206 routing_id, response));
207 }
208
209 WebSocketHostState WebSocketDispatcherHost::NotifyFailure(
210 int routing_id,
211 const std::string& message) {
212 if (SendOrDrop(new WebSocketMsg_NotifyFailure(
213 routing_id, message)) == WEBSOCKET_HOST_DELETED) {
214 return WEBSOCKET_HOST_DELETED;
215 }
216 DeleteWebSocketHost(routing_id);
217 return WEBSOCKET_HOST_DELETED;
218 }
219
220 WebSocketHostState WebSocketDispatcherHost::BlobSendComplete(int routing_id) {
221 return SendOrDrop(new WebSocketMsg_BlobSendComplete(routing_id));
222 }
223
224 WebSocketHostState WebSocketDispatcherHost::DoDropChannel(
225 int routing_id,
226 bool was_clean,
227 uint16_t code,
228 const std::string& reason) {
229 if (SendOrDrop(
230 new WebSocketMsg_DropChannel(routing_id, was_clean, code, reason)) ==
231 WEBSOCKET_HOST_DELETED)
232 return WEBSOCKET_HOST_DELETED;
233 DeleteWebSocketHost(routing_id);
234 return WEBSOCKET_HOST_DELETED;
235 }
236
237 WebSocketDispatcherHost::~WebSocketDispatcherHost() {
238 std::vector<WebSocketHost*> hosts;
239 for (base::hash_map<int, WebSocketHost*>::const_iterator i = hosts_.begin();
240 i != hosts_.end(); ++i) {
241 // In order to avoid changing the container while iterating, we copy
242 // the hosts.
243 hosts.push_back(i->second);
244 }
245
246 for (size_t i = 0; i < hosts.size(); ++i) {
247 // Note that some calls to GoAway could fail. In that case hosts[i] will be
248 // deleted and removed from |hosts_| in |DoDropChannel|.
249 hosts[i]->GoAway();
250 hosts[i] = NULL;
251 }
252
253 STLDeleteContainerPairSecondPointers(hosts_.begin(), hosts_.end());
254 }
255
256 void WebSocketDispatcherHost::DeleteWebSocketHost(int routing_id) {
257 WebSocketHostTable::iterator it = hosts_.find(routing_id);
258 DCHECK(it != hosts_.end());
259 DCHECK(it->second);
260 if (!it->second->handshake_succeeded()) {
261 // Update throttling counters (failure).
262 --num_pending_connections_;
263 DCHECK_GE(num_pending_connections_, 0);
264 ++num_current_failed_connections_;
265 }
266
267 delete it->second;
268 hosts_.erase(it);
269
270 DCHECK_LE(base::checked_cast<size_t>(num_pending_connections_),
271 hosts_.size());
272 }
273
274 int64_t WebSocketDispatcherHost::num_failed_connections() const {
275 return num_previous_failed_connections_ +
276 num_current_failed_connections_;
277 }
278
279 int64_t WebSocketDispatcherHost::num_succeeded_connections() const {
280 return num_previous_succeeded_connections_ +
281 num_current_succeeded_connections_;
282 }
283
284 // Calculate delay as described in
285 // the per-renderer WebSocket throttling design doc:
286 // https://docs.google.com/document/d/1aw2oN5PKfk-1gLnBrlv1OwLA8K3-ykM2ckwX2lubT g4/edit?usp=sharing
287 base::TimeDelta WebSocketDispatcherHost::CalculateDelay() const {
288 int64_t f = num_failed_connections();
289 int64_t s = num_succeeded_connections();
290 int p = num_pending_connections();
291 return base::TimeDelta::FromMilliseconds(
292 base::RandInt(1000, 5000) *
293 (1 << std::min(p + f / (s + 1), INT64_C(16))) / 65536);
294 }
295
296 void WebSocketDispatcherHost::ThrottlingPeriodTimerCallback() {
297 num_previous_failed_connections_ = num_current_failed_connections_;
298 num_current_failed_connections_ = 0;
299
300 num_previous_succeeded_connections_ = num_current_succeeded_connections_;
301 num_current_succeeded_connections_ = 0;
302
303 if (num_pending_connections_ == 0 &&
304 num_previous_failed_connections_ == 0 &&
305 num_previous_succeeded_connections_ == 0) {
306 throttling_period_timer_.Stop();
307 }
308 }
309
310 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698