2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "modules/websockets/NewWebSocketChannelImpl.h"
34 #include "core/dom/Document.h"
35 #include "core/dom/ExecutionContext.h"
36 #include "core/fileapi/FileReaderLoader.h"
37 #include "core/fileapi/FileReaderLoaderClient.h"
38 #include "core/inspector/InspectorInstrumentation.h"
39 #include "core/loader/UniqueIdentifier.h"
40 #include "modules/websockets/WebSocketChannelClient.h"
41 #include "modules/websockets/WebSocketFrame.h"
42 #include "platform/Logging.h"
43 #include "platform/weborigin/SecurityOrigin.h"
44 #include "public/platform/Platform.h"
45 #include "public/platform/WebSocketHandshakeRequestInfo.h"
46 #include "public/platform/WebSocketHandshakeResponseInfo.h"
47 #include "public/platform/WebString.h"
48 #include "public/platform/WebURL.h"
49 #include "public/platform/WebVector.h"
51 using blink::WebSocketHandle;
55 class NewWebSocketChannelImpl::BlobLoader FINAL : public FileReaderLoaderClient {
57 BlobLoader(PassRefPtr<BlobDataHandle>, NewWebSocketChannelImpl*);
58 virtual ~BlobLoader() { }
62 // FileReaderLoaderClient functions.
63 virtual void didStartLoading() OVERRIDE { }
64 virtual void didReceiveData() OVERRIDE { }
65 virtual void didFinishLoading() OVERRIDE;
66 virtual void didFail(FileError::ErrorCode) OVERRIDE;
69 NewWebSocketChannelImpl* m_channel;
70 FileReaderLoader m_loader;
73 NewWebSocketChannelImpl::BlobLoader::BlobLoader(PassRefPtr<BlobDataHandle> blobDataHandle, NewWebSocketChannelImpl* channel)
75 , m_loader(FileReaderLoader::ReadAsArrayBuffer, this)
77 m_loader.start(channel->executionContext(), blobDataHandle);
80 void NewWebSocketChannelImpl::BlobLoader::cancel()
83 // didFail will be called immediately.
84 // |this| is deleted here.
87 void NewWebSocketChannelImpl::BlobLoader::didFinishLoading()
89 m_channel->didFinishLoadingBlob(m_loader.arrayBufferResult());
90 // |this| is deleted here.
93 void NewWebSocketChannelImpl::BlobLoader::didFail(FileError::ErrorCode errorCode)
95 m_channel->didFailLoadingBlob(errorCode);
96 // |this| is deleted here.
99 NewWebSocketChannelImpl::NewWebSocketChannelImpl(ExecutionContext* context, WebSocketChannelClient* client, const String& sourceURL, unsigned lineNumber)
100 : ContextLifecycleObserver(context)
101 , m_handle(adoptPtr(blink::Platform::current()->createWebSocketHandle()))
105 , m_receivedDataSizeForFlowControl(receivedDataSizeForFlowControlHighWaterMark * 2) // initial quota
106 , m_bufferedAmount(0)
107 , m_sentSizeOfTopMessage(0)
108 , m_sourceURLAtConstruction(sourceURL)
109 , m_lineNumberAtConstruction(lineNumber)
111 if (context->isDocument() && toDocument(context)->page())
112 m_identifier = createUniqueIdentifier();
115 NewWebSocketChannelImpl::~NewWebSocketChannelImpl()
117 abortAsyncOperations();
120 void NewWebSocketChannelImpl::connect(const KURL& url, const String& protocol)
122 WTF_LOG(Network, "NewWebSocketChannelImpl %p connect()", this);
126 Vector<String> protocols;
127 // Avoid placing an empty token in the Vector when the protocol string is
129 if (!protocol.isEmpty()) {
130 // Since protocol is already verified and escaped, we can simply split
132 protocol.split(", ", true, protocols);
134 blink::WebVector<blink::WebString> webProtocols(protocols.size());
135 for (size_t i = 0; i < protocols.size(); ++i) {
136 webProtocols[i] = protocols[i];
138 String origin = executionContext()->securityOrigin()->toString();
139 m_handle->connect(url, webProtocols, origin, this);
140 flowControlIfNecessary();
142 InspectorInstrumentation::didCreateWebSocket(document(), m_identifier, url, protocol);
145 String NewWebSocketChannelImpl::subprotocol()
147 WTF_LOG(Network, "NewWebSocketChannelImpl %p subprotocol()", this);
148 return m_subprotocol;
151 String NewWebSocketChannelImpl::extensions()
153 WTF_LOG(Network, "NewWebSocketChannelImpl %p extensions()", this);
157 WebSocketChannel::SendResult NewWebSocketChannelImpl::send(const String& message)
159 WTF_LOG(Network, "NewWebSocketChannelImpl %p sendText(%s)", this, message.utf8().data());
161 // FIXME: Change the inspector API to show the entire message instead
162 // of individual frames.
163 CString data = message.utf8();
164 WebSocketFrame frame(WebSocketFrame::OpCodeText, data.data(), data.length(), WebSocketFrame::Final | WebSocketFrame::Masked);
165 InspectorInstrumentation::didSendWebSocketFrame(document(), m_identifier, frame.opCode, frame.masked, frame.payload, frame.payloadLength);
167 m_messages.append(Message(message));
172 WebSocketChannel::SendResult NewWebSocketChannelImpl::send(PassRefPtr<BlobDataHandle> blobDataHandle)
174 WTF_LOG(Network, "NewWebSocketChannelImpl %p sendBlob(%s, %s, %llu)", this, blobDataHandle->uuid().utf8().data(), blobDataHandle->type().utf8().data(), blobDataHandle->size());
176 // FIXME: Change the inspector API to show the entire message instead
177 // of individual frames.
178 // FIXME: We can't access the data here.
179 // Since Binary data are not displayed in Inspector, this does not
180 // affect actual behavior.
181 WebSocketFrame frame(WebSocketFrame::OpCodeBinary, "", 0, WebSocketFrame::Final | WebSocketFrame::Masked);
182 InspectorInstrumentation::didSendWebSocketFrame(document(), m_identifier, frame.opCode, frame.masked, frame.payload, frame.payloadLength);
184 m_messages.append(Message(blobDataHandle));
189 WebSocketChannel::SendResult NewWebSocketChannelImpl::send(const ArrayBuffer& buffer, unsigned byteOffset, unsigned byteLength)
191 WTF_LOG(Network, "NewWebSocketChannelImpl %p sendArrayBuffer(%p, %u, %u)", this, buffer.data(), byteOffset, byteLength);
193 // FIXME: Change the inspector API to show the entire message instead
194 // of individual frames.
195 WebSocketFrame frame(WebSocketFrame::OpCodeBinary, static_cast<const char*>(buffer.data()) + byteOffset, byteLength, WebSocketFrame::Final | WebSocketFrame::Masked);
196 InspectorInstrumentation::didSendWebSocketFrame(document(), m_identifier, frame.opCode, frame.masked, frame.payload, frame.payloadLength);
198 // buffer.slice copies its contents.
199 m_messages.append(buffer.slice(byteOffset, byteOffset + byteLength));
204 unsigned long NewWebSocketChannelImpl::bufferedAmount() const
206 WTF_LOG(Network, "NewWebSocketChannelImpl %p bufferedAmount()", this);
207 return m_bufferedAmount;
210 void NewWebSocketChannelImpl::close(int code, const String& reason)
212 WTF_LOG(Network, "NewWebSocketChannelImpl %p close(%d, %s)", this, code, reason.utf8().data());
214 m_handle->close(static_cast<unsigned short>(code), reason);
217 void NewWebSocketChannelImpl::fail(const String& reason, MessageLevel level, const String& sourceURL, unsigned lineNumber)
219 WTF_LOG(Network, "NewWebSocketChannelImpl %p fail(%s)", this, reason.utf8().data());
220 // m_handle and m_client can be null here.
223 InspectorInstrumentation::didReceiveWebSocketFrameError(document(), m_identifier, reason);
224 const String message = "WebSocket connection to '" + m_url.elidedString() + "' failed: " + reason;
225 executionContext()->addConsoleMessage(JSMessageSource, level, message, sourceURL, lineNumber);
228 m_client->didReceiveMessageError();
229 // |reason| is only for logging and should not be provided for scripts,
230 // hence close reason must be empty.
231 handleDidClose(false, CloseEventCodeAbnormalClosure, String());
232 // handleDidClose may delete this object.
235 void NewWebSocketChannelImpl::disconnect()
237 WTF_LOG(Network, "NewWebSocketChannelImpl %p disconnect()", this);
239 InspectorInstrumentation::didCloseWebSocket(document(), m_identifier);
240 abortAsyncOperations();
246 void NewWebSocketChannelImpl::suspend()
248 WTF_LOG(Network, "NewWebSocketChannelImpl %p suspend()", this);
251 void NewWebSocketChannelImpl::resume()
253 WTF_LOG(Network, "NewWebSocketChannelImpl %p resume()", this);
256 NewWebSocketChannelImpl::Message::Message(const String& text)
257 : type(MessageTypeText)
258 , text(text.utf8(StrictUTF8ConversionReplacingUnpairedSurrogatesWithFFFD)) { }
260 NewWebSocketChannelImpl::Message::Message(PassRefPtr<BlobDataHandle> blobDataHandle)
261 : type(MessageTypeBlob)
262 , blobDataHandle(blobDataHandle) { }
264 NewWebSocketChannelImpl::Message::Message(PassRefPtr<ArrayBuffer> arrayBuffer)
265 : type(MessageTypeArrayBuffer)
266 , arrayBuffer(arrayBuffer) { }
268 void NewWebSocketChannelImpl::sendInternal()
271 unsigned long bufferedAmount = m_bufferedAmount;
272 while (!m_messages.isEmpty() && m_sendingQuota > 0 && !m_blobLoader) {
274 const Message& message = m_messages.first();
275 switch (message.type) {
276 case MessageTypeText: {
277 WebSocketHandle::MessageType type =
278 m_sentSizeOfTopMessage ? WebSocketHandle::MessageTypeContinuation : WebSocketHandle::MessageTypeText;
279 size_t size = std::min(static_cast<size_t>(m_sendingQuota), message.text.length() - m_sentSizeOfTopMessage);
280 final = (m_sentSizeOfTopMessage + size == message.text.length());
281 m_handle->send(final, type, message.text.data() + m_sentSizeOfTopMessage, size);
282 m_sentSizeOfTopMessage += size;
283 m_sendingQuota -= size;
286 case MessageTypeBlob:
287 ASSERT(!m_blobLoader);
288 m_blobLoader = adoptPtr(new BlobLoader(message.blobDataHandle, this));
290 case MessageTypeArrayBuffer: {
291 WebSocketHandle::MessageType type =
292 m_sentSizeOfTopMessage ? WebSocketHandle::MessageTypeContinuation : WebSocketHandle::MessageTypeBinary;
293 size_t size = std::min(static_cast<size_t>(m_sendingQuota), message.arrayBuffer->byteLength() - m_sentSizeOfTopMessage);
294 final = (m_sentSizeOfTopMessage + size == message.arrayBuffer->byteLength());
295 m_handle->send(final, type, static_cast<const char*>(message.arrayBuffer->data()) + m_sentSizeOfTopMessage, size);
296 m_sentSizeOfTopMessage += size;
297 m_sendingQuota -= size;
302 m_messages.removeFirst();
303 m_sentSizeOfTopMessage = 0;
306 if (m_client && m_bufferedAmount != bufferedAmount) {
307 m_client->didUpdateBufferedAmount(m_bufferedAmount);
311 void NewWebSocketChannelImpl::flowControlIfNecessary()
313 if (!m_handle || m_receivedDataSizeForFlowControl < receivedDataSizeForFlowControlHighWaterMark) {
316 m_handle->flowControl(m_receivedDataSizeForFlowControl);
317 m_receivedDataSizeForFlowControl = 0;
320 void NewWebSocketChannelImpl::abortAsyncOperations()
323 m_blobLoader->cancel();
324 m_blobLoader.clear();
328 void NewWebSocketChannelImpl::handleDidClose(bool wasClean, unsigned short code, const String& reason)
331 abortAsyncOperations();
335 WebSocketChannelClient* client = m_client;
337 WebSocketChannelClient::ClosingHandshakeCompletionStatus status =
338 wasClean ? WebSocketChannelClient::ClosingHandshakeComplete : WebSocketChannelClient::ClosingHandshakeIncomplete;
339 client->didClose(m_bufferedAmount, status, code, reason);
340 // client->didClose may delete this object.
343 Document* NewWebSocketChannelImpl::document()
345 ASSERT(m_identifier);
346 ExecutionContext* context = executionContext();
347 ASSERT(context->isDocument());
348 return toDocument(context);
351 void NewWebSocketChannelImpl::didConnect(WebSocketHandle* handle, bool fail, const blink::WebString& selectedProtocol, const blink::WebString& extensions)
353 WTF_LOG(Network, "NewWebSocketChannelImpl %p didConnect(%p, %d, %s, %s)", this, handle, fail, selectedProtocol.utf8().data(), extensions.utf8().data());
355 ASSERT(handle == m_handle);
358 failAsError("Cannot connect to " + m_url.string() + ".");
359 // failAsError may delete this object.
362 m_subprotocol = selectedProtocol;
363 m_extensions = extensions;
364 m_client->didConnect();
367 void NewWebSocketChannelImpl::didStartOpeningHandshake(WebSocketHandle* handle, const blink::WebSocketHandshakeRequestInfo& request)
369 WTF_LOG(Network, "NewWebSocketChannelImpl %p didStartOpeningHandshake(%p)", this, handle);
371 InspectorInstrumentation::willSendWebSocketHandshakeRequest(document(), m_identifier, request.toCoreRequest());
374 void NewWebSocketChannelImpl::didFinishOpeningHandshake(WebSocketHandle* handle, const blink::WebSocketHandshakeResponseInfo& response)
376 WTF_LOG(Network, "NewWebSocketChannelImpl %p didFinishOpeningHandshake(%p)", this, handle);
378 InspectorInstrumentation::didReceiveWebSocketHandshakeResponse(document(), m_identifier, response.toCoreResponse());
381 void NewWebSocketChannelImpl::didFail(WebSocketHandle* handle, const blink::WebString& message)
383 WTF_LOG(Network, "NewWebSocketChannelImpl %p didFail(%p, %s)", this, handle, message.utf8().data());
384 // This function is called when the browser is required to fail the
385 // WebSocketConnection. Hence we fail this channel by calling
386 // |this->failAsError| function.
387 failAsError(message);
388 // |this| may be deleted.
391 void NewWebSocketChannelImpl::didReceiveData(WebSocketHandle* handle, bool fin, WebSocketHandle::MessageType type, const char* data, size_t size)
393 WTF_LOG(Network, "NewWebSocketChannelImpl %p didReceiveData(%p, %d, %d, (%p, %zu))", this, handle, fin, type, data, size);
395 ASSERT(handle == m_handle);
397 // Non-final frames cannot be empty.
400 case WebSocketHandle::MessageTypeText:
401 ASSERT(m_receivingMessageData.isEmpty());
402 m_receivingMessageTypeIsText = true;
404 case WebSocketHandle::MessageTypeBinary:
405 ASSERT(m_receivingMessageData.isEmpty());
406 m_receivingMessageTypeIsText = false;
408 case WebSocketHandle::MessageTypeContinuation:
409 ASSERT(!m_receivingMessageData.isEmpty());
413 m_receivingMessageData.append(data, size);
414 m_receivedDataSizeForFlowControl += size;
415 flowControlIfNecessary();
420 // FIXME: Change the inspector API to show the entire message instead
421 // of individual frames.
422 WebSocketFrame::OpCode opcode = m_receivingMessageTypeIsText ? WebSocketFrame::OpCodeText : WebSocketFrame::OpCodeBinary;
423 WebSocketFrame frame(opcode, m_receivingMessageData.data(), m_receivingMessageData.size(), WebSocketFrame::Final);
424 InspectorInstrumentation::didReceiveWebSocketFrame(document(), m_identifier, frame.opCode, frame.masked, frame.payload, frame.payloadLength);
426 if (m_receivingMessageTypeIsText) {
427 String message = m_receivingMessageData.isEmpty() ? emptyString() : String::fromUTF8(m_receivingMessageData.data(), m_receivingMessageData.size());
428 m_receivingMessageData.clear();
429 if (message.isNull()) {
430 failAsError("Could not decode a text frame as UTF-8.");
431 // failAsError may delete this object.
433 m_client->didReceiveMessage(message);
436 OwnPtr<Vector<char> > binaryData = adoptPtr(new Vector<char>);
437 binaryData->swap(m_receivingMessageData);
438 m_client->didReceiveBinaryData(binaryData.release());
442 void NewWebSocketChannelImpl::didClose(WebSocketHandle* handle, bool wasClean, unsigned short code, const blink::WebString& reason)
444 WTF_LOG(Network, "NewWebSocketChannelImpl %p didClose(%p, %d, %u, %s)", this, handle, wasClean, code, String(reason).utf8().data());
448 InspectorInstrumentation::didCloseWebSocket(document(), m_identifier);
452 handleDidClose(wasClean, code, reason);
453 // handleDidClose may delete this object.
456 void NewWebSocketChannelImpl::didReceiveFlowControl(WebSocketHandle* handle, int64_t quota)
458 WTF_LOG(Network, "NewWebSocketChannelImpl %p didReceiveFlowControl(%p, %ld)", this, handle, static_cast<long>(quota));
460 m_sendingQuota += quota;
464 void NewWebSocketChannelImpl::didStartClosingHandshake(WebSocketHandle* handle)
466 WTF_LOG(Network, "NewWebSocketChannelImpl %p didStartClosingHandshake(%p)", this, handle);
468 m_client->didStartClosingHandshake();
471 void NewWebSocketChannelImpl::didFinishLoadingBlob(PassRefPtr<ArrayBuffer> buffer)
473 m_blobLoader.clear();
475 // The loaded blob is always placed on m_messages[0].
476 ASSERT(m_messages.size() > 0 && m_messages.first().type == MessageTypeBlob);
477 // We replace it with the loaded blob.
478 m_messages.first() = Message(buffer);
482 void NewWebSocketChannelImpl::didFailLoadingBlob(FileError::ErrorCode errorCode)
484 m_blobLoader.clear();
485 if (errorCode == FileError::ABORT_ERR) {
486 // The error is caused by cancel().
489 // FIXME: Generate human-friendly reason message.
490 failAsError("Failed to load Blob: error code = " + String::number(errorCode));
491 // |this| can be deleted here.
494 } // namespace WebCore