OLD | NEW |
| (Empty) |
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/renderer/websockethandle_impl.h" | |
6 | |
7 #include <stdint.h> | |
8 #include <string.h> | |
9 | |
10 #include <string> | |
11 #include <utility> | |
12 | |
13 #include "base/logging.h" | |
14 #include "base/strings/string_util.h" | |
15 #include "third_party/WebKit/public/platform/InterfaceProvider.h" | |
16 #include "third_party/WebKit/public/platform/Platform.h" | |
17 #include "third_party/WebKit/public/platform/WebSecurityOrigin.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/platform/WebVector.h" | |
21 #include "third_party/WebKit/public/platform/modules/websockets/WebSocketHandle.
h" | |
22 #include "third_party/WebKit/public/platform/modules/websockets/WebSocketHandleC
lient.h" | |
23 #include "third_party/WebKit/public/platform/modules/websockets/WebSocketHandsha
keRequestInfo.h" | |
24 #include "third_party/WebKit/public/platform/modules/websockets/WebSocketHandsha
keResponseInfo.h" | |
25 #include "url/gurl.h" | |
26 #include "url/origin.h" | |
27 | |
28 using blink::WebSecurityOrigin; | |
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 namespace { | |
37 | |
38 const uint16_t kAbnormalShutdownOpCode = 1006; | |
39 | |
40 } // namespace | |
41 | |
42 WebSocketHandleImpl::WebSocketHandleImpl( | |
43 scoped_refptr<base::SingleThreadTaskRunner> task_runner) | |
44 : client_(nullptr), | |
45 client_binding_(this), | |
46 task_runner_(std::move(task_runner)), | |
47 did_initialize_(false) { | |
48 DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) | |
49 << " created"; | |
50 } | |
51 | |
52 void WebSocketHandleImpl::Initialize( | |
53 blink::InterfaceProvider* interface_provider) { | |
54 DCHECK(!websocket_); | |
55 DCHECK(!did_initialize_); | |
56 | |
57 interface_provider->getInterface(mojo::GetProxy(&websocket_)); | |
58 | |
59 websocket_.set_connection_error_handler( | |
60 base::Bind(&WebSocketHandleImpl::OnConnectionError, | |
61 base::Unretained(this))); | |
62 did_initialize_ = true; | |
63 } | |
64 | |
65 void WebSocketHandleImpl::connect(const WebURL& url, | |
66 const WebVector<WebString>& protocols, | |
67 const WebSecurityOrigin& origin, | |
68 const WebURL& first_party_for_cookies, | |
69 const WebString& user_agent_override, | |
70 WebSocketHandleClient* client) { | |
71 DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) | |
72 << " Connect(" << url.string().utf8() << ", " | |
73 << origin.toString().utf8() << ")"; | |
74 | |
75 // It is insufficient to test if websocket_ is non-null as Disconnect() sets | |
76 // websocket_ to null. | |
77 if (!did_initialize_) | |
78 Initialize(blink::Platform::current()->interfaceProvider()); | |
79 | |
80 DCHECK(websocket_); | |
81 | |
82 DCHECK(!client_); | |
83 DCHECK(client); | |
84 client_ = client; | |
85 | |
86 std::vector<std::string> protocols_to_pass(protocols.size()); | |
87 for (size_t i = 0; i < protocols.size(); ++i) | |
88 protocols_to_pass[i] = protocols[i].utf8(); | |
89 | |
90 websocket_->AddChannelRequest( | |
91 url, protocols_to_pass, origin, first_party_for_cookies, | |
92 user_agent_override.latin1(), | |
93 client_binding_.CreateInterfacePtrAndBind(task_runner_)); | |
94 } | |
95 | |
96 void WebSocketHandleImpl::send(bool fin, | |
97 WebSocketHandle::MessageType type, | |
98 const char* data, | |
99 size_t size) { | |
100 DCHECK(websocket_); | |
101 | |
102 mojom::WebSocketMessageType type_to_pass; | |
103 switch (type) { | |
104 case WebSocketHandle::MessageTypeContinuation: | |
105 type_to_pass = mojom::WebSocketMessageType::CONTINUATION; | |
106 break; | |
107 case WebSocketHandle::MessageTypeText: | |
108 type_to_pass = mojom::WebSocketMessageType::TEXT; | |
109 break; | |
110 case WebSocketHandle::MessageTypeBinary: | |
111 type_to_pass = mojom::WebSocketMessageType::BINARY; | |
112 break; | |
113 default: | |
114 NOTREACHED(); | |
115 return; | |
116 } | |
117 | |
118 DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) | |
119 << " Send(" << fin << ", " << type_to_pass << ", " | |
120 << "(data size = " << size << "))"; | |
121 | |
122 std::vector<uint8_t> data_to_pass(size); | |
123 std::copy(data, data + size, data_to_pass.begin()); | |
124 | |
125 websocket_->SendFrame(fin, type_to_pass, data_to_pass); | |
126 } | |
127 | |
128 void WebSocketHandleImpl::flowControl(int64_t quota) { | |
129 DCHECK(websocket_); | |
130 | |
131 DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) | |
132 << " FlowControl(" << quota << ")"; | |
133 | |
134 websocket_->SendFlowControl(quota); | |
135 } | |
136 | |
137 void WebSocketHandleImpl::close(unsigned short code, const WebString& reason) { | |
138 DCHECK(websocket_); | |
139 | |
140 std::string reason_to_pass = reason.utf8(); | |
141 | |
142 DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) | |
143 << " Close(" << code << ", " << reason_to_pass << ")"; | |
144 | |
145 websocket_->StartClosingHandshake(code, reason_to_pass); | |
146 } | |
147 | |
148 WebSocketHandleImpl::~WebSocketHandleImpl() { | |
149 DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) | |
150 << " deleted"; | |
151 | |
152 if (websocket_) | |
153 websocket_->StartClosingHandshake(kAbnormalShutdownOpCode, std::string()); | |
154 } | |
155 | |
156 void WebSocketHandleImpl::Disconnect() { | |
157 websocket_.reset(); | |
158 client_ = nullptr; | |
159 } | |
160 | |
161 void WebSocketHandleImpl::OnConnectionError() { | |
162 if (!blink::Platform::current()) { | |
163 // In the renderrer shutdown sequence, mojo channels are destructed and this | |
164 // function is called. On the other hand, blink objects became invalid | |
165 // *silently*, which means we must not touch |*client_| any more. | |
166 // TODO(yhirano): Remove this code once the shutdown sequence is fixed. | |
167 Disconnect(); | |
168 return; | |
169 } | |
170 | |
171 // Our connection to the WebSocket was dropped. This could be due to | |
172 // exceeding the maximum number of concurrent websockets from this process. | |
173 | |
174 // TODO(darin): This error message is overly specific. We don't know for sure | |
175 // that this is the only reason we'd get here. This should be more generic or | |
176 // we should figure out how to make it more specific. | |
177 OnFailChannel("Error in connection establishment: " | |
178 "net::ERR_INSUFFICIENT_RESOURCES"); | |
179 } | |
180 | |
181 void WebSocketHandleImpl::OnFailChannel(const std::string& message) { | |
182 DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) | |
183 << " OnFailChannel(" << message << ")"; | |
184 | |
185 WebSocketHandleClient* client = client_; | |
186 Disconnect(); | |
187 if (!client) | |
188 return; | |
189 | |
190 client->didFail(this, WebString::fromUTF8(message)); | |
191 // |this| can be deleted here. | |
192 } | |
193 | |
194 void WebSocketHandleImpl::OnStartOpeningHandshake( | |
195 mojom::WebSocketHandshakeRequestPtr request) { | |
196 DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) | |
197 << " OnStartOpeningHandshake(" << request->url << ")"; | |
198 // All strings are already encoded to ASCII in the browser. | |
199 blink::WebSocketHandshakeRequestInfo request_to_pass; | |
200 request_to_pass.setURL(WebURL(request->url)); | |
201 for (size_t i = 0; i < request->headers.size(); ++i) { | |
202 const mojom::HttpHeaderPtr& header = request->headers[i]; | |
203 request_to_pass.addHeaderField(WebString::fromLatin1(header->name), | |
204 WebString::fromLatin1(header->value)); | |
205 } | |
206 request_to_pass.setHeadersText(WebString::fromLatin1(request->headers_text)); | |
207 client_->didStartOpeningHandshake(this, request_to_pass); | |
208 } | |
209 | |
210 void WebSocketHandleImpl::OnFinishOpeningHandshake( | |
211 mojom::WebSocketHandshakeResponsePtr response) { | |
212 DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) | |
213 << " OnFinishOpeningHandshake(" << response->url << ")"; | |
214 | |
215 // All strings are already encoded to ASCII in the browser. | |
216 blink::WebSocketHandshakeResponseInfo response_to_pass; | |
217 response_to_pass.setStatusCode(response->status_code); | |
218 response_to_pass.setStatusText(WebString::fromLatin1(response->status_text)); | |
219 for (size_t i = 0; i < response->headers.size(); ++i) { | |
220 const mojom::HttpHeaderPtr& header = response->headers[i]; | |
221 response_to_pass.addHeaderField(WebString::fromLatin1(header->name), | |
222 WebString::fromLatin1(header->value)); | |
223 } | |
224 response_to_pass.setHeadersText( | |
225 WebString::fromLatin1(response->headers_text)); | |
226 client_->didFinishOpeningHandshake(this, response_to_pass); | |
227 } | |
228 | |
229 void WebSocketHandleImpl::OnAddChannelResponse(const std::string& protocol, | |
230 const std::string& extensions) { | |
231 DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) | |
232 << " OnAddChannelResponse(" | |
233 << protocol << ", " << extensions << ")"; | |
234 | |
235 if (!client_) | |
236 return; | |
237 | |
238 client_->didConnect(this, | |
239 WebString::fromUTF8(protocol), | |
240 WebString::fromUTF8(extensions)); | |
241 // |this| can be deleted here. | |
242 } | |
243 | |
244 void WebSocketHandleImpl::OnDataFrame(bool fin, | |
245 mojom::WebSocketMessageType type, | |
246 const std::vector<uint8_t>& data) { | |
247 DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) | |
248 << " OnDataFrame(" << fin << ", " << type << ", " | |
249 << "(data size = " << data.size() << "))"; | |
250 if (!client_) | |
251 return; | |
252 | |
253 WebSocketHandle::MessageType type_to_pass = | |
254 WebSocketHandle::MessageTypeContinuation; | |
255 switch (type) { | |
256 case mojom::WebSocketMessageType::CONTINUATION: | |
257 type_to_pass = WebSocketHandle::MessageTypeContinuation; | |
258 break; | |
259 case mojom::WebSocketMessageType::TEXT: | |
260 type_to_pass = WebSocketHandle::MessageTypeText; | |
261 break; | |
262 case mojom::WebSocketMessageType::BINARY: | |
263 type_to_pass = WebSocketHandle::MessageTypeBinary; | |
264 break; | |
265 } | |
266 const char* data_to_pass = | |
267 reinterpret_cast<const char*>(data.empty() ? nullptr : &data[0]); | |
268 client_->didReceiveData(this, fin, type_to_pass, data_to_pass, data.size()); | |
269 // |this| can be deleted here. | |
270 } | |
271 | |
272 void WebSocketHandleImpl::OnFlowControl(int64_t quota) { | |
273 DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) | |
274 << " OnFlowControl(" << quota << ")"; | |
275 if (!client_) | |
276 return; | |
277 | |
278 client_->didReceiveFlowControl(this, quota); | |
279 // |this| can be deleted here. | |
280 } | |
281 | |
282 void WebSocketHandleImpl::OnDropChannel(bool was_clean, | |
283 uint16_t code, | |
284 const std::string& reason) { | |
285 DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) | |
286 << " OnDropChannel(" << was_clean << ", " << code << ", " | |
287 << reason << ")"; | |
288 | |
289 WebSocketHandleClient* client = client_; | |
290 Disconnect(); | |
291 if (!client) | |
292 return; | |
293 | |
294 client->didClose(this, was_clean, code, WebString::fromUTF8(reason)); | |
295 // |this| can be deleted here. | |
296 } | |
297 | |
298 void WebSocketHandleImpl::OnClosingHandshake() { | |
299 DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) | |
300 << " OnClosingHandshake()"; | |
301 if (!client_) | |
302 return; | |
303 | |
304 client_->didStartClosingHandshake(this); | |
305 // |this| can be deleted here. | |
306 } | |
307 | |
308 } // namespace content | |
OLD | NEW |