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

Side by Side Diff: ppapi/proxy/websocket_resource.cc

Issue 10944005: Pepper WebSocket API: Implement new design Chrome IPC (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: (not for review: fix win build) 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 "ppapi/proxy/websocket_resource.h"
6
7 #include <set>
8 #include <vector>
9
10 #include "ppapi/c/pp_errors.h"
11 #include "ppapi/proxy/ppapi_messages.h"
12 #include "ppapi/shared_impl/ppapi_globals.h"
13 #include "ppapi/shared_impl/var.h"
14 #include "ppapi/shared_impl/var_tracker.h"
15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSocket.h"
16
17 namespace {
18
19 const uint32_t kMaxReasonSizeInBytes = 123;
20 const size_t kBaseFramingOverhead = 2;
21 const size_t kMaskingKeyLength = 4;
22 const size_t kMinimumPayloadSizeWithTwoByteExtendedPayloadLength = 126;
23 const size_t kMinimumPayloadSizeWithEightByteExtendedPayloadLength = 0x10000;
24
25 uint64_t SaturateAdd(uint64_t a, uint64_t b) {
26 if (kuint64max - a < b)
27 return kuint64max;
28 return a + b;
29 }
30
31 uint64_t GetFrameSize(uint64_t payload_size) {
32 uint64_t overhead = kBaseFramingOverhead + kMaskingKeyLength;
33 if (payload_size > kMinimumPayloadSizeWithEightByteExtendedPayloadLength)
34 overhead += 8;
35 else if (payload_size > kMinimumPayloadSizeWithTwoByteExtendedPayloadLength)
36 overhead += 2;
37 return SaturateAdd(payload_size, overhead);
38 }
39
40 bool InValidStateToReceive(PP_WebSocketReadyState state) {
41 return state == PP_WEBSOCKETREADYSTATE_OPEN ||
42 state == PP_WEBSOCKETREADYSTATE_CLOSING;
43 }
44
45 } // namespace
46
47
48 namespace ppapi {
49 namespace proxy {
50
51 WebSocketResource::WebSocketResource(Connection connection,
52 PP_Instance instance)
53 : PluginResource(connection, instance),
54 state_(PP_WEBSOCKETREADYSTATE_INVALID),
55 error_was_received_(false),
56 receive_callback_var_(NULL),
57 empty_string_(new StringVar("", 0)),
brettw 2012/10/03 20:30:24 Can you use "new StringVar(std::string())," here i
Takashi Toyoshima 2012/10/05 07:35:01 Done.
58 close_code_(0),
59 close_reason_(NULL),
60 close_was_clean_(PP_FALSE),
61 extensions_(NULL),
62 protocol_(NULL),
63 url_(NULL),
64 buffered_amount_(0),
65 buffered_amount_after_close_(0) {
66 }
67
68 WebSocketResource::~WebSocketResource() {
69 }
70
71 thunk::PPB_WebSocket_API* WebSocketResource::AsPPB_WebSocket_API() {
72 return this;
73 }
74
75 int32_t WebSocketResource::Connect(
76 const PP_Var& url,
77 const PP_Var protocols[],
78 uint32_t protocol_count,
79 scoped_refptr<TrackedCallback> callback) {
80 if (TrackedCallback::IsPending(connect_callback_))
81 return PP_ERROR_INPROGRESS;
82
83 // Connect() can be called at most once.
84 if (state_ != PP_WEBSOCKETREADYSTATE_INVALID)
85 return PP_ERROR_INPROGRESS;
86 state_ = PP_WEBSOCKETREADYSTATE_CLOSED;
87
88 // Get the URL.
89 scoped_refptr<StringVar> url_string = StringVar::FromPPVar(url);
90 if (!url_string)
91 return PP_ERROR_BADARGUMENT;
92
93 // Get the protocols.
94 std::set<std::string> protocol_set;
95 std::vector<std::string> protocol_strings;
96 protocol_strings.reserve(protocol_count);
97 for (uint32_t i = 0; i < protocol_count; ++i) {
98 scoped_refptr<StringVar> protocol(StringVar::FromPPVar(protocols[i]));
99
100 // Check invalid and empty entries.
101 if (!protocol || !protocol->value().length())
102 return PP_ERROR_BADARGUMENT;
103
104 // Check duplicated protocol entries.
105 if (protocol_set.find(protocol->value()) != protocol_set.end())
106 return PP_ERROR_BADARGUMENT;
107 protocol_set.insert(protocol->value());
108
109 protocol_strings.push_back(protocol->value());
110 }
111
112 // Install callback.
113 connect_callback_ = callback;
114
115 // Create remote host in the renderer, then request to check the URL and
116 // establish the connection.
117 state_ = PP_WEBSOCKETREADYSTATE_CONNECTING;
118 SendCreateToRenderer(PpapiHostMsg_WebSocket_Create());
119 CallRenderer(PpapiHostMsg_WebSocket_Connect(url_string->value(),
120 protocol_strings));
121
122 return PP_OK_COMPLETIONPENDING;
123 }
124
125 int32_t WebSocketResource::Close(uint16_t code,
126 const PP_Var& reason,
127 scoped_refptr<TrackedCallback> callback) {
128 if (TrackedCallback::IsPending(close_callback_))
129 return PP_ERROR_INPROGRESS;
130 if (state_ == PP_WEBSOCKETREADYSTATE_INVALID)
131 return PP_ERROR_FAILED;
132
133 // Validate |code| and |reason|.
134 scoped_refptr<StringVar> reason_string_var;
135 std::string reason_string;
136 WebKit::WebSocket::CloseEventCode event_code =
137 static_cast<WebKit::WebSocket::CloseEventCode>(code);
138 if (code == PP_WEBSOCKETSTATUSCODE_NOT_SPECIFIED) {
139 // PP_WEBSOCKETSTATUSCODE_NOT_SPECIFIED and CloseEventCodeNotSpecified are
140 // assigned to different values. A conversion is needed if
141 // PP_WEBSOCKETSTATUSCODE_NOT_SPECIFIED is specified.
142 event_code = WebKit::WebSocket::CloseEventCodeNotSpecified;
143 } else {
144 if (!(code == PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE ||
145 (PP_WEBSOCKETSTATUSCODE_USER_REGISTERED_MIN <= code &&
146 code <= PP_WEBSOCKETSTATUSCODE_USER_PRIVATE_MAX)))
147 // RFC 6455 limits applications to use reserved connection close code in
148 // section 7.4.2.. The WebSocket API (http://www.w3.org/TR/websockets/)
149 // defines this out of range error as InvalidAccessError in JavaScript.
150 return PP_ERROR_NOACCESS;
151
152 // |reason| must be ignored if it is PP_VARTYPE_UNDEFINED or |code| is
153 // PP_WEBSOCKETSTATUSCODE_NOT_SPECIFIED.
154 if (reason.type != PP_VARTYPE_UNDEFINED) {
155 // Validate |reason|.
156 reason_string_var = StringVar::FromPPVar(reason);
157 if (!reason_string_var ||
158 reason_string_var->value().size() > kMaxReasonSizeInBytes)
159 return PP_ERROR_BADARGUMENT;
160 reason_string = reason_string_var->value();
161 }
162 }
163
164 // Check state.
165 if (state_ == PP_WEBSOCKETREADYSTATE_CLOSING)
166 return PP_ERROR_INPROGRESS;
167 if (state_ == PP_WEBSOCKETREADYSTATE_CLOSED)
168 return PP_OK;
169
170 // Install |callback|.
171 close_callback_ = callback;
172
173 // Abort ongoing connect.
174 if (connect_callback_) {
175 state_ = PP_WEBSOCKETREADYSTATE_CLOSING;
176 // Need to do a "Post" to avoid reentering the plugin.
177 connect_callback_->PostAbort();
178 connect_callback_ = NULL;
179 CallRenderer(PpapiHostMsg_WebSocket_Fail(
180 "WebSocket was closed before the connection was established."));
181 return PP_OK_COMPLETIONPENDING;
182 }
183
184 // Abort ongoing receive.
185 if (receive_callback_) {
186 receive_callback_var_ = NULL;
187 // Need to do a "Post" to avoid reentering the plugin.
188 receive_callback_->PostAbort();
189 receive_callback_ = NULL;
190 }
191
192 // Close connection.
193 state_ = PP_WEBSOCKETREADYSTATE_CLOSING;
194 CallRenderer(PpapiHostMsg_WebSocket_Close(static_cast<int32_t>(event_code),
195 reason_string));
196 return PP_OK_COMPLETIONPENDING;
197 }
198
199 int32_t WebSocketResource::ReceiveMessage(
200 PP_Var* message,
201 scoped_refptr<TrackedCallback> callback) {
202 if (TrackedCallback::IsPending(receive_callback_))
203 return PP_ERROR_INPROGRESS;
204
205 // Check state.
206 if (state_ == PP_WEBSOCKETREADYSTATE_INVALID ||
207 state_ == PP_WEBSOCKETREADYSTATE_CONNECTING)
208 return PP_ERROR_BADARGUMENT;
209
210 // Just return received message if any received message is queued.
211 if (!received_messages_.empty()) {
212 receive_callback_var_ = message;
213 return DoReceive();
214 }
215
216 // Check state again. In CLOSED state, no more messages will be received.
217 if (state_ == PP_WEBSOCKETREADYSTATE_CLOSED)
218 return PP_ERROR_BADARGUMENT;
219
220 // Returns PP_ERROR_FAILED after an error is received and received messages
221 // is exhausted.
222 if (error_was_received_)
223 return PP_ERROR_FAILED;
224
225 // Or retain |message| as buffer to store and install |callback|.
226 receive_callback_var_ = message;
227 receive_callback_ = callback;
228
229 return PP_OK_COMPLETIONPENDING;
230 }
231
232 int32_t WebSocketResource::SendMessage(const PP_Var& message) {
233 // Check state.
234 if (state_ == PP_WEBSOCKETREADYSTATE_INVALID ||
235 state_ == PP_WEBSOCKETREADYSTATE_CONNECTING)
236 return PP_ERROR_BADARGUMENT;
237
238 if (state_ == PP_WEBSOCKETREADYSTATE_CLOSING ||
239 state_ == PP_WEBSOCKETREADYSTATE_CLOSED) {
240 // Handle buffered_amount_after_close_.
241 uint64_t payload_size = 0;
242 if (message.type == PP_VARTYPE_STRING) {
243 scoped_refptr<StringVar> message_string = StringVar::FromPPVar(message);
244 if (message_string)
245 payload_size += message_string->value().length();
246 } else if (message.type == PP_VARTYPE_ARRAY_BUFFER) {
247 scoped_refptr<ArrayBufferVar> message_array_buffer =
248 ArrayBufferVar::FromPPVar(message);
249 if (message_array_buffer)
250 payload_size += message_array_buffer->ByteLength();
251 } else {
252 // TODO(toyoshim): Support Blob.
253 return PP_ERROR_NOTSUPPORTED;
254 }
255
256 buffered_amount_after_close_ =
257 SaturateAdd(buffered_amount_after_close_, GetFrameSize(payload_size));
258
259 return PP_ERROR_FAILED;
260 }
261
262 // Send the message.
263 if (message.type == PP_VARTYPE_STRING) {
264 // Convert message to std::string, then send it.
265 scoped_refptr<StringVar> message_string = StringVar::FromPPVar(message);
266 if (!message_string)
267 return PP_ERROR_BADARGUMENT;
268 CallRenderer(PpapiHostMsg_WebSocket_SendText(message_string->value()));
269 } else if (message.type == PP_VARTYPE_ARRAY_BUFFER) {
270 // Convert message to std::vector<uint8_t>, then send it.
271 scoped_refptr<ArrayBufferVar> message_arraybuffer =
272 ArrayBufferVar::FromPPVar(message);
273 if (!message_arraybuffer)
274 return PP_ERROR_BADARGUMENT;
275 uint8_t* message_data = static_cast<uint8_t*>(message_arraybuffer->Map());
276 uint32 message_length = message_arraybuffer->ByteLength();
277 std::vector<uint8_t> message_vector(message_data,
278 message_data + message_length);
279 CallRenderer(PpapiHostMsg_WebSocket_SendBinary(message_vector));
brettw 2012/10/03 20:30:24 For the cases where you don't need a callback, you
Takashi Toyoshima 2012/10/05 07:35:01 Thank you. I misunderstood about params.has_callba
280 } else {
281 // TODO(toyoshim): Support Blob.
282 return PP_ERROR_NOTSUPPORTED;
283 }
284 return PP_OK;
285 }
286
287 uint64_t WebSocketResource::GetBufferedAmount() {
288 return SaturateAdd(buffered_amount_, buffered_amount_after_close_);
289 }
290
291 uint16_t WebSocketResource::GetCloseCode() {
292 return close_code_;
293 }
294
295 PP_Var WebSocketResource::GetCloseReason() {
296 if (!close_reason_)
297 return empty_string_->GetPPVar();
298 return close_reason_->GetPPVar();
299 }
300
301 PP_Bool WebSocketResource::GetCloseWasClean() {
302 return close_was_clean_;
303 }
304
305 PP_Var WebSocketResource::GetExtensions() {
306 return StringVar::StringToPPVar(std::string());
307 }
308
309 PP_Var WebSocketResource::GetProtocol() {
310 if (!protocol_)
311 return empty_string_->GetPPVar();
312 return protocol_->GetPPVar();
313 }
314
315 PP_WebSocketReadyState WebSocketResource::GetReadyState() {
316 return state_;
317 }
318
319 PP_Var WebSocketResource::GetURL() {
320 if (!url_)
321 return empty_string_->GetPPVar();
322 return url_->GetPPVar();
323 }
324
325 void WebSocketResource::OnReplyReceived(
326 const ResourceMessageReplyParams& params,
327 const IPC::Message& msg) {
328 // TODO(toyoshim): DISPATCH_RESOURCE_REPLY will be replaced with other
329 // mechanisms. WebSocket dispatches IPC messages manually for now and must be
330 // replaced with the new one in the future.
331 switch (msg.type()) {
332 case PpapiPluginMsg_WebSocket_ConnectReply::ID: {
333 PpapiPluginMsg_WebSocket_ConnectReply::Schema::Param p;
334 if (PpapiPluginMsg_WebSocket_ConnectReply::Read(&msg, &p))
335 OnPluginMsgConnectReply(params, p.a, p.b);
336 else // On error use default params and run the callback.
337 OnPluginMsgConnectReply(params, std::string(), std::string());
338 break;
339 }
340 case PpapiPluginMsg_WebSocket_CloseReply::ID: {
341 PpapiPluginMsg_WebSocket_CloseReply::Schema::Param p;
342 if (PpapiPluginMsg_WebSocket_CloseReply::Read(&msg, &p))
343 OnPluginMsgCloseReply(params, p.a, p.b, p.c, p.d);
344 else // On error use default params and run the callback.
345 OnPluginMsgCloseReply(params, 0, false, 0, std::string());
346 break;
347 }
348 case PpapiPluginMsg_WebSocket_ReceiveTextReply::ID: {
349 PpapiPluginMsg_WebSocket_ReceiveTextReply::Schema::Param p;
350 if (PpapiPluginMsg_WebSocket_ReceiveTextReply::Read(&msg, &p))
351 OnPluginMsgReceiveTextReply(params, p.a);
352 else
353 NOTREACHED();
354 break;
355 }
356 case PpapiPluginMsg_WebSocket_ReceiveBinaryReply::ID: {
357 PpapiPluginMsg_WebSocket_ReceiveBinaryReply::Schema::Param p;
358 if (PpapiPluginMsg_WebSocket_ReceiveBinaryReply::Read(&msg, &p))
359 OnPluginMsgReceiveBinaryReply(params, p.a);
360 else
361 NOTREACHED();
362 break;
363 }
364 case PpapiPluginMsg_WebSocket_ErrorReply::ID: {
365 OnPluginMsgErrorReply(params);
366 break;
367 }
368 case PpapiPluginMsg_WebSocket_BufferedAmountReply::ID: {
369 PpapiPluginMsg_WebSocket_BufferedAmountReply::Schema::Param p;
370 if (PpapiPluginMsg_WebSocket_BufferedAmountReply::Read(&msg, &p))
371 OnPluginMsgBufferedAmountReply(params, p.a);
372 else
373 NOTREACHED();
374 break;
375 }
376 case PpapiPluginMsg_WebSocket_StateReply::ID: {
377 PpapiPluginMsg_WebSocket_StateReply::Schema::Param p;
378 if (PpapiPluginMsg_WebSocket_StateReply::Read(&msg, &p))
379 OnPluginMsgStateReply(params, p.a);
380 else
381 NOTREACHED();
382 break;
383 }
384 case PpapiPluginMsg_WebSocket_ClosedReply::ID: {
385 PpapiPluginMsg_WebSocket_ClosedReply::Schema::Param p;
386 if (PpapiPluginMsg_WebSocket_ClosedReply::Read(&msg, &p))
387 OnPluginMsgClosedReply(params, p.a, p.b, p.c, p.d);
388 else
389 NOTREACHED();
390 break;
391 }
392 default:
393 NOTREACHED();
394 }
395 }
396
397 void WebSocketResource::OnPluginMsgConnectReply(
398 const ResourceMessageReplyParams& params,
399 const std::string& url,
400 const std::string& protocol) {
401 if (!TrackedCallback::IsPending(connect_callback_))
402 return;
403
404 int32_t result = params.result();
405 if (result == PP_OK) {
406 state_ = PP_WEBSOCKETREADYSTATE_OPEN;
407 protocol_ = new StringVar(protocol);
408 } else {
409 url_ = new StringVar(url);
410 }
411 if (result != PP_OK_COMPLETIONPENDING)
412 TrackedCallback::ClearAndRun(&connect_callback_, params.result());
413 }
414
415 void WebSocketResource::OnPluginMsgCloseReply(
416 const ResourceMessageReplyParams& params,
417 unsigned long buffered_amount,
418 bool was_clean,
419 unsigned short code,
420 const std::string& reason) {
421 // Set close related properties.
422 state_ = PP_WEBSOCKETREADYSTATE_CLOSED;
423 buffered_amount_ = buffered_amount;
424 close_was_clean_ = PP_FromBool(was_clean);
425 close_code_ = code;
426 close_reason_ = new StringVar(reason);
427
428 if (TrackedCallback::IsPending(connect_callback_)) {
429 connect_callback_->PostRun(PP_ERROR_FAILED);
430 }
431
432 if (TrackedCallback::IsPending(receive_callback_)) {
433 receive_callback_var_ = NULL;
434 receive_callback_->PostRun(PP_ERROR_FAILED);
435 }
436
437 if (!TrackedCallback::IsPending(close_callback_))
438 return;
439
440 close_callback_->PostRun(params.result());
441 }
442
443 void WebSocketResource::OnPluginMsgReceiveTextReply(
444 const ResourceMessageReplyParams& params,
445 const std::string& message) {
446 // Dispose packets after receiving an error or in invalid state.
447 if (error_was_received_ || !InValidStateToReceive(state_))
448 return;
449
450 // Append received data to queue.
451 received_messages_.push(scoped_refptr<Var>(new StringVar(message)));
452
453 if (!TrackedCallback::IsPending(receive_callback_))
454 return;
455
456 TrackedCallback::ClearAndRun(&receive_callback_, DoReceive());
457 }
458
459 void WebSocketResource::OnPluginMsgReceiveBinaryReply(
460 const ResourceMessageReplyParams& params,
461 const std::vector<uint8_t>& message) {
462 // Dispose packets after receiving an error or in invalid state.
463 if (error_was_received_ || !InValidStateToReceive(state_))
464 return;
brettw 2012/10/03 20:30:24 Indenting is one space too much. This is also the
Takashi Toyoshima 2012/10/05 07:35:01 Oops. Thank you for catching these nits.
465
466 // Append received data to queue.
467 scoped_refptr<Var> message_var(ArrayBufferVar::FromPPVar(
468 PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
469 message.size(),
470 &message.front())));
471 received_messages_.push(message_var);
472
473 if (!TrackedCallback::IsPending(receive_callback_))
474 return;
475
476 TrackedCallback::ClearAndRun(&receive_callback_, DoReceive());
477 }
478
479 void WebSocketResource::OnPluginMsgErrorReply(
480 const ResourceMessageReplyParams& params) {
481 }
482
483 void WebSocketResource::OnPluginMsgBufferedAmountReply(
484 const ResourceMessageReplyParams& params,
485 unsigned long buffered_amount) {
486 buffered_amount_ = buffered_amount;
487 }
488
489 void WebSocketResource::OnPluginMsgStateReply(
490 const ResourceMessageReplyParams& params,
491 int32_t state) {
492 state_ = static_cast<PP_WebSocketReadyState>(state);
493 }
494
495 void WebSocketResource::OnPluginMsgClosedReply(
496 const ResourceMessageReplyParams& params,
497 unsigned long buffered_amount,
498 bool was_clean,
499 unsigned short code,
500 const std::string& reason) {
501 OnPluginMsgCloseReply(params, buffered_amount, was_clean, code, reason);
502 }
503
504 int32_t WebSocketResource::DoReceive() {
505 if (!receive_callback_var_)
506 return PP_OK;
507
508 *receive_callback_var_ = received_messages_.front()->GetPPVar();
509 received_messages_.pop();
510 receive_callback_var_ = NULL;
511 return PP_OK;
512 }
513
514 } // namespace proxy
515 } // namespace ppapi
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698