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

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

Powered by Google App Engine
This is Rietveld 408576698