Index: content/renderer/websockethandle_impl.cc |
diff --git a/content/renderer/websockethandle_impl.cc b/content/renderer/websockethandle_impl.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..92e8b5fad44eedbd95f96e1804db507ab4d351ed |
--- /dev/null |
+++ b/content/renderer/websockethandle_impl.cc |
@@ -0,0 +1,308 @@ |
+// Copyright 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "content/renderer/websockethandle_impl.h" |
+ |
+#include <stdint.h> |
+#include <string.h> |
+ |
+#include <string> |
+#include <utility> |
+ |
+#include "base/logging.h" |
+#include "base/strings/string_util.h" |
+#include "third_party/WebKit/public/platform/InterfaceProvider.h" |
+#include "third_party/WebKit/public/platform/Platform.h" |
+#include "third_party/WebKit/public/platform/WebSecurityOrigin.h" |
+#include "third_party/WebKit/public/platform/WebString.h" |
+#include "third_party/WebKit/public/platform/WebURL.h" |
+#include "third_party/WebKit/public/platform/WebVector.h" |
+#include "third_party/WebKit/public/platform/modules/websockets/WebSocketHandle.h" |
+#include "third_party/WebKit/public/platform/modules/websockets/WebSocketHandleClient.h" |
+#include "third_party/WebKit/public/platform/modules/websockets/WebSocketHandshakeRequestInfo.h" |
+#include "third_party/WebKit/public/platform/modules/websockets/WebSocketHandshakeResponseInfo.h" |
+#include "url/gurl.h" |
+#include "url/origin.h" |
+ |
+using blink::WebSecurityOrigin; |
+using blink::WebSocketHandle; |
+using blink::WebSocketHandleClient; |
+using blink::WebString; |
+using blink::WebURL; |
+using blink::WebVector; |
+ |
+namespace content { |
+namespace { |
+ |
+const uint16_t kAbnormalShutdownOpCode = 1006; |
+ |
+} // namespace |
+ |
+WebSocketHandleImpl::WebSocketHandleImpl( |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) |
+ : client_(nullptr), |
+ client_binding_(this), |
+ task_runner_(std::move(task_runner)), |
+ did_initialize_(false) { |
+ DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) |
+ << " created"; |
+} |
+ |
+void WebSocketHandleImpl::Initialize( |
+ blink::InterfaceProvider* interface_provider) { |
+ DCHECK(!websocket_); |
+ DCHECK(!did_initialize_); |
+ |
+ interface_provider->getInterface(mojo::GetProxy(&websocket_)); |
+ |
+ websocket_.set_connection_error_handler( |
+ base::Bind(&WebSocketHandleImpl::OnConnectionError, |
+ base::Unretained(this))); |
+ did_initialize_ = true; |
+} |
+ |
+void WebSocketHandleImpl::connect(const WebURL& url, |
+ const WebVector<WebString>& protocols, |
+ const WebSecurityOrigin& origin, |
+ const WebURL& first_party_for_cookies, |
+ const WebString& user_agent_override, |
+ WebSocketHandleClient* client) { |
+ DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) |
+ << " Connect(" << url.string().utf8() << ", " |
+ << origin.toString().utf8() << ")"; |
+ |
+ // It is insufficient to test if websocket_ is non-null as Disconnect() sets |
+ // websocket_ to null. |
+ if (!did_initialize_) |
+ Initialize(blink::Platform::current()->interfaceProvider()); |
+ |
+ DCHECK(websocket_); |
+ |
+ DCHECK(!client_); |
+ DCHECK(client); |
+ client_ = client; |
+ |
+ std::vector<std::string> protocols_to_pass(protocols.size()); |
+ for (size_t i = 0; i < protocols.size(); ++i) |
+ protocols_to_pass[i] = protocols[i].utf8(); |
+ |
+ websocket_->AddChannelRequest( |
+ url, protocols_to_pass, origin, first_party_for_cookies, |
+ user_agent_override.latin1(), |
+ client_binding_.CreateInterfacePtrAndBind(task_runner_)); |
+} |
+ |
+void WebSocketHandleImpl::send(bool fin, |
+ WebSocketHandle::MessageType type, |
+ const char* data, |
+ size_t size) { |
+ DCHECK(websocket_); |
+ |
+ mojom::WebSocketMessageType type_to_pass; |
+ switch (type) { |
+ case WebSocketHandle::MessageTypeContinuation: |
+ type_to_pass = mojom::WebSocketMessageType::CONTINUATION; |
+ break; |
+ case WebSocketHandle::MessageTypeText: |
+ type_to_pass = mojom::WebSocketMessageType::TEXT; |
+ break; |
+ case WebSocketHandle::MessageTypeBinary: |
+ type_to_pass = mojom::WebSocketMessageType::BINARY; |
+ break; |
+ default: |
+ NOTREACHED(); |
+ return; |
+ } |
+ |
+ DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) |
+ << " Send(" << fin << ", " << type_to_pass << ", " |
+ << "(data size = " << size << "))"; |
+ |
+ std::vector<uint8_t> data_to_pass(size); |
+ std::copy(data, data + size, data_to_pass.begin()); |
+ |
+ websocket_->SendFrame(fin, type_to_pass, data_to_pass); |
+} |
+ |
+void WebSocketHandleImpl::flowControl(int64_t quota) { |
+ DCHECK(websocket_); |
+ |
+ DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) |
+ << " FlowControl(" << quota << ")"; |
+ |
+ websocket_->SendFlowControl(quota); |
+} |
+ |
+void WebSocketHandleImpl::close(unsigned short code, const WebString& reason) { |
+ DCHECK(websocket_); |
+ |
+ std::string reason_to_pass = reason.utf8(); |
+ |
+ DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) |
+ << " Close(" << code << ", " << reason_to_pass << ")"; |
+ |
+ websocket_->StartClosingHandshake(code, reason_to_pass); |
+} |
+ |
+WebSocketHandleImpl::~WebSocketHandleImpl() { |
+ DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) |
+ << " deleted"; |
+ |
+ if (websocket_) |
+ websocket_->StartClosingHandshake(kAbnormalShutdownOpCode, std::string()); |
+} |
+ |
+void WebSocketHandleImpl::Disconnect() { |
+ websocket_.reset(); |
+ client_ = nullptr; |
+} |
+ |
+void WebSocketHandleImpl::OnConnectionError() { |
+ if (!blink::Platform::current()) { |
+ // In the renderrer shutdown sequence, mojo channels are destructed and this |
+ // function is called. On the other hand, blink objects became invalid |
+ // *silently*, which means we must not touch |*client_| any more. |
+ // TODO(yhirano): Remove this code once the shutdown sequence is fixed. |
+ Disconnect(); |
+ return; |
+ } |
+ |
+ // Our connection to the WebSocket was dropped. This could be due to |
+ // exceeding the maximum number of concurrent websockets from this process. |
+ |
+ // TODO(darin): This error message is overly specific. We don't know for sure |
+ // that this is the only reason we'd get here. This should be more generic or |
+ // we should figure out how to make it more specific. |
+ OnFailChannel("Error in connection establishment: " |
+ "net::ERR_INSUFFICIENT_RESOURCES"); |
+} |
+ |
+void WebSocketHandleImpl::OnFailChannel(const std::string& message) { |
+ DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) |
+ << " OnFailChannel(" << message << ")"; |
+ |
+ WebSocketHandleClient* client = client_; |
+ Disconnect(); |
+ if (!client) |
+ return; |
+ |
+ client->didFail(this, WebString::fromUTF8(message)); |
+ // |this| can be deleted here. |
+} |
+ |
+void WebSocketHandleImpl::OnStartOpeningHandshake( |
+ mojom::WebSocketHandshakeRequestPtr request) { |
+ DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) |
+ << " OnStartOpeningHandshake(" << request->url << ")"; |
+ // All strings are already encoded to ASCII in the browser. |
+ blink::WebSocketHandshakeRequestInfo request_to_pass; |
+ request_to_pass.setURL(WebURL(request->url)); |
+ for (size_t i = 0; i < request->headers.size(); ++i) { |
+ const mojom::HttpHeaderPtr& header = request->headers[i]; |
+ request_to_pass.addHeaderField(WebString::fromLatin1(header->name), |
+ WebString::fromLatin1(header->value)); |
+ } |
+ request_to_pass.setHeadersText(WebString::fromLatin1(request->headers_text)); |
+ client_->didStartOpeningHandshake(this, request_to_pass); |
+} |
+ |
+void WebSocketHandleImpl::OnFinishOpeningHandshake( |
+ mojom::WebSocketHandshakeResponsePtr response) { |
+ DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) |
+ << " OnFinishOpeningHandshake(" << response->url << ")"; |
+ |
+ // All strings are already encoded to ASCII in the browser. |
+ blink::WebSocketHandshakeResponseInfo response_to_pass; |
+ response_to_pass.setStatusCode(response->status_code); |
+ response_to_pass.setStatusText(WebString::fromLatin1(response->status_text)); |
+ for (size_t i = 0; i < response->headers.size(); ++i) { |
+ const mojom::HttpHeaderPtr& header = response->headers[i]; |
+ response_to_pass.addHeaderField(WebString::fromLatin1(header->name), |
+ WebString::fromLatin1(header->value)); |
+ } |
+ response_to_pass.setHeadersText( |
+ WebString::fromLatin1(response->headers_text)); |
+ client_->didFinishOpeningHandshake(this, response_to_pass); |
+} |
+ |
+void WebSocketHandleImpl::OnAddChannelResponse(const std::string& protocol, |
+ const std::string& extensions) { |
+ DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) |
+ << " OnAddChannelResponse(" |
+ << protocol << ", " << extensions << ")"; |
+ |
+ if (!client_) |
+ return; |
+ |
+ client_->didConnect(this, |
+ WebString::fromUTF8(protocol), |
+ WebString::fromUTF8(extensions)); |
+ // |this| can be deleted here. |
+} |
+ |
+void WebSocketHandleImpl::OnDataFrame(bool fin, |
+ mojom::WebSocketMessageType type, |
+ const std::vector<uint8_t>& data) { |
+ DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) |
+ << " OnDataFrame(" << fin << ", " << type << ", " |
+ << "(data size = " << data.size() << "))"; |
+ if (!client_) |
+ return; |
+ |
+ WebSocketHandle::MessageType type_to_pass = |
+ WebSocketHandle::MessageTypeContinuation; |
+ switch (type) { |
+ case mojom::WebSocketMessageType::CONTINUATION: |
+ type_to_pass = WebSocketHandle::MessageTypeContinuation; |
+ break; |
+ case mojom::WebSocketMessageType::TEXT: |
+ type_to_pass = WebSocketHandle::MessageTypeText; |
+ break; |
+ case mojom::WebSocketMessageType::BINARY: |
+ type_to_pass = WebSocketHandle::MessageTypeBinary; |
+ break; |
+ } |
+ const char* data_to_pass = |
+ reinterpret_cast<const char*>(data.empty() ? nullptr : &data[0]); |
+ client_->didReceiveData(this, fin, type_to_pass, data_to_pass, data.size()); |
+ // |this| can be deleted here. |
+} |
+ |
+void WebSocketHandleImpl::OnFlowControl(int64_t quota) { |
+ DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) |
+ << " OnFlowControl(" << quota << ")"; |
+ if (!client_) |
+ return; |
+ |
+ client_->didReceiveFlowControl(this, quota); |
+ // |this| can be deleted here. |
+} |
+ |
+void WebSocketHandleImpl::OnDropChannel(bool was_clean, |
+ uint16_t code, |
+ const std::string& reason) { |
+ DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) |
+ << " OnDropChannel(" << was_clean << ", " << code << ", " |
+ << reason << ")"; |
+ |
+ WebSocketHandleClient* client = client_; |
+ Disconnect(); |
+ if (!client) |
+ return; |
+ |
+ client->didClose(this, was_clean, code, WebString::fromUTF8(reason)); |
+ // |this| can be deleted here. |
+} |
+ |
+void WebSocketHandleImpl::OnClosingHandshake() { |
+ DVLOG(1) << "WebSocketHandleImpl @" << reinterpret_cast<void*>(this) |
+ << " OnClosingHandshake()"; |
+ if (!client_) |
+ return; |
+ |
+ client_->didStartClosingHandshake(this); |
+ // |this| can be deleted here. |
+} |
+ |
+} // namespace content |