Home | History | Annotate | Download | only in child
      1 // Copyright 2013 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/child/websocket_bridge.h"
      6 
      7 #include <stdint.h>
      8 #include <string>
      9 #include <utility>
     10 #include <vector>
     11 
     12 #include "base/logging.h"
     13 #include "base/strings/string_util.h"
     14 #include "content/child/child_thread.h"
     15 #include "content/child/websocket_dispatcher.h"
     16 #include "content/common/websocket.h"
     17 #include "content/common/websocket_messages.h"
     18 #include "ipc/ipc_message.h"
     19 #include "ipc/ipc_message_macros.h"
     20 #include "url/gurl.h"
     21 #include "third_party/WebKit/public/platform/WebSocketHandle.h"
     22 #include "third_party/WebKit/public/platform/WebSocketHandleClient.h"
     23 #include "third_party/WebKit/public/platform/WebSocketHandshakeRequestInfo.h"
     24 #include "third_party/WebKit/public/platform/WebSocketHandshakeResponseInfo.h"
     25 #include "third_party/WebKit/public/platform/WebString.h"
     26 #include "third_party/WebKit/public/platform/WebURL.h"
     27 #include "third_party/WebKit/public/platform/WebVector.h"
     28 
     29 using blink::WebSocketHandle;
     30 using blink::WebSocketHandleClient;
     31 using blink::WebString;
     32 using blink::WebURL;
     33 using blink::WebVector;
     34 
     35 namespace content {
     36 
     37 namespace {
     38 
     39 const unsigned short kAbnormalShutdownOpCode = 1006;
     40 
     41 }  // namespace
     42 
     43 WebSocketBridge::WebSocketBridge()
     44     : channel_id_(kInvalidChannelId), client_(NULL) {}
     45 
     46 WebSocketBridge::~WebSocketBridge() {
     47   if (channel_id_ != kInvalidChannelId) {
     48     // The connection is abruptly disconnected by the renderer without
     49     // closing handshake.
     50     ChildThread::current()->Send(
     51         new WebSocketMsg_DropChannel(channel_id_,
     52                                      false,
     53                                      kAbnormalShutdownOpCode,
     54                                      std::string()));
     55   }
     56   Disconnect();
     57 }
     58 
     59 bool WebSocketBridge::OnMessageReceived(const IPC::Message& msg) {
     60   bool handled = true;
     61   IPC_BEGIN_MESSAGE_MAP(WebSocketBridge, msg)
     62     IPC_MESSAGE_HANDLER(WebSocketMsg_AddChannelResponse, DidConnect)
     63     IPC_MESSAGE_HANDLER(WebSocketMsg_NotifyStartOpeningHandshake,
     64                         DidStartOpeningHandshake)
     65     IPC_MESSAGE_HANDLER(WebSocketMsg_NotifyFinishOpeningHandshake,
     66                         DidFinishOpeningHandshake)
     67     IPC_MESSAGE_HANDLER(WebSocketMsg_NotifyFailure, DidFail)
     68     IPC_MESSAGE_HANDLER(WebSocketMsg_SendFrame, DidReceiveData)
     69     IPC_MESSAGE_HANDLER(WebSocketMsg_FlowControl, DidReceiveFlowControl)
     70     IPC_MESSAGE_HANDLER(WebSocketMsg_DropChannel, DidClose)
     71     IPC_MESSAGE_UNHANDLED(handled = false)
     72   IPC_END_MESSAGE_MAP()
     73   return handled;
     74 }
     75 
     76 void WebSocketBridge::DidConnect(bool fail,
     77                                  const std::string& selected_protocol,
     78                                  const std::string& extensions) {
     79   WebSocketHandleClient* client = client_;
     80   DVLOG(1) << "WebSocketBridge::DidConnect("
     81            << fail << ", "
     82            << selected_protocol << ", "
     83            << extensions << ")";
     84   if (fail)
     85     Disconnect();
     86   if (!client)
     87     return;
     88 
     89   WebString protocol_to_pass = WebString::fromUTF8(selected_protocol);
     90   WebString extensions_to_pass = WebString::fromUTF8(extensions);
     91   client->didConnect(this, fail, protocol_to_pass, extensions_to_pass);
     92   // |this| can be deleted here.
     93 }
     94 
     95 void WebSocketBridge::DidStartOpeningHandshake(
     96     const WebSocketHandshakeRequest& request) {
     97   DVLOG(1) << "WebSocketBridge::DidStartOpeningHandshake("
     98            << request.url << ")";
     99   // All strings are already encoded to ASCII in the browser.
    100   blink::WebSocketHandshakeRequestInfo request_to_pass;
    101   request_to_pass.setURL(WebURL(request.url));
    102   for (size_t i = 0; i < request.headers.size(); ++i) {
    103     const std::pair<std::string, std::string>& header = request.headers[i];
    104     request_to_pass.addHeaderField(WebString::fromLatin1(header.first),
    105                                    WebString::fromLatin1(header.second));
    106   }
    107   client_->didStartOpeningHandshake(this, request_to_pass);
    108 }
    109 
    110 void WebSocketBridge::DidFinishOpeningHandshake(
    111     const WebSocketHandshakeResponse& response) {
    112   DVLOG(1) << "WebSocketBridge::DidFinishOpeningHandshake("
    113            << response.url << ")";
    114   // All strings are already encoded to ASCII in the browser.
    115   blink::WebSocketHandshakeResponseInfo response_to_pass;
    116   response_to_pass.setStatusCode(response.status_code);
    117   response_to_pass.setStatusText(WebString::fromLatin1(response.status_text));
    118   for (size_t i = 0; i < response.headers.size(); ++i) {
    119     const std::pair<std::string, std::string>& header = response.headers[i];
    120     response_to_pass.addHeaderField(WebString::fromLatin1(header.first),
    121                                     WebString::fromLatin1(header.second));
    122   }
    123   client_->didFinishOpeningHandshake(this, response_to_pass);
    124 }
    125 
    126 void WebSocketBridge::DidFail(const std::string& message) {
    127   DVLOG(1) << "WebSocketBridge::DidFail(" << message << ")";
    128   WebSocketHandleClient* client = client_;
    129   Disconnect();
    130   if (!client)
    131     return;
    132 
    133   WebString message_to_pass = WebString::fromUTF8(message);
    134   client->didFail(this, message_to_pass);
    135   // |this| can be deleted here.
    136 }
    137 
    138 void WebSocketBridge::DidReceiveData(bool fin,
    139                                      WebSocketMessageType type,
    140                                      const std::vector<char>& data) {
    141   DVLOG(1) << "WebSocketBridge::DidReceiveData("
    142            << fin << ", "
    143            << type << ", "
    144            << "(data size = " << data.size() << "))";
    145   if (!client_)
    146     return;
    147 
    148   WebSocketHandle::MessageType type_to_pass =
    149       WebSocketHandle::MessageTypeContinuation;
    150   switch (type) {
    151     case WEB_SOCKET_MESSAGE_TYPE_CONTINUATION:
    152       type_to_pass = WebSocketHandle::MessageTypeContinuation;
    153       break;
    154     case WEB_SOCKET_MESSAGE_TYPE_TEXT:
    155       type_to_pass = WebSocketHandle::MessageTypeText;
    156       break;
    157     case WEB_SOCKET_MESSAGE_TYPE_BINARY:
    158       type_to_pass = WebSocketHandle::MessageTypeBinary;
    159       break;
    160   }
    161   const char* data_to_pass = data.empty() ? NULL : &data[0];
    162   client_->didReceiveData(this, fin, type_to_pass, data_to_pass, data.size());
    163   // |this| can be deleted here.
    164 }
    165 
    166 void WebSocketBridge::DidReceiveFlowControl(int64_t quota) {
    167   DVLOG(1) << "WebSocketBridge::DidReceiveFlowControl(" << quota << ")";
    168   if (!client_)
    169     return;
    170 
    171   client_->didReceiveFlowControl(this, quota);
    172   // |this| can be deleted here.
    173 }
    174 
    175 void WebSocketBridge::DidClose(bool was_clean,
    176                                unsigned short code,
    177                                const std::string& reason) {
    178   DVLOG(1) << "WebSocketBridge::DidClose("
    179            << was_clean << ", "
    180            << code << ", "
    181            << reason << ")";
    182   WebSocketHandleClient* client = client_;
    183   Disconnect();
    184   if (!client)
    185     return;
    186 
    187   WebString reason_to_pass = WebString::fromUTF8(reason);
    188   client->didClose(this, was_clean, code, reason_to_pass);
    189   // |this| can be deleted here.
    190 }
    191 
    192 void WebSocketBridge::connect(
    193     const WebURL& url,
    194     const WebVector<WebString>& protocols,
    195     const WebString& origin,
    196     WebSocketHandleClient* client) {
    197   DCHECK_EQ(kInvalidChannelId, channel_id_);
    198   WebSocketDispatcher* dispatcher =
    199       ChildThread::current()->websocket_dispatcher();
    200   channel_id_ = dispatcher->AddBridge(this);
    201   client_ = client;
    202 
    203   std::vector<std::string> protocols_to_pass;
    204   for (size_t i = 0; i < protocols.size(); ++i)
    205     protocols_to_pass.push_back(protocols[i].utf8());
    206   GURL origin_to_pass(origin.utf8());
    207 
    208   DVLOG(1) << "Bridge#" << channel_id_ << " Connect("
    209            << url << ", (" << JoinString(protocols_to_pass, ", ") << "), "
    210            << origin_to_pass << ")";
    211 
    212   ChildThread::current()->Send(
    213       new WebSocketHostMsg_AddChannelRequest(channel_id_,
    214                                              url,
    215                                              protocols_to_pass,
    216                                              origin_to_pass));
    217 }
    218 
    219 void WebSocketBridge::send(bool fin,
    220                            WebSocketHandle::MessageType type,
    221                            const char* data,
    222                            size_t size) {
    223   if (channel_id_ == kInvalidChannelId)
    224     return;
    225 
    226   WebSocketMessageType type_to_pass = WEB_SOCKET_MESSAGE_TYPE_CONTINUATION;
    227   switch (type) {
    228     case WebSocketHandle::MessageTypeContinuation:
    229       type_to_pass = WEB_SOCKET_MESSAGE_TYPE_CONTINUATION;
    230       break;
    231     case WebSocketHandle::MessageTypeText:
    232       type_to_pass = WEB_SOCKET_MESSAGE_TYPE_TEXT;
    233       break;
    234     case WebSocketHandle::MessageTypeBinary:
    235       type_to_pass = WEB_SOCKET_MESSAGE_TYPE_BINARY;
    236       break;
    237   }
    238 
    239   DVLOG(1) << "Bridge #" << channel_id_ << " Send("
    240            << fin << ", " << type_to_pass << ", "
    241            << "(data size = "  << size << "))";
    242 
    243   ChildThread::current()->Send(
    244       new WebSocketMsg_SendFrame(channel_id_,
    245                                  fin,
    246                                  type_to_pass,
    247                                  std::vector<char>(data, data + size)));
    248 }
    249 
    250 void WebSocketBridge::flowControl(int64_t quota) {
    251   if (channel_id_ == kInvalidChannelId)
    252     return;
    253 
    254   DVLOG(1) << "Bridge #" << channel_id_ << " FlowControl(" << quota << ")";
    255 
    256   ChildThread::current()->Send(
    257       new WebSocketMsg_FlowControl(channel_id_, quota));
    258 }
    259 
    260 void WebSocketBridge::close(unsigned short code,
    261                             const WebString& reason) {
    262   if (channel_id_ == kInvalidChannelId)
    263     return;
    264 
    265   std::string reason_to_pass = reason.utf8();
    266   DVLOG(1) << "Bridge #" << channel_id_ << " Close("
    267            << code << ", " << reason_to_pass << ")";
    268   // This method is for closing handshake and hence |was_clean| shall be true.
    269   ChildThread::current()->Send(
    270       new WebSocketMsg_DropChannel(channel_id_, true, code, reason_to_pass));
    271 }
    272 
    273 void WebSocketBridge::Disconnect() {
    274   if (channel_id_ == kInvalidChannelId)
    275     return;
    276   WebSocketDispatcher* dispatcher =
    277       ChildThread::current()->websocket_dispatcher();
    278   dispatcher->RemoveBridge(channel_id_);
    279 
    280   channel_id_ = kInvalidChannelId;
    281   client_ = NULL;
    282 }
    283 
    284 }  // namespace content
    285