• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "content/renderer/pepper/pepper_websocket_host.h"
6 
7 #include <string>
8 
9 #include "content/public/renderer/renderer_ppapi_host.h"
10 #include "net/base/net_util.h"
11 #include "ppapi/c/pp_errors.h"
12 #include "ppapi/c/ppb_websocket.h"
13 #include "ppapi/host/dispatch_host_message.h"
14 #include "ppapi/host/host_message_context.h"
15 #include "ppapi/host/ppapi_host.h"
16 #include "ppapi/proxy/ppapi_messages.h"
17 #include "third_party/WebKit/public/platform/WebArrayBuffer.h"
18 #include "third_party/WebKit/public/platform/WebString.h"
19 #include "third_party/WebKit/public/platform/WebURL.h"
20 #include "third_party/WebKit/public/web/WebDocument.h"
21 #include "third_party/WebKit/public/web/WebElement.h"
22 #include "third_party/WebKit/public/web/WebPluginContainer.h"
23 #include "third_party/WebKit/public/web/WebSocket.h"
24 
25 using blink::WebArrayBuffer;
26 using blink::WebDocument;
27 using blink::WebString;
28 using blink::WebSocket;
29 using blink::WebURL;
30 
31 namespace content {
32 
PepperWebSocketHost(RendererPpapiHost * host,PP_Instance instance,PP_Resource resource)33 PepperWebSocketHost::PepperWebSocketHost(
34     RendererPpapiHost* host,
35     PP_Instance instance,
36     PP_Resource resource)
37     : ResourceHost(host->GetPpapiHost(), instance, resource),
38       renderer_ppapi_host_(host),
39       connecting_(false),
40       initiating_close_(false),
41       accepting_close_(false),
42       error_was_received_(false) {
43 }
44 
~PepperWebSocketHost()45 PepperWebSocketHost::~PepperWebSocketHost() {
46   if (websocket_)
47     websocket_->disconnect();
48 }
49 
OnResourceMessageReceived(const IPC::Message & msg,ppapi::host::HostMessageContext * context)50 int32_t PepperWebSocketHost::OnResourceMessageReceived(
51     const IPC::Message& msg,
52     ppapi::host::HostMessageContext* context) {
53   IPC_BEGIN_MESSAGE_MAP(PepperWebSocketHost, msg)
54     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_WebSocket_Connect,
55                                       OnHostMsgConnect)
56     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_WebSocket_Close,
57                                       OnHostMsgClose)
58     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_WebSocket_SendText,
59                                       OnHostMsgSendText)
60     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_WebSocket_SendBinary,
61                                       OnHostMsgSendBinary)
62     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_WebSocket_Fail,
63                                       OnHostMsgFail)
64   IPC_END_MESSAGE_MAP()
65   return PP_ERROR_FAILED;
66 }
67 
didConnect()68 void PepperWebSocketHost::didConnect() {
69   std::string protocol;
70   if (websocket_)
71     protocol = websocket_->subprotocol().utf8();
72   connecting_ = false;
73   connect_reply_.params.set_result(PP_OK);
74   host()->SendReply(connect_reply_,
75                     PpapiPluginMsg_WebSocket_ConnectReply(
76                         url_,
77                         protocol));
78 }
79 
didReceiveMessage(const blink::WebString & message)80 void PepperWebSocketHost::didReceiveMessage(const blink::WebString& message) {
81   // Dispose packets after receiving an error.
82   if (error_was_received_)
83     return;
84 
85   // Send an IPC to transport received data.
86   std::string string_message = message.utf8();
87   host()->SendUnsolicitedReply(pp_resource(),
88                                PpapiPluginMsg_WebSocket_ReceiveTextReply(
89                                   string_message));
90 }
91 
didReceiveArrayBuffer(const blink::WebArrayBuffer & binaryData)92 void PepperWebSocketHost::didReceiveArrayBuffer(
93     const blink::WebArrayBuffer& binaryData) {
94   // Dispose packets after receiving an error.
95   if (error_was_received_)
96     return;
97 
98   // Send an IPC to transport received data.
99   uint8_t* data = static_cast<uint8_t*>(binaryData.data());
100   std::vector<uint8_t> array_message(data, data + binaryData.byteLength());
101   host()->SendUnsolicitedReply(pp_resource(),
102                                PpapiPluginMsg_WebSocket_ReceiveBinaryReply(
103                                   array_message));
104 }
105 
didReceiveMessageError()106 void PepperWebSocketHost::didReceiveMessageError() {
107   // Records the error, then stops receiving any frames after this error.
108   // The error must be notified after all queued messages are read.
109   error_was_received_ = true;
110 
111   // Send an IPC to report the error. After this IPC, ReceiveTextReply and
112   // ReceiveBinaryReply IPC are not sent anymore because |error_was_received_|
113   // blocks.
114   host()->SendUnsolicitedReply(pp_resource(),
115                                PpapiPluginMsg_WebSocket_ErrorReply());
116 }
117 
didUpdateBufferedAmount(unsigned long buffered_amount)118 void PepperWebSocketHost::didUpdateBufferedAmount(
119     unsigned long buffered_amount) {
120   // Send an IPC to update buffered amount.
121   host()->SendUnsolicitedReply(pp_resource(),
122                                PpapiPluginMsg_WebSocket_BufferedAmountReply(
123                                   buffered_amount));
124 }
125 
didStartClosingHandshake()126 void PepperWebSocketHost::didStartClosingHandshake() {
127   accepting_close_ = true;
128 
129   // Send an IPC to notice that server starts closing handshake.
130   host()->SendUnsolicitedReply(pp_resource(),
131                                PpapiPluginMsg_WebSocket_StateReply(
132                                   PP_WEBSOCKETREADYSTATE_CLOSING));
133 }
134 
didClose(unsigned long unhandled_buffered_amount,ClosingHandshakeCompletionStatus status,unsigned short code,const blink::WebString & reason)135 void PepperWebSocketHost::didClose(unsigned long unhandled_buffered_amount,
136                                    ClosingHandshakeCompletionStatus status,
137                                    unsigned short code,
138                                    const blink::WebString& reason) {
139   if (connecting_) {
140     connecting_ = false;
141     connect_reply_.params.set_result(PP_ERROR_FAILED);
142     host()->SendReply(
143         connect_reply_,
144         PpapiPluginMsg_WebSocket_ConnectReply(url_, std::string()));
145   }
146 
147   // Set close_was_clean_.
148   bool was_clean =
149       (initiating_close_ || accepting_close_) &&
150       !unhandled_buffered_amount &&
151       status == WebSocketClient::ClosingHandshakeComplete;
152 
153   if (initiating_close_) {
154     initiating_close_ = false;
155     close_reply_.params.set_result(PP_OK);
156     host()->SendReply(close_reply_, PpapiPluginMsg_WebSocket_CloseReply(
157           unhandled_buffered_amount,
158           was_clean,
159           code,
160           reason.utf8()));
161   } else {
162     accepting_close_ = false;
163     host()->SendUnsolicitedReply(pp_resource(),
164                                  PpapiPluginMsg_WebSocket_ClosedReply(
165                                      unhandled_buffered_amount,
166                                      was_clean,
167                                      code,
168                                      reason.utf8()));
169   }
170 
171   // Disconnect.
172   if (websocket_)
173     websocket_->disconnect();
174 }
175 
OnHostMsgConnect(ppapi::host::HostMessageContext * context,const std::string & url,const std::vector<std::string> & protocols)176 int32_t PepperWebSocketHost::OnHostMsgConnect(
177     ppapi::host::HostMessageContext* context,
178     const std::string& url,
179     const std::vector<std::string>& protocols) {
180   // Validate url and convert it to WebURL.
181   GURL gurl(url);
182   url_ = gurl.spec();
183   if (!gurl.is_valid())
184     return PP_ERROR_BADARGUMENT;
185   if (!gurl.SchemeIs("ws") && !gurl.SchemeIs("wss"))
186     return PP_ERROR_BADARGUMENT;
187   if (gurl.has_ref())
188     return PP_ERROR_BADARGUMENT;
189   if (!net::IsPortAllowedByDefault(gurl.IntPort()))
190     return PP_ERROR_BADARGUMENT;
191   WebURL web_url(gurl);
192 
193   // Validate protocols.
194   std::string protocol_string;
195   for (std::vector<std::string>::const_iterator vector_it = protocols.begin();
196        vector_it != protocols.end();
197        ++vector_it) {
198 
199     // Check containing characters.
200     for (std::string::const_iterator string_it = vector_it->begin();
201         string_it != vector_it->end();
202         ++string_it) {
203       uint8_t character = *string_it;
204       // WebSocket specification says "(Subprotocol string must consist of)
205       // characters in the range U+0021 to U+007E not including separator
206       // characters as defined in [RFC2616]."
207       const uint8_t minimumProtocolCharacter = '!';  // U+0021.
208       const uint8_t maximumProtocolCharacter = '~';  // U+007E.
209       if (character < minimumProtocolCharacter ||
210           character > maximumProtocolCharacter ||
211           character == '"' || character == '(' || character == ')' ||
212           character == ',' || character == '/' ||
213           (character >= ':' && character <= '@') ||  // U+003A - U+0040
214           (character >= '[' && character <= ']') ||  // U+005B - u+005D
215           character == '{' || character == '}')
216         return PP_ERROR_BADARGUMENT;
217     }
218     // Join protocols with the comma separator.
219     if (vector_it != protocols.begin())
220       protocol_string.append(",");
221     protocol_string.append(*vector_it);
222   }
223 
224   // Convert protocols to WebString.
225   WebString web_protocols = WebString::fromUTF8(protocol_string);
226 
227   // Create blink::WebSocket object and connect.
228   blink::WebPluginContainer* container =
229       renderer_ppapi_host_->GetContainerForInstance(pp_instance());
230   if (!container)
231     return PP_ERROR_BADARGUMENT;
232   // TODO(toyoshim) Remove following WebDocument object copy.
233   WebDocument document = container->element().document();
234   websocket_.reset(WebSocket::create(document, this));
235   DCHECK(websocket_.get());
236   if (!websocket_)
237     return PP_ERROR_NOTSUPPORTED;
238 
239   // Set receiving binary object type.
240   websocket_->setBinaryType(WebSocket::BinaryTypeArrayBuffer);
241   websocket_->connect(web_url, web_protocols);
242 
243   connect_reply_ = context->MakeReplyMessageContext();
244   connecting_ = true;
245   return PP_OK_COMPLETIONPENDING;
246 }
247 
OnHostMsgClose(ppapi::host::HostMessageContext * context,int32_t code,const std::string & reason)248 int32_t PepperWebSocketHost::OnHostMsgClose(
249     ppapi::host::HostMessageContext* context,
250     int32_t code,
251     const std::string& reason) {
252   if (!websocket_)
253     return PP_ERROR_FAILED;
254   close_reply_ = context->MakeReplyMessageContext();
255   initiating_close_ = true;
256   WebString web_reason = WebString::fromUTF8(reason);
257   websocket_->close(code, web_reason);
258   return PP_OK_COMPLETIONPENDING;
259 }
260 
OnHostMsgSendText(ppapi::host::HostMessageContext * context,const std::string & message)261 int32_t PepperWebSocketHost::OnHostMsgSendText(
262     ppapi::host::HostMessageContext* context,
263     const std::string& message) {
264   if (websocket_) {
265     WebString web_message = WebString::fromUTF8(message);
266     websocket_->sendText(web_message);
267   }
268   return PP_OK;
269 }
270 
OnHostMsgSendBinary(ppapi::host::HostMessageContext * context,const std::vector<uint8_t> & message)271 int32_t PepperWebSocketHost::OnHostMsgSendBinary(
272     ppapi::host::HostMessageContext* context,
273     const std::vector<uint8_t>& message) {
274   if (websocket_.get() && !message.empty()) {
275     WebArrayBuffer web_message = WebArrayBuffer::create(message.size(), 1);
276     memcpy(web_message.data(), &message.front(), message.size());
277     websocket_->sendArrayBuffer(web_message);
278   }
279   return PP_OK;
280 }
281 
OnHostMsgFail(ppapi::host::HostMessageContext * context,const std::string & message)282 int32_t PepperWebSocketHost::OnHostMsgFail(
283     ppapi::host::HostMessageContext* context,
284     const std::string& message) {
285   if (websocket_)
286     websocket_->fail(WebString::fromUTF8(message));
287   return PP_OK;
288 }
289 
290 }  // namespace content
291