pepper_websocket_host.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/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"
24
25using WebKit::WebArrayBuffer;
26using WebKit::WebDocument;
27using WebKit::WebString;
28using WebKit::WebSocket;
29using WebKit::WebURL;
30
31namespace content {
32
33PepperWebSocketHost::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
45PepperWebSocketHost::~PepperWebSocketHost() {
46  if (websocket_)
47    websocket_->disconnect();
48}
49
50int32_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
68void 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
80void PepperWebSocketHost::didReceiveMessage(const WebKit::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
92void PepperWebSocketHost::didReceiveArrayBuffer(
93    const WebKit::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
106void 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
118void 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
126void 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
135void PepperWebSocketHost::didClose(unsigned long unhandled_buffered_amount,
136                                   ClosingHandshakeCompletionStatus status,
137                                   unsigned short code,
138                                   const WebKit::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
176int32_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 WebKit::WebSocket object and connect.
228  WebKit::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
248int32_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
261int32_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
271int32_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
282int32_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