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.
5 #include "content/renderer/pepper/pepper_websocket_host.h"
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/Source/Platform/chromium/public/WebString.h"
18 #include "third_party/WebKit/Source/Platform/chromium/public/WebURL.h"
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebArrayBuffer.h"
20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h"
23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSocket.h"
25 using WebKit::WebArrayBuffer
;
26 using WebKit::WebDocument
;
27 using WebKit::WebString
;
28 using WebKit::WebSocket
;
33 PepperWebSocketHost::PepperWebSocketHost(
34 RendererPpapiHost
* host
,
37 : ResourceHost(host
->GetPpapiHost(), instance
, resource
),
38 renderer_ppapi_host_(host
),
40 initiating_close_(false),
41 accepting_close_(false),
42 error_was_received_(false) {
45 PepperWebSocketHost::~PepperWebSocketHost() {
47 websocket_
->disconnect();
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
,
56 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_WebSocket_Close
,
58 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_WebSocket_SendText
,
60 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_WebSocket_SendBinary
,
62 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_WebSocket_Fail
,
65 return PP_ERROR_FAILED
;
68 void PepperWebSocketHost::didConnect() {
71 protocol
= websocket_
->subprotocol().utf8();
73 connect_reply_
.params
.set_result(PP_OK
);
74 host()->SendReply(connect_reply_
,
75 PpapiPluginMsg_WebSocket_ConnectReply(
80 void PepperWebSocketHost::didReceiveMessage(const WebKit::WebString
& message
) {
81 // Dispose packets after receiving an error.
82 if (error_was_received_
)
85 // Send an IPC to transport received data.
86 std::string string_message
= message
.utf8();
87 host()->SendUnsolicitedReply(pp_resource(),
88 PpapiPluginMsg_WebSocket_ReceiveTextReply(
92 void PepperWebSocketHost::didReceiveArrayBuffer(
93 const WebKit::WebArrayBuffer
& binaryData
) {
94 // Dispose packets after receiving an error.
95 if (error_was_received_
)
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(
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;
111 // Send an IPC to report the error. After this IPC, ReceiveTextReply and
112 // ReceiveBinaryReply IPC are not sent anymore because |error_was_received_|
114 host()->SendUnsolicitedReply(pp_resource(),
115 PpapiPluginMsg_WebSocket_ErrorReply());
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(
126 void PepperWebSocketHost::didStartClosingHandshake() {
127 accepting_close_
= true;
129 // Send an IPC to notice that server starts closing handshake.
130 host()->SendUnsolicitedReply(pp_resource(),
131 PpapiPluginMsg_WebSocket_StateReply(
132 PP_WEBSOCKETREADYSTATE_CLOSING
));
135 void PepperWebSocketHost::didClose(unsigned long unhandled_buffered_amount
,
136 ClosingHandshakeCompletionStatus status
,
138 const WebKit::WebString
& reason
) {
141 connect_reply_
.params
.set_result(PP_ERROR_FAILED
);
144 PpapiPluginMsg_WebSocket_ConnectReply(url_
, std::string()));
147 // Set close_was_clean_.
149 (initiating_close_
|| accepting_close_
) &&
150 !unhandled_buffered_amount
&&
151 status
== WebSocketClient::ClosingHandshakeComplete
;
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
,
162 accepting_close_
= false;
163 host()->SendUnsolicitedReply(pp_resource(),
164 PpapiPluginMsg_WebSocket_ClosedReply(
165 unhandled_buffered_amount
,
173 websocket_
->disconnect();
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.
183 if (!gurl
.is_valid())
184 return PP_ERROR_BADARGUMENT
;
185 if (!gurl
.SchemeIs("ws") && !gurl
.SchemeIs("wss"))
186 return PP_ERROR_BADARGUMENT
;
188 return PP_ERROR_BADARGUMENT
;
189 if (!net::IsPortAllowedByDefault(gurl
.IntPort()))
190 return PP_ERROR_BADARGUMENT
;
191 WebURL
web_url(gurl
);
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();
199 // Check containing characters.
200 for (std::string::const_iterator string_it
= vector_it
->begin();
201 string_it
!= vector_it
->end();
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
;
218 // Join protocols with the comma separator.
219 if (vector_it
!= protocols
.begin())
220 protocol_string
.append(",");
221 protocol_string
.append(*vector_it
);
224 // Convert protocols to WebString.
225 WebString web_protocols
= WebString::fromUTF8(protocol_string
);
227 // Create WebKit::WebSocket object and connect.
228 WebKit::WebPluginContainer
* container
=
229 renderer_ppapi_host_
->GetContainerForInstance(pp_instance());
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());
237 return PP_ERROR_NOTSUPPORTED
;
239 // Set receiving binary object type.
240 websocket_
->setBinaryType(WebSocket::BinaryTypeArrayBuffer
);
241 websocket_
->connect(web_url
, web_protocols
);
243 connect_reply_
= context
->MakeReplyMessageContext();
245 return PP_OK_COMPLETIONPENDING
;
248 int32_t PepperWebSocketHost::OnHostMsgClose(
249 ppapi::host::HostMessageContext
* context
,
251 const std::string
& reason
) {
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
;
261 int32_t PepperWebSocketHost::OnHostMsgSendText(
262 ppapi::host::HostMessageContext
* context
,
263 const std::string
& message
) {
265 WebString web_message
= WebString::fromUTF8(message
);
266 websocket_
->sendText(web_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
);
282 int32_t PepperWebSocketHost::OnHostMsgFail(
283 ppapi::host::HostMessageContext
* context
,
284 const std::string
& message
) {
286 websocket_
->fail(WebString::fromUTF8(message
));
290 } // namespace content