1// Copyright 2014 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 "mojo/services/html_viewer/websockethandle_impl.h"
6
7#include <vector>
8
9#include "base/bind.h"
10#include "base/memory/scoped_vector.h"
11#include "mojo/services/html_viewer/blink_basic_type_converters.h"
12#include "mojo/services/public/cpp/network/web_socket_read_queue.h"
13#include "mojo/services/public/cpp/network/web_socket_write_queue.h"
14#include "mojo/services/public/interfaces/network/network_service.mojom.h"
15#include "third_party/WebKit/public/platform/WebSerializedOrigin.h"
16#include "third_party/WebKit/public/platform/WebSocketHandleClient.h"
17#include "third_party/WebKit/public/platform/WebString.h"
18#include "third_party/WebKit/public/platform/WebURL.h"
19#include "third_party/WebKit/public/platform/WebVector.h"
20
21using blink::WebSerializedOrigin;
22using blink::WebSocketHandle;
23using blink::WebSocketHandleClient;
24using blink::WebString;
25using blink::WebURL;
26using blink::WebVector;
27
28namespace mojo {
29
30template<>
31struct TypeConverter<WebSocket::MessageType, WebSocketHandle::MessageType> {
32  static WebSocket::MessageType Convert(WebSocketHandle::MessageType type) {
33    DCHECK(type == WebSocketHandle::MessageTypeContinuation ||
34           type == WebSocketHandle::MessageTypeText ||
35           type == WebSocketHandle::MessageTypeBinary);
36    typedef WebSocket::MessageType MessageType;
37    COMPILE_ASSERT(
38        static_cast<MessageType>(WebSocketHandle::MessageTypeContinuation) ==
39            WebSocket::MESSAGE_TYPE_CONTINUATION,
40        enum_values_must_match_for_message_type);
41    COMPILE_ASSERT(
42        static_cast<MessageType>(WebSocketHandle::MessageTypeText) ==
43            WebSocket::MESSAGE_TYPE_TEXT,
44        enum_values_must_match_for_message_type);
45    COMPILE_ASSERT(
46        static_cast<MessageType>(WebSocketHandle::MessageTypeBinary) ==
47            WebSocket::MESSAGE_TYPE_BINARY,
48        enum_values_must_match_for_message_type);
49    return static_cast<WebSocket::MessageType>(type);
50  }
51};
52
53template<>
54struct TypeConverter<WebSocketHandle::MessageType, WebSocket::MessageType> {
55  static WebSocketHandle::MessageType Convert(WebSocket::MessageType type) {
56    DCHECK(type == WebSocket::MESSAGE_TYPE_CONTINUATION ||
57           type == WebSocket::MESSAGE_TYPE_TEXT ||
58           type == WebSocket::MESSAGE_TYPE_BINARY);
59    return static_cast<WebSocketHandle::MessageType>(type);
60  }
61};
62
63// This class forms a bridge from the mojo WebSocketClient interface and the
64// Blink WebSocketHandleClient interface.
65class WebSocketClientImpl : public InterfaceImpl<WebSocketClient> {
66 public:
67  explicit WebSocketClientImpl(WebSocketHandleImpl* handle,
68                               blink::WebSocketHandleClient* client)
69      : handle_(handle), client_(client) {}
70  virtual ~WebSocketClientImpl() {}
71
72 private:
73  // WebSocketClient methods:
74  virtual void DidConnect(bool fail,
75                          const String& selected_subprotocol,
76                          const String& extensions,
77                          ScopedDataPipeConsumerHandle receive_stream)
78      OVERRIDE {
79    blink::WebSocketHandleClient* client = client_;
80    WebSocketHandleImpl* handle = handle_;
81    receive_stream_ = receive_stream.Pass();
82    read_queue_.reset(new WebSocketReadQueue(receive_stream_.get()));
83    if (fail)
84      handle->Disconnect();  // deletes |this|
85    client->didConnect(handle,
86                       fail,
87                       selected_subprotocol.To<WebString>(),
88                       extensions.To<WebString>());
89    // |handle| can be deleted here.
90  }
91
92  virtual void DidReceiveData(bool fin,
93                              WebSocket::MessageType type,
94                              uint32_t num_bytes) OVERRIDE {
95    read_queue_->Read(num_bytes,
96                      base::Bind(&WebSocketClientImpl::DidReadFromReceiveStream,
97                                 base::Unretained(this),
98                                 fin, type, num_bytes));
99  }
100
101  virtual void DidReceiveFlowControl(int64_t quota) OVERRIDE {
102    client_->didReceiveFlowControl(handle_, quota);
103    // |handle| can be deleted here.
104  }
105
106  virtual void DidFail(const String& message) OVERRIDE {
107    blink::WebSocketHandleClient* client = client_;
108    WebSocketHandleImpl* handle = handle_;
109    handle->Disconnect();  // deletes |this|
110    client->didFail(handle, message.To<WebString>());
111    // |handle| can be deleted here.
112  }
113
114  virtual void DidClose(bool was_clean,
115                        uint16_t code,
116                        const String& reason) OVERRIDE {
117    blink::WebSocketHandleClient* client = client_;
118    WebSocketHandleImpl* handle = handle_;
119    handle->Disconnect();  // deletes |this|
120    client->didClose(handle, was_clean, code, reason.To<WebString>());
121    // |handle| can be deleted here.
122  }
123
124  void DidReadFromReceiveStream(bool fin,
125                                WebSocket::MessageType type,
126                                uint32_t num_bytes,
127                                const char* data) {
128    client_->didReceiveData(handle_,
129                            fin,
130                            ConvertTo<WebSocketHandle::MessageType>(type),
131                            data,
132                            num_bytes);
133    // |handle_| can be deleted here.
134  }
135
136  // |handle_| owns this object, so it is guaranteed to outlive us.
137  WebSocketHandleImpl* handle_;
138  blink::WebSocketHandleClient* client_;
139  ScopedDataPipeConsumerHandle receive_stream_;
140  scoped_ptr<WebSocketReadQueue> read_queue_;
141
142  DISALLOW_COPY_AND_ASSIGN(WebSocketClientImpl);
143};
144
145WebSocketHandleImpl::WebSocketHandleImpl(NetworkService* network_service)
146    : did_close_(false) {
147  network_service->CreateWebSocket(Get(&web_socket_));
148}
149
150WebSocketHandleImpl::~WebSocketHandleImpl() {
151  if (!did_close_) {
152    // The connection is abruptly disconnected by the renderer without
153    // closing handshake.
154    web_socket_->Close(WebSocket::kAbnormalCloseCode, String());
155  }
156}
157
158void WebSocketHandleImpl::connect(const WebURL& url,
159                                  const WebVector<WebString>& protocols,
160                                  const WebSerializedOrigin& origin,
161                                  WebSocketHandleClient* client) {
162  client_.reset(new WebSocketClientImpl(this, client));
163  WebSocketClientPtr client_ptr;
164  // TODO(mpcomplete): Is this the right ownership model? Or should mojo own
165  // |client_|?
166  WeakBindToProxy(client_.get(), &client_ptr);
167
168  DataPipe data_pipe;
169  send_stream_ = data_pipe.producer_handle.Pass();
170  write_queue_.reset(new WebSocketWriteQueue(send_stream_.get()));
171  web_socket_->Connect(url.string().utf8(),
172                       Array<String>::From(protocols),
173                       origin.string().utf8(),
174                       data_pipe.consumer_handle.Pass(),
175                       client_ptr.Pass());
176}
177
178void WebSocketHandleImpl::send(bool fin,
179                               WebSocketHandle::MessageType type,
180                               const char* data,
181                               size_t size) {
182  if (!client_)
183    return;
184
185  uint32_t size32 = static_cast<uint32_t>(size);
186  write_queue_->Write(
187      data, size32,
188      base::Bind(&WebSocketHandleImpl::DidWriteToSendStream,
189                 base::Unretained(this),
190                 fin, type, size32));
191}
192
193void WebSocketHandleImpl::flowControl(int64_t quota) {
194  if (!client_)
195    return;
196
197  web_socket_->FlowControl(quota);
198}
199
200void WebSocketHandleImpl::close(unsigned short code, const WebString& reason) {
201  web_socket_->Close(code, reason.utf8());
202}
203
204void WebSocketHandleImpl::DidWriteToSendStream(
205    bool fin,
206    WebSocketHandle::MessageType type,
207    uint32_t num_bytes,
208    const char* data) {
209  web_socket_->Send(fin, ConvertTo<WebSocket::MessageType>(type), num_bytes);
210}
211
212void WebSocketHandleImpl::Disconnect() {
213  did_close_ = true;
214  client_.reset();
215}
216
217}  // namespace mojo
218