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

Side by Side Diff: webkit/plugins/ppapi/ppb_websocket_impl.cc

Issue 10944005: Pepper WebSocket API: Implement new design Chrome IPC (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix nits Created 8 years, 2 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 | Annotate | Revision Log
OLDNEW
(Empty)
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.
4
5 #include "webkit/plugins/ppapi/ppb_websocket_impl.h"
6
7 #include <set>
8 #include <string>
9
10 #include "base/basictypes.h"
11 #include "googleurl/src/gurl.h"
12 #include "net/base/net_util.h"
13 #include "ppapi/c/pp_completion_callback.h"
14 #include "ppapi/c/pp_errors.h"
15 #include "ppapi/c/pp_var.h"
16 #include "ppapi/c/ppb_var.h"
17 #include "ppapi/c/ppb_var_array_buffer.h"
18 #include "ppapi/shared_impl/var.h"
19 #include "ppapi/shared_impl/var_tracker.h"
20 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
21 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURL.h"
22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebArrayBuffer.h"
23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
25 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h"
26 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSocket.h"
27 #include "webkit/plugins/ppapi/host_array_buffer_var.h"
28 #include "webkit/plugins/ppapi/host_globals.h"
29 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
30 #include "webkit/plugins/ppapi/resource_helper.h"
31
32 using ppapi::ArrayBufferVar;
33 using ppapi::PpapiGlobals;
34 using ppapi::StringVar;
35 using ppapi::thunk::PPB_WebSocket_API;
36 using ppapi::TrackedCallback;
37 using ppapi::Var;
38 using ppapi::VarTracker;
39 using WebKit::WebArrayBuffer;
40 using WebKit::WebDocument;
41 using WebKit::WebString;
42 using WebKit::WebSocket;
43 using WebKit::WebSocketClient;
44 using WebKit::WebURL;
45
46 const uint32_t kMaxReasonSizeInBytes = 123;
47 const size_t kHybiBaseFramingOverhead = 2;
48 const size_t kHybiMaskingKeyLength = 4;
49 const size_t kMinimumPayloadSizeWithTwoByteExtendedPayloadLength = 126;
50 const size_t kMinimumPayloadSizeWithEightByteExtendedPayloadLength = 0x10000;
51
52 namespace {
53
54 uint64_t SaturateAdd(uint64_t a, uint64_t b) {
55 if (kuint64max - a < b)
56 return kuint64max;
57 return a + b;
58 }
59
60 uint64_t GetFrameSize(uint64_t payload_size) {
61 uint64_t overhead = kHybiBaseFramingOverhead + kHybiMaskingKeyLength;
62 if (payload_size > kMinimumPayloadSizeWithEightByteExtendedPayloadLength)
63 overhead += 8;
64 else if (payload_size > kMinimumPayloadSizeWithTwoByteExtendedPayloadLength)
65 overhead += 2;
66 return SaturateAdd(payload_size, overhead);
67 }
68
69 bool InValidStateToReceive(PP_WebSocketReadyState state) {
70 return state == PP_WEBSOCKETREADYSTATE_OPEN ||
71 state == PP_WEBSOCKETREADYSTATE_CLOSING;
72 }
73
74 } // namespace
75
76 namespace webkit {
77 namespace ppapi {
78
79 PPB_WebSocket_Impl::PPB_WebSocket_Impl(PP_Instance instance)
80 : Resource(::ppapi::OBJECT_IS_IMPL, instance),
81 state_(PP_WEBSOCKETREADYSTATE_INVALID),
82 error_was_received_(false),
83 receive_callback_var_(NULL),
84 wait_for_receive_(false),
85 close_code_(0),
86 close_was_clean_(PP_FALSE),
87 empty_string_(new StringVar("", 0)),
88 buffered_amount_(0),
89 buffered_amount_after_close_(0) {
90 }
91
92 PPB_WebSocket_Impl::~PPB_WebSocket_Impl() {
93 if (websocket_.get())
94 websocket_->disconnect();
95 }
96
97 // static
98 PP_Resource PPB_WebSocket_Impl::Create(PP_Instance instance) {
99 scoped_refptr<PPB_WebSocket_Impl> ws(new PPB_WebSocket_Impl(instance));
100 return ws->GetReference();
101 }
102
103 PPB_WebSocket_API* PPB_WebSocket_Impl::AsPPB_WebSocket_API() {
104 return this;
105 }
106
107 int32_t PPB_WebSocket_Impl::Connect(PP_Var url,
108 const PP_Var protocols[],
109 uint32_t protocol_count,
110 scoped_refptr<TrackedCallback> callback) {
111 // Check mandatory interfaces.
112 PluginInstance* plugin_instance = ResourceHelper::GetPluginInstance(this);
113 DCHECK(plugin_instance);
114 if (!plugin_instance)
115 return PP_ERROR_FAILED;
116
117 // Connect() can be called at most once.
118 if (websocket_.get())
119 return PP_ERROR_INPROGRESS;
120 if (state_ != PP_WEBSOCKETREADYSTATE_INVALID)
121 return PP_ERROR_INPROGRESS;
122 state_ = PP_WEBSOCKETREADYSTATE_CLOSED;
123
124 // Validate url and convert it to WebURL.
125 scoped_refptr<StringVar> url_string = StringVar::FromPPVar(url);
126 if (!url_string)
127 return PP_ERROR_BADARGUMENT;
128 GURL gurl(url_string->value());
129 url_ = new StringVar(gurl.spec());
130 if (!gurl.is_valid())
131 return PP_ERROR_BADARGUMENT;
132 if (!gurl.SchemeIs("ws") && !gurl.SchemeIs("wss"))
133 return PP_ERROR_BADARGUMENT;
134 if (gurl.has_ref())
135 return PP_ERROR_BADARGUMENT;
136 if (!net::IsPortAllowedByDefault(gurl.IntPort()))
137 return PP_ERROR_BADARGUMENT;
138 WebURL web_url(gurl);
139
140 // Validate protocols and convert it to WebString.
141 std::string protocol_string;
142 std::set<std::string> protocol_set;
143 for (uint32_t i = 0; i < protocol_count; i++) {
144 // TODO(toyoshim): Similar function exist in WebKit::WebSocket.
145 // We must rearrange them into WebKit::WebChannel and share its protocol
146 // related implementation via WebKit API.
147 scoped_refptr<StringVar> protocol(StringVar::FromPPVar(protocols[i]));
148
149 // Check invalid and empty entries.
150 if (!protocol || !protocol->value().length())
151 return PP_ERROR_BADARGUMENT;
152
153 // Check duplicated protocol entries.
154 if (protocol_set.find(protocol->value()) != protocol_set.end())
155 return PP_ERROR_BADARGUMENT;
156 protocol_set.insert(protocol->value());
157
158 // Check containing characters.
159 for (std::string::const_iterator it = protocol->value().begin();
160 it != protocol->value().end();
161 ++it) {
162 uint8_t character = *it;
163 // WebSocket specification says "(Subprotocol string must consist of)
164 // characters in the range U+0021 to U+007E not including separator
165 // characters as defined in [RFC2616]."
166 const uint8_t minimumProtocolCharacter = '!'; // U+0021.
167 const uint8_t maximumProtocolCharacter = '~'; // U+007E.
168 if (character < minimumProtocolCharacter ||
169 character > maximumProtocolCharacter ||
170 character == '"' || character == '(' || character == ')' ||
171 character == ',' || character == '/' ||
172 (character >= ':' && character <= '@') || // U+003A - U+0040
173 (character >= '[' && character <= ']') || // U+005B - u+005D
174 character == '{' || character == '}')
175 return PP_ERROR_BADARGUMENT;
176 }
177 // Join protocols with the comma separator.
178 if (i != 0)
179 protocol_string.append(",");
180 protocol_string.append(protocol->value());
181 }
182 WebString web_protocols = WebString::fromUTF8(protocol_string);
183
184 // Create WebKit::WebSocket object and connect.
185 WebDocument document = plugin_instance->container()->element().document();
186 websocket_.reset(WebSocket::create(document, this));
187 DCHECK(websocket_.get());
188 if (!websocket_.get())
189 return PP_ERROR_NOTSUPPORTED;
190
191 // Set receiving binary object type.
192 websocket_->setBinaryType(WebSocket::BinaryTypeArrayBuffer);
193
194 websocket_->connect(web_url, web_protocols);
195 state_ = PP_WEBSOCKETREADYSTATE_CONNECTING;
196
197 // Install callback.
198 connect_callback_ = callback;
199
200 return PP_OK_COMPLETIONPENDING;
201 }
202
203 int32_t PPB_WebSocket_Impl::Close(uint16_t code,
204 PP_Var reason,
205 scoped_refptr<TrackedCallback> callback) {
206 // Check mandarory interfaces.
207 if (!websocket_.get())
208 return PP_ERROR_FAILED;
209
210 // Validate |code| and |reason|.
211 scoped_refptr<StringVar> reason_string;
212 WebString web_reason;
213 WebSocket::CloseEventCode event_code =
214 static_cast<WebSocket::CloseEventCode>(code);
215 if (code == PP_WEBSOCKETSTATUSCODE_NOT_SPECIFIED) {
216 // PP_WEBSOCKETSTATUSCODE_NOT_SPECIFIED and CloseEventCodeNotSpecified are
217 // assigned to different values. A conversion is needed if
218 // PP_WEBSOCKETSTATUSCODE_NOT_SPECIFIED is specified.
219 event_code = WebSocket::CloseEventCodeNotSpecified;
220 } else {
221 if (!(code == PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE ||
222 (PP_WEBSOCKETSTATUSCODE_USER_REGISTERED_MIN <= code &&
223 code <= PP_WEBSOCKETSTATUSCODE_USER_PRIVATE_MAX)))
224 // RFC 6455 limits applications to use reserved connection close code in
225 // section 7.4.2.. The WebSocket API (http://www.w3.org/TR/websockets/)
226 // defines this out of range error as InvalidAccessError in JavaScript.
227 return PP_ERROR_NOACCESS;
228
229 // |reason| must be ignored if it is PP_VARTYPE_UNDEFINED or |code| is
230 // PP_WEBSOCKETSTATUSCODE_NOT_SPECIFIED.
231 if (reason.type != PP_VARTYPE_UNDEFINED) {
232 // Validate |reason|.
233 reason_string = StringVar::FromPPVar(reason);
234 if (!reason_string ||
235 reason_string->value().size() > kMaxReasonSizeInBytes)
236 return PP_ERROR_BADARGUMENT;
237 web_reason = WebString::fromUTF8(reason_string->value());
238 }
239 }
240
241 // Check state.
242 if (state_ == PP_WEBSOCKETREADYSTATE_CLOSING)
243 return PP_ERROR_INPROGRESS;
244 if (state_ == PP_WEBSOCKETREADYSTATE_CLOSED)
245 return PP_OK;
246
247 // Install |callback|.
248 close_callback_ = callback;
249
250 // Abort ongoing connect.
251 if (state_ == PP_WEBSOCKETREADYSTATE_CONNECTING) {
252 state_ = PP_WEBSOCKETREADYSTATE_CLOSING;
253 // Need to do a "Post" to avoid reentering the plugin.
254 connect_callback_->PostAbort();
255 connect_callback_ = NULL;
256 websocket_->fail(
257 "WebSocket was closed before the connection was established.");
258 return PP_OK_COMPLETIONPENDING;
259 }
260
261 // Abort ongoing receive.
262 if (wait_for_receive_) {
263 wait_for_receive_ = false;
264 receive_callback_var_ = NULL;
265
266 // Need to do a "Post" to avoid reentering the plugin.
267 receive_callback_->PostAbort();
268 receive_callback_ = NULL;
269 }
270
271 // Close connection.
272 state_ = PP_WEBSOCKETREADYSTATE_CLOSING;
273 websocket_->close(event_code, web_reason);
274
275 return PP_OK_COMPLETIONPENDING;
276 }
277
278 int32_t PPB_WebSocket_Impl::ReceiveMessage(
279 PP_Var* message,
280 scoped_refptr<TrackedCallback> callback) {
281 // Check state.
282 if (state_ == PP_WEBSOCKETREADYSTATE_INVALID ||
283 state_ == PP_WEBSOCKETREADYSTATE_CONNECTING)
284 return PP_ERROR_BADARGUMENT;
285
286 // Just return received message if any received message is queued.
287 if (!received_messages_.empty()) {
288 receive_callback_var_ = message;
289 return DoReceive();
290 }
291
292 // Check state again. In CLOSED state, no more messages will be received.
293 if (state_ == PP_WEBSOCKETREADYSTATE_CLOSED)
294 return PP_ERROR_BADARGUMENT;
295
296 // Returns PP_ERROR_FAILED after an error is received and received messages
297 // is exhausted.
298 if (error_was_received_)
299 return PP_ERROR_FAILED;
300
301 // Or retain |message| as buffer to store and install |callback|.
302 wait_for_receive_ = true;
303 receive_callback_var_ = message;
304 receive_callback_ = callback;
305
306 return PP_OK_COMPLETIONPENDING;
307 }
308
309 int32_t PPB_WebSocket_Impl::SendMessage(PP_Var message) {
310 // Check mandatory interfaces.
311 if (!websocket_.get())
312 return PP_ERROR_FAILED;
313
314 // Check state.
315 if (state_ == PP_WEBSOCKETREADYSTATE_INVALID ||
316 state_ == PP_WEBSOCKETREADYSTATE_CONNECTING)
317 return PP_ERROR_BADARGUMENT;
318
319 if (state_ == PP_WEBSOCKETREADYSTATE_CLOSING ||
320 state_ == PP_WEBSOCKETREADYSTATE_CLOSED) {
321 // Handle buffered_amount_after_close_.
322 uint64_t payload_size = 0;
323 if (message.type == PP_VARTYPE_STRING) {
324 scoped_refptr<StringVar> message_string = StringVar::FromPPVar(message);
325 if (message_string)
326 payload_size += message_string->value().length();
327 } else if (message.type == PP_VARTYPE_ARRAY_BUFFER) {
328 scoped_refptr<ArrayBufferVar> message_array_buffer =
329 ArrayBufferVar::FromPPVar(message);
330 if (message_array_buffer)
331 payload_size += message_array_buffer->ByteLength();
332 } else {
333 // TODO(toyoshim): Support Blob.
334 return PP_ERROR_NOTSUPPORTED;
335 }
336
337 buffered_amount_after_close_ =
338 SaturateAdd(buffered_amount_after_close_, GetFrameSize(payload_size));
339
340 return PP_ERROR_FAILED;
341 }
342
343 // Send the message.
344 if (message.type == PP_VARTYPE_STRING) {
345 // Convert message to WebString.
346 scoped_refptr<StringVar> message_string = StringVar::FromPPVar(message);
347 if (!message_string)
348 return PP_ERROR_BADARGUMENT;
349 WebString web_message = WebString::fromUTF8(message_string->value());
350 if (!websocket_->sendText(web_message))
351 return PP_ERROR_BADARGUMENT;
352 } else if (message.type == PP_VARTYPE_ARRAY_BUFFER) {
353 // Convert message to WebArrayBuffer.
354 scoped_refptr<HostArrayBufferVar> host_message =
355 static_cast<HostArrayBufferVar*>(ArrayBufferVar::FromPPVar(message));
356 if (!host_message)
357 return PP_ERROR_BADARGUMENT;
358 WebArrayBuffer& web_message = host_message->webkit_buffer();
359 if (!websocket_->sendArrayBuffer(web_message))
360 return PP_ERROR_BADARGUMENT;
361 } else {
362 // TODO(toyoshim): Support Blob.
363 return PP_ERROR_NOTSUPPORTED;
364 }
365
366 return PP_OK;
367 }
368
369 uint64_t PPB_WebSocket_Impl::GetBufferedAmount() {
370 return SaturateAdd(buffered_amount_, buffered_amount_after_close_);
371 }
372
373 uint16_t PPB_WebSocket_Impl::GetCloseCode() {
374 return close_code_;
375 }
376
377 PP_Var PPB_WebSocket_Impl::GetCloseReason() {
378 if (!close_reason_)
379 return empty_string_->GetPPVar();
380 return close_reason_->GetPPVar();
381 }
382
383 PP_Bool PPB_WebSocket_Impl::GetCloseWasClean() {
384 return close_was_clean_;
385 }
386
387 PP_Var PPB_WebSocket_Impl::GetExtensions() {
388 // Check mandatory interfaces.
389 if (!websocket_.get())
390 return empty_string_->GetPPVar();
391
392 std::string extensions = websocket_->extensions().utf8();
393 return StringVar::StringToPPVar(extensions);
394 }
395
396 PP_Var PPB_WebSocket_Impl::GetProtocol() {
397 // Check mandatory interfaces.
398 if (!websocket_.get())
399 return empty_string_->GetPPVar();
400
401 std::string protocol = websocket_->subprotocol().utf8();
402 return StringVar::StringToPPVar(protocol);
403 }
404
405 PP_WebSocketReadyState PPB_WebSocket_Impl::GetReadyState() {
406 return state_;
407 }
408
409 PP_Var PPB_WebSocket_Impl::GetURL() {
410 if (!url_)
411 return empty_string_->GetPPVar();
412 return url_->GetPPVar();
413 }
414
415 void PPB_WebSocket_Impl::didConnect() {
416 DCHECK_EQ(PP_WEBSOCKETREADYSTATE_CONNECTING, state_);
417 state_ = PP_WEBSOCKETREADYSTATE_OPEN;
418 TrackedCallback::ClearAndRun(&connect_callback_, PP_OK);
419 }
420
421 void PPB_WebSocket_Impl::didReceiveMessage(const WebString& message) {
422 // Dispose packets after receiving an error or in invalid state.
423 if (error_was_received_ || !InValidStateToReceive(state_))
424 return;
425
426 // Append received data to queue.
427 std::string string = message.utf8();
428 received_messages_.push(scoped_refptr<Var>(new StringVar(string)));
429
430 if (!wait_for_receive_)
431 return;
432
433 TrackedCallback::ClearAndRun(&receive_callback_, DoReceive());
434 }
435
436 void PPB_WebSocket_Impl::didReceiveArrayBuffer(
437 const WebArrayBuffer& binaryData) {
438 // Dispose packets after receiving an error or in invalid state.
439 if (error_was_received_ || !InValidStateToReceive(state_))
440 return;
441
442 // Append received data to queue.
443 received_messages_.push(
444 scoped_refptr<Var>(new HostArrayBufferVar(binaryData)));
445
446 if (!wait_for_receive_)
447 return;
448
449 TrackedCallback::ClearAndRun(&receive_callback_, DoReceive());
450 }
451
452 void PPB_WebSocket_Impl::didReceiveMessageError() {
453 // Ignore error notification in invalid state.
454 if (!InValidStateToReceive(state_))
455 return;
456
457 // Records the error, then stops receiving any frames after this error.
458 // The error will be notified after all queued messages are read via
459 // ReceiveMessage().
460 error_was_received_ = true;
461 if (!wait_for_receive_)
462 return;
463
464 // But, if no messages are queued and ReceiveMessage() is now on going.
465 // We must invoke the callback with error code here.
466 wait_for_receive_ = false;
467 receive_callback_var_ = NULL;
468 TrackedCallback::ClearAndRun(&receive_callback_, PP_ERROR_FAILED);
469 }
470
471 void PPB_WebSocket_Impl::didUpdateBufferedAmount(
472 unsigned long buffered_amount) {
473 if (state_ == PP_WEBSOCKETREADYSTATE_CLOSED)
474 return;
475 buffered_amount_ = buffered_amount;
476 }
477
478 void PPB_WebSocket_Impl::didStartClosingHandshake() {
479 state_ = PP_WEBSOCKETREADYSTATE_CLOSING;
480 }
481
482 void PPB_WebSocket_Impl::didClose(unsigned long unhandled_buffered_amount,
483 ClosingHandshakeCompletionStatus status,
484 unsigned short code,
485 const WebString& reason) {
486 // Store code and reason.
487 close_code_ = code;
488 close_reason_ = new StringVar(reason.utf8());
489
490 // Set close_was_clean_.
491 bool was_clean =
492 state_ == PP_WEBSOCKETREADYSTATE_CLOSING &&
493 !unhandled_buffered_amount &&
494 status == WebSocketClient::ClosingHandshakeComplete;
495 close_was_clean_ = was_clean ? PP_TRUE : PP_FALSE;
496
497 // Update buffered_amount_.
498 buffered_amount_ = unhandled_buffered_amount;
499
500 // Handle state transition and invoking callback.
501 DCHECK_NE(PP_WEBSOCKETREADYSTATE_CLOSED, state_);
502 PP_WebSocketReadyState state = state_;
503 state_ = PP_WEBSOCKETREADYSTATE_CLOSED;
504
505 // User handlers may release WebSocket PP_Resource in the following
506 // completion callbacks. Retain |this| here to assure that this object
507 // keep on being valid in this function.
508 scoped_refptr<PPB_WebSocket_Impl> retain_this(this);
509 if (state == PP_WEBSOCKETREADYSTATE_CONNECTING)
510 TrackedCallback::ClearAndRun(&connect_callback_, PP_ERROR_FAILED);
511
512 if (wait_for_receive_) {
513 wait_for_receive_ = false;
514 receive_callback_var_ = NULL;
515 TrackedCallback::ClearAndRun(&receive_callback_, PP_ERROR_FAILED);
516 }
517
518 if ((state == PP_WEBSOCKETREADYSTATE_CLOSING) && close_callback_.get())
519 TrackedCallback::ClearAndRun(&close_callback_, PP_OK);
520
521 // Disconnect.
522 if (websocket_.get())
523 websocket_->disconnect();
524 }
525
526 int32_t PPB_WebSocket_Impl::DoReceive() {
527 if (!receive_callback_var_)
528 return PP_OK;
529
530 *receive_callback_var_ = received_messages_.front()->GetPPVar();
531 received_messages_.pop();
532 receive_callback_var_ = NULL;
533 wait_for_receive_ = false;
534 return PP_OK;
535 }
536
537 } // namespace ppapi
538 } // namespace webkit
OLDNEW
« no previous file with comments | « webkit/plugins/ppapi/ppb_websocket_impl.h ('k') | webkit/plugins/ppapi/resource_creation_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698