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.
5 #include "content/child/websocket_bridge.h"
12 #include "base/logging.h"
13 #include "base/strings/string_util.h"
14 #include "content/child/child_thread_impl.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 "third_party/WebKit/public/platform/WebSecurityOrigin.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"
29 #include "url/origin.h"
31 using blink::WebSecurityOrigin
;
32 using blink::WebSocketHandle
;
33 using blink::WebSocketHandleClient
;
34 using blink::WebString
;
36 using blink::WebVector
;
42 const unsigned short kAbnormalShutdownOpCode
= 1006;
46 WebSocketBridge::WebSocketBridge()
47 : channel_id_(kInvalidChannelId
),
48 render_frame_id_(MSG_ROUTING_NONE
),
51 WebSocketBridge::~WebSocketBridge() {
52 if (channel_id_
!= kInvalidChannelId
) {
53 // The connection is abruptly disconnected by the renderer without
55 ChildThreadImpl::current()->Send(
56 new WebSocketMsg_DropChannel(channel_id_
,
58 kAbnormalShutdownOpCode
,
64 bool WebSocketBridge::OnMessageReceived(const IPC::Message
& msg
) {
66 IPC_BEGIN_MESSAGE_MAP(WebSocketBridge
, msg
)
67 IPC_MESSAGE_HANDLER(WebSocketMsg_AddChannelResponse
, DidConnect
)
68 IPC_MESSAGE_HANDLER(WebSocketMsg_NotifyStartOpeningHandshake
,
69 DidStartOpeningHandshake
)
70 IPC_MESSAGE_HANDLER(WebSocketMsg_NotifyFinishOpeningHandshake
,
71 DidFinishOpeningHandshake
)
72 IPC_MESSAGE_HANDLER(WebSocketMsg_NotifyFailure
, DidFail
)
73 IPC_MESSAGE_HANDLER(WebSocketMsg_SendFrame
, DidReceiveData
)
74 IPC_MESSAGE_HANDLER(WebSocketMsg_FlowControl
, DidReceiveFlowControl
)
75 IPC_MESSAGE_HANDLER(WebSocketMsg_DropChannel
, DidClose
)
76 IPC_MESSAGE_HANDLER(WebSocketMsg_NotifyClosing
,
77 DidStartClosingHandshake
)
78 IPC_MESSAGE_UNHANDLED(handled
= false)
83 void WebSocketBridge::DidConnect(const std::string
& selected_protocol
,
84 const std::string
& extensions
) {
85 WebSocketHandleClient
* client
= client_
;
86 DVLOG(1) << "WebSocketBridge::DidConnect("
87 << selected_protocol
<< ", "
92 WebString protocol_to_pass
= WebString::fromUTF8(selected_protocol
);
93 WebString extensions_to_pass
= WebString::fromUTF8(extensions
);
94 client
->didConnect(this, protocol_to_pass
, extensions_to_pass
);
95 // |this| can be deleted here.
98 void WebSocketBridge::DidStartOpeningHandshake(
99 const WebSocketHandshakeRequest
& request
) {
100 DVLOG(1) << "WebSocketBridge::DidStartOpeningHandshake("
101 << request
.url
<< ")";
102 // All strings are already encoded to ASCII in the browser.
103 blink::WebSocketHandshakeRequestInfo request_to_pass
;
104 request_to_pass
.setURL(WebURL(request
.url
));
105 for (size_t i
= 0; i
< request
.headers
.size(); ++i
) {
106 const std::pair
<std::string
, std::string
>& header
= request
.headers
[i
];
107 request_to_pass
.addHeaderField(WebString::fromLatin1(header
.first
),
108 WebString::fromLatin1(header
.second
));
110 request_to_pass
.setHeadersText(WebString::fromLatin1(request
.headers_text
));
111 client_
->didStartOpeningHandshake(this, request_to_pass
);
114 void WebSocketBridge::DidFinishOpeningHandshake(
115 const WebSocketHandshakeResponse
& response
) {
116 DVLOG(1) << "WebSocketBridge::DidFinishOpeningHandshake("
117 << response
.url
<< ")";
118 // All strings are already encoded to ASCII in the browser.
119 blink::WebSocketHandshakeResponseInfo response_to_pass
;
120 response_to_pass
.setStatusCode(response
.status_code
);
121 response_to_pass
.setStatusText(WebString::fromLatin1(response
.status_text
));
122 for (size_t i
= 0; i
< response
.headers
.size(); ++i
) {
123 const std::pair
<std::string
, std::string
>& header
= response
.headers
[i
];
124 response_to_pass
.addHeaderField(WebString::fromLatin1(header
.first
),
125 WebString::fromLatin1(header
.second
));
127 response_to_pass
.setHeadersText(WebString::fromLatin1(response
.headers_text
));
128 client_
->didFinishOpeningHandshake(this, response_to_pass
);
131 void WebSocketBridge::DidFail(const std::string
& message
) {
132 DVLOG(1) << "WebSocketBridge::DidFail(" << message
<< ")";
133 WebSocketHandleClient
* client
= client_
;
138 WebString message_to_pass
= WebString::fromUTF8(message
);
139 client
->didFail(this, message_to_pass
);
140 // |this| can be deleted here.
143 void WebSocketBridge::DidReceiveData(bool fin
,
144 WebSocketMessageType type
,
145 const std::vector
<char>& data
) {
146 DVLOG(1) << "WebSocketBridge::DidReceiveData("
149 << "(data size = " << data
.size() << "))";
153 WebSocketHandle::MessageType type_to_pass
=
154 WebSocketHandle::MessageTypeContinuation
;
156 case WEB_SOCKET_MESSAGE_TYPE_CONTINUATION
:
157 type_to_pass
= WebSocketHandle::MessageTypeContinuation
;
159 case WEB_SOCKET_MESSAGE_TYPE_TEXT
:
160 type_to_pass
= WebSocketHandle::MessageTypeText
;
162 case WEB_SOCKET_MESSAGE_TYPE_BINARY
:
163 type_to_pass
= WebSocketHandle::MessageTypeBinary
;
166 const char* data_to_pass
= data
.empty() ? NULL
: &data
[0];
167 client_
->didReceiveData(this, fin
, type_to_pass
, data_to_pass
, data
.size());
168 // |this| can be deleted here.
171 void WebSocketBridge::DidReceiveFlowControl(int64_t quota
) {
172 DVLOG(1) << "WebSocketBridge::DidReceiveFlowControl(" << quota
<< ")";
176 client_
->didReceiveFlowControl(this, quota
);
177 // |this| can be deleted here.
180 void WebSocketBridge::DidClose(bool was_clean
,
182 const std::string
& reason
) {
183 DVLOG(1) << "WebSocketBridge::DidClose("
187 WebSocketHandleClient
* client
= client_
;
192 WebString reason_to_pass
= WebString::fromUTF8(reason
);
193 client
->didClose(this, was_clean
, code
, reason_to_pass
);
194 // |this| can be deleted here.
197 void WebSocketBridge::DidStartClosingHandshake() {
198 DVLOG(1) << "WebSocketBridge::DidStartClosingHandshake()";
202 client_
->didStartClosingHandshake(this);
203 // |this| can be deleted here.
206 void WebSocketBridge::connect(const WebURL
& url
,
207 const WebVector
<WebString
>& protocols
,
208 const WebSecurityOrigin
& origin
,
209 WebSocketHandleClient
* client
) {
210 DCHECK_EQ(kInvalidChannelId
, channel_id_
);
211 WebSocketDispatcher
* dispatcher
=
212 ChildThreadImpl::current()->websocket_dispatcher();
213 channel_id_
= dispatcher
->AddBridge(this);
216 std::vector
<std::string
> protocols_to_pass
;
217 for (size_t i
= 0; i
< protocols
.size(); ++i
)
218 protocols_to_pass
.push_back(protocols
[i
].utf8());
220 DVLOG(1) << "Bridge#" << channel_id_
<< " Connect(" << url
<< ", ("
221 << base::JoinString(protocols_to_pass
, ", ") << "), "
222 << origin
.toString().utf8() << ")";
224 ChildThreadImpl::current()->Send(new WebSocketHostMsg_AddChannelRequest(
225 channel_id_
, url
, protocols_to_pass
, origin
, render_frame_id_
));
228 void WebSocketBridge::send(bool fin
,
229 WebSocketHandle::MessageType type
,
232 if (channel_id_
== kInvalidChannelId
)
235 WebSocketMessageType type_to_pass
= WEB_SOCKET_MESSAGE_TYPE_CONTINUATION
;
237 case WebSocketHandle::MessageTypeContinuation
:
238 type_to_pass
= WEB_SOCKET_MESSAGE_TYPE_CONTINUATION
;
240 case WebSocketHandle::MessageTypeText
:
241 type_to_pass
= WEB_SOCKET_MESSAGE_TYPE_TEXT
;
243 case WebSocketHandle::MessageTypeBinary
:
244 type_to_pass
= WEB_SOCKET_MESSAGE_TYPE_BINARY
;
248 DVLOG(1) << "Bridge #" << channel_id_
<< " Send("
249 << fin
<< ", " << type_to_pass
<< ", "
250 << "(data size = " << size
<< "))";
252 ChildThreadImpl::current()->Send(
253 new WebSocketMsg_SendFrame(channel_id_
,
256 std::vector
<char>(data
, data
+ size
)));
259 void WebSocketBridge::flowControl(int64_t quota
) {
260 if (channel_id_
== kInvalidChannelId
)
263 DVLOG(1) << "Bridge #" << channel_id_
<< " FlowControl(" << quota
<< ")";
265 ChildThreadImpl::current()->Send(
266 new WebSocketMsg_FlowControl(channel_id_
, quota
));
269 void WebSocketBridge::close(unsigned short code
,
270 const WebString
& reason
) {
271 if (channel_id_
== kInvalidChannelId
)
274 std::string reason_to_pass
= reason
.utf8();
275 DVLOG(1) << "Bridge #" << channel_id_
<< " Close("
276 << code
<< ", " << reason_to_pass
<< ")";
277 // This method is for closing handshake and hence |was_clean| shall be true.
278 ChildThreadImpl::current()->Send(
279 new WebSocketMsg_DropChannel(channel_id_
, true, code
, reason_to_pass
));
282 void WebSocketBridge::Disconnect() {
283 if (channel_id_
== kInvalidChannelId
)
285 WebSocketDispatcher
* dispatcher
=
286 ChildThreadImpl::current()->websocket_dispatcher();
287 dispatcher
->RemoveBridge(channel_id_
);
289 channel_id_
= kInvalidChannelId
;
293 } // namespace content