Home | History | Annotate | Download | only in pepper
      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 WebKit::WebArrayBuffer;
     26 using WebKit::WebDocument;
     27 using WebKit::WebString;
     28 using WebKit::WebSocket;
     29 using WebKit::WebURL;
     30 
     31 namespace content {
     32 
     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 
     45 PepperWebSocketHost::~PepperWebSocketHost() {
     46   if (websocket_)
     47     websocket_->disconnect();
     48 }
     49 
     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 
     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 
     80 void 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 
     92 void 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 
    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 
    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 
    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 
    135 void 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 
    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 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 
    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 
    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 
    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 
    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