• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
WebSocketBridge()43 WebSocketBridge::WebSocketBridge()
44     : channel_id_(kInvalidChannelId), client_(NULL) {}
45 
~WebSocketBridge()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 
OnMessageReceived(const IPC::Message & msg)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 
DidConnect(bool fail,const std::string & selected_protocol,const std::string & extensions)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 
DidStartOpeningHandshake(const WebSocketHandshakeRequest & request)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 
DidFinishOpeningHandshake(const WebSocketHandshakeResponse & response)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 
DidFail(const std::string & message)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 
DidReceiveData(bool fin,WebSocketMessageType type,const std::vector<char> & data)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 
DidReceiveFlowControl(int64_t quota)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 
DidClose(bool was_clean,unsigned short code,const std::string & reason)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 
connect(const WebURL & url,const WebVector<WebString> & protocols,const WebString & origin,WebSocketHandleClient * client)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 
send(bool fin,WebSocketHandle::MessageType type,const char * data,size_t size)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 
flowControl(int64_t quota)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 
close(unsigned short code,const WebString & reason)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 
Disconnect()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