Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(621)

Side by Side Diff: Source/modules/websockets/NewWebSocketChannelImpl.cpp

Issue 22914026: [ABANDONED] Introduce blink-side bridges for the new WebSocket implementation. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
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
13 * distribution.
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.
17 *
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.
29 */
30
31 #include "config.h"
32 #include "modules/websockets/NewWebSocketChannelImpl.h"
33
34 #include "bindings/v8/ScriptCallStackFactory.h"
35 #include "core/dom/ScriptExecutionContext.h"
36 #include "core/fileapi/Blob.h"
37 #include "core/fileapi/FileReaderLoader.h"
38 #include "core/inspector/InspectorInstrumentation.h"
39 #include "core/inspector/ScriptCallStack.h"
40 #include "core/loader/UniqueIdentifier.h"
41 #include "core/platform/Logging.h"
42 #include "modules/websockets/WebSocketChannel.h"
43 #include "modules/websockets/WebSocketChannelClient.h"
44 #include "public/platform/Platform.h"
45 #include "public/platform/WebData.h"
46 #include "public/platform/WebSocketHandle.h"
47 #include "public/platform/WebString.h"
48 #include "public/platform/WebURL.h"
49 #include "public/platform/WebVector.h"
50 #include "weborigin/SecurityOrigin.h"
51 #include "wtf/ArrayBuffer.h"
52 #include "wtf/OwnPtr.h"
53 #include "wtf/PassRefPtr.h"
54 #include "wtf/RefPtr.h"
55 #include "wtf/Vector.h"
56 #include "wtf/text/WTFString.h"
57
58 // FIXME: The following notifications are not implemented:
59 // InspectorInstrument::willSendWebSocketHandshake
60 // InspectorInstrument::didReceiveWebSocketHandshakeResponse
61 // InspectorInstrument::didReceiveWebSocketFrame
62 // InspectorInstrument::didSendWebSocketFrame
63
64 using WebKit::WebSocketHandle;
65
66 namespace WebCore {
67
68 namespace {
69
70 bool isClean(int code)
71 {
72 return code == WebSocketChannel::CloseEventCodeNormalClosure
73 || (WebSocketChannel::CloseEventCodeMinimumUserDefined <= code
74 && code <= WebSocketChannel::CloseEventCodeMaximumUserDefined);
75 }
76
77 } // namespace
78
79 NewWebSocketChannelImpl::NewWebSocketChannelImpl(ScriptExecutionContext* context , WebSocketChannelClient* client, const String& sourceURL, unsigned lineNumber)
80 : m_context(context)
81 , m_handle(adoptPtr(WebKit::Platform::current()->createWebSocketHandle()))
82 , m_client(client)
83 , m_identifier(0)
84 , m_state(NotConnected)
85 , m_sendingQuota(0)
86 , m_receivedDataSizeForFlowControl(0)
87 , m_bufferedAmount(0)
88 , m_sentSizeOfTopMessage(0)
89 , m_hasAlreadyFailed(false)
90 , m_isSuspended(false)
91 , m_resumeTimer(this, &NewWebSocketChannelImpl::resumeTimerFired)
92 , m_sourceURLAtConnection(sourceURL)
93 , m_lineNumberAtConnection(lineNumber)
94 {
95 if (context->isDocument() && toDocument(context)->page()) {
96 m_identifier = createUniqueIdentifier();
97 }
98 }
99
100 void NewWebSocketChannelImpl::connect(const KURL& url, const String& protocol)
101 {
102 ASSERT(m_state == NotConnected);
103 LOG(Network, "NewWebSocketChannelImpl %p connect()", this);
104 if (m_identifier) {
tyoshino (SeeGerritForStatus) 2013/08/28 05:26:03 ASSERT(m_context->isDocument()) If you think it's
yhirano 2013/08/28 06:29:09 Done.
105 InspectorInstrumentation::didCreateWebSocket(toDocument(m_context), m_id entifier, url, protocol);
106 }
107 m_state = Connecting;
108 m_url = url;
109 Vector<String> protocols;
110 // Since protocol is already verified and escaped, we can simply split it.
111 protocol.split(", ", true, protocols);
112 WebKit::WebVector<WebKit::WebString> webProtocols(protocols.size());
113 for (size_t i = 0; i < protocols.size(); ++i) {
114 webProtocols[i] = protocols[i];
115 }
116 m_handle->connect(url, webProtocols, m_context->securityOrigin()->toString() , this);
117
118 RefPtr<ScriptCallStack> callStack = createScriptCallStack(1, true);
119 if (callStack && callStack->size()) {
120 m_sourceURLAtConnection = callStack->at(0).sourceURL();
121 m_lineNumberAtConnection = callStack->at(0).lineNumber();
122 }
123 }
124
125 String NewWebSocketChannelImpl::subprotocol()
126 {
127 LOG(Network, "NewWebSocketChannelImpl %p subprotocol()", this);
128 return m_subprotocol;
129 }
130
131 String NewWebSocketChannelImpl::extensions()
132 {
133 LOG(Network, "NewWebSocketChannelImpl %p extensions()", this);
134 return m_extensions;
135 }
136
137 WebSocketChannel::SendResult NewWebSocketChannelImpl::send(const String& message )
138 {
139 LOG(Network, "NewWebSocketChannelImpl %p sendText(%s)", this, message.utf8() .data());
140 if (m_state != Open) {
141 return SendFail;
142 }
143 m_messages.append(Message(message));
144 sendInternal();
145 return SendSuccess;
146 }
147
148 WebSocketChannel::SendResult NewWebSocketChannelImpl::send(const Blob& blob)
149 {
150 LOG(Network, "NewWebSocketChannelImpl %p sendBlob()", this);
151 if (m_state != Open) {
152 return SendFail;
153 }
154 m_messages.append(Message(blob));
155 sendInternal();
156 return SendSuccess;
157 }
158
159 WebSocketChannel::SendResult NewWebSocketChannelImpl::send(const ArrayBuffer& bu ffer, unsigned byteOffset, unsigned byteLength)
160 {
161 LOG(Network, "NewWebSocketChannelImpl %p sendArrayBuffer(%p, %u, %u)", this, buffer.data(), byteOffset, byteLength);
162 if (m_state != Open) {
163 return SendFail;
164 }
165 // buffer.slice copies its contents.
166 m_messages.append(buffer.slice(byteOffset, byteOffset + byteLength));
167 sendInternal();
168 return SendSuccess;
169 }
170
171 unsigned long NewWebSocketChannelImpl::bufferedAmount() const
172 {
173 LOG(Network, "NewWebSocketChannelImpl %p bufferedAmount()", this);
174 return m_bufferedAmount;
175 }
176
177 void NewWebSocketChannelImpl::close(int code, const String& reason)
178 {
179 LOG(Network, "NewWebSocketChannelImpl %p close(%d, %s)", this, code, reason. utf8().data());
180 if (m_state == Closing || m_state == Closed) {
181 return;
182 }
183 ASSERT(m_handle);
184 if (m_state == Open) {
185 m_handle->close(static_cast<unsigned short>(code), reason);
186 }
187 m_state = Closing;
188 }
189
190 void NewWebSocketChannelImpl::fail(const String& reason, MessageLevel level, con st String& sourceURL, unsigned lineNumber)
191 {
192 LOG(Network, "NewWebSocketChannelImpl %p fail(%s)", this, reason.utf8().data ());
193 // m_handle and m_client can be null here.
194 const String message = "WebSocket connection to '" + m_url.elidedString() + "' failed: " + reason;
195 m_context->addConsoleMessage(JSMessageSource, level, message, sourceURL, lin eNumber);
196 if (m_identifier) {
tyoshino (SeeGerritForStatus) 2013/08/28 05:26:03 ASSERT(m_context->isDocument())
yhirano 2013/08/28 06:29:09 Done.
197 InspectorInstrumentation::didReceiveWebSocketFrameError(toDocument(m_con text), m_identifier, reason);
198 }
199
200 if (m_client && !m_hasAlreadyFailed) {
201 if (m_isSuspended) {
202 m_pendingEvents.append(PendingEvent(PendingEvent::DidReceiveError));
203 } else {
204 m_client->didReceiveMessageError();
tyoshino (SeeGerritForStatus) 2013/08/28 05:26:03 is it possible that this happens between resume()
yhirano 2013/08/28 06:29:09 You are right. I introduced m_isSuspendState inste
205 }
206 }
207 m_hasAlreadyFailed = true;
208 if (m_state != Closing && m_state != Closed) {
209 disconnect();
210 }
211 }
212
213 void NewWebSocketChannelImpl::disconnect()
214 {
215 LOG(Network, "NewWebSocketChannelImpl %p disconnect()", this);
216 if (m_state == Closed) {
217 return;
218 }
219 if (m_identifier) {
tyoshino (SeeGerritForStatus) 2013/08/28 05:26:03 ASSERT(m_context->isDocument())
yhirano 2013/08/28 06:29:09 Done.
220 InspectorInstrumentation::didCloseWebSocket(toDocument(m_context), m_ide ntifier);
221 }
222 ASSERT(m_handle);
223 if (m_state != Closing) {
224 m_handle->close(CloseEventCodeAbnormalClosure, "");
225 }
226 m_state = Closed;
227 m_handle = 0;
tyoshino (SeeGerritForStatus) 2013/08/28 05:26:03 is this ok? L409 expects m_handle to be non-NULL
yhirano 2013/08/28 06:29:09 Please see L403.
228 m_client = 0;
229 }
230
231 void NewWebSocketChannelImpl::suspend()
232 {
233 LOG(Network, "NewWebSocketChannelImpl %p suspend()", this);
234 m_isSuspended = true;
235 }
236
237 void NewWebSocketChannelImpl::resume()
238 {
239 LOG(Network, "NewWebSocketChannelImpl %p resume()", this);
240 m_isSuspended = false;
241 // Use a timer to finish this function quickly.
242 if (!m_resumeTimer.isActive()) {
243 // Protect this object until the timer fires.
244 ref();
245 m_resumeTimer.startOneShot(0);
246 }
247 }
248
249 NewWebSocketChannelImpl::Message::Message(const String& text)
250 : type(MessageTypeText)
251 , text(text.utf8(String::StrictConversionReplacingUnpairedSurrogatesWithFFFD )) { }
252
253 NewWebSocketChannelImpl::Message::Message(const Blob& blob)
254 : type(MessageTypeBlob)
255 , blob(Blob::create(blob.url(), blob.type(), blob.size())) { }
256
257 NewWebSocketChannelImpl::Message::Message(PassRefPtr<ArrayBuffer> arrayBuffer)
258 : type(MessageTypeArrayBuffer)
259 , arrayBuffer(arrayBuffer) { }
260
261 void NewWebSocketChannelImpl::sendInternal()
262 {
263 if (m_state != Open || m_blobLoader || !m_sendingQuota) {
264 return;
265 }
266 ASSERT(m_handle);
267 ASSERT(m_client);
268 unsigned long bufferedAmount = m_bufferedAmount;
269 while (!m_messages.isEmpty()) {
270 if (!m_sendingQuota) {
271 break;
272 }
273 bool final = false;
274 const Message& message = m_messages.first();
275 switch (message.type) {
276 case MessageTypeText: {
277 WebSocketHandle::MessageType type =
278 m_sentSizeOfTopMessage ? WebSocketHandle::MessageTypeContinuatio n : WebSocketHandle::MessageTypeText;
279 size_t size = std::min(static_cast<size_t>(m_sendingQuota), message. text.length() - m_sentSizeOfTopMessage);
280 final = (m_sendingQuota == size);
281 m_handle->send(type, message.text.data() + m_sentSizeOfTopMessage, s ize, final);
282 m_sentSizeOfTopMessage += size;
283 m_sendingQuota -= size;
284 break;
285 }
286 case MessageTypeBlob:
287 startLoadingBlob(*message.blob);
288 break;
289 case MessageTypeArrayBuffer: {
290 WebSocketHandle::MessageType type =
291 m_sentSizeOfTopMessage ? WebSocketHandle::MessageTypeContinuatio n : WebSocketHandle::MessageTypeBinary;
292 size_t size = std::min(static_cast<size_t>(m_sendingQuota), message. arrayBuffer->byteLength() - m_sentSizeOfTopMessage);
293 final = (m_sendingQuota == size);
294 m_handle->send(type, static_cast<const char*>(message.arrayBuffer->d ata()) + m_sentSizeOfTopMessage, size, final);
295 m_sentSizeOfTopMessage += size;
296 m_sendingQuota -= size;
297 break;
298 }
299 default:
300 ASSERT_NOT_REACHED();
301 }
302 if (m_blobLoader) {
303 break;
304 }
305 ASSERT(final || !m_sendingQuota);
306 if (final) {
307 m_messages.removeFirst();
308 m_sentSizeOfTopMessage = 0;
309 }
310 }
311 if (!m_isSuspended && m_bufferedAmount != bufferedAmount) {
312 m_client->didUpdateBufferedAmount(m_bufferedAmount);
313 }
314 }
315
316 void NewWebSocketChannelImpl::flowControlIfNecessary()
317 {
318 if (m_state != Open) {
319 return;
320 }
321 ASSERT(m_handle);
322 if (m_receivedDataSizeForFlowControl < receivedDataSizeForFlowControlHighWat erMark) {
323 return;
324 }
325 m_handle->flowControl(m_receivedDataSizeForFlowControl);
326 m_receivedDataSizeForFlowControl = 0;
327 }
328
329 void NewWebSocketChannelImpl::didConnect(WebSocketHandle* handle, bool succeed, const WebKit::WebString& selectedProtocol, const WebKit::WebString& extensions)
330 {
331 LOG(Network, "NewWebSocketChannelImpl %p didConnect(%p, %d, %s, %s)", this, handle, succeed, selectedProtocol.utf8().data(), extensions.utf8().data());
332 if (m_state != Connecting) {
333 return;
334 }
335 ASSERT(handle == m_handle);
336 ASSERT(m_client);
337 if (!succeed) {
338 failAsError("Cannot connect to " + m_url.string() + ".");
339 return;
340 }
341 m_state = Open;
342 m_subprotocol = selectedProtocol;
343 m_extensions = extensions;
344 if (m_isSuspended) {
345 m_pendingEvents.append(PendingEvent(PendingEvent::DidConnectComplete));
346 } else {
347 m_client->didConnect();
348 }
349 }
350
351 void NewWebSocketChannelImpl::didReceiveData(WebSocketHandle* handle, WebSocketH andle::MessageType type, const char* data, size_t size, bool fin)
352 {
353 LOG(Network, "NewWebSocketChannelImpl %p didReceiveData(%p, %d, (%p, %zu), % d)", this, handle, type, data, size, fin);
354 if (m_state != Open) {
355 return;
356 }
357 ASSERT(handle == m_handle);
358 ASSERT(m_client);
359 // Non-final frames cannot be empty.
360 ASSERT(fin || size);
361 switch (type) {
362 case WebSocketHandle::MessageTypeText:
363 ASSERT(m_receivingMessageData.isEmpty());
364 m_receivingMessageTypeIsText = true;
365 break;
366 case WebSocketHandle::MessageTypeBinary:
367 ASSERT(m_receivingMessageData.isEmpty());
368 m_receivingMessageTypeIsText = false;
369 break;
370 case WebSocketHandle::MessageTypeContinuation:
371 ASSERT(!m_receivingMessageData.isEmpty());
372 break;
373 default:
374 ASSERT_NOT_REACHED();
375 break;
376 }
377 m_receivingMessageData.append(data, size);
378 m_receivedDataSizeForFlowControl += size;
379 flowControlIfNecessary();
380 if (!fin) {
381 return;
382 }
383 if (m_isSuspended) {
384 m_pendingEvents.append(PendingEvent(m_receivingMessageTypeIsText ? Pendi ngEvent::DidReceiveTextMessage : PendingEvent::DidReceiveBinaryMessage));
385 m_pendingEvents.last().message.swap(m_receivingMessageData);
386 } else {
387 Vector<char> messageData;
388 messageData.swap(m_receivingMessageData);
389 if (m_receivingMessageTypeIsText) {
390 handleTextMessage(&messageData);
391 // handleTextMessage can delete this object.
392 } else {
393 handleBinaryMessage(&messageData);
394 }
395 }
396 }
397
398
399 void NewWebSocketChannelImpl::didClose(WebSocketHandle* handle, unsigned short c ode, const WebKit::WebString& reason)
400 {
401 // FIXME: Maybe we should notify an error to m_client for some didClose mess ages.
402 LOG(Network, "NewWebSocketChannelImpl %p didClose(%p, %d, %s)", this, code, String(reason).utf8().data());
403 if (m_state == Closed) {
404 return;
405 }
406 if (m_identifier) {
tyoshino (SeeGerritForStatus) 2013/08/28 05:26:03 ASSERT(m_context->isDocument())
yhirano 2013/08/28 06:29:09 Done.
407 InspectorInstrumentation::didCloseWebSocket(toDocument(m_context), m_ide ntifier);
408 }
409 ASSERT(handle == m_handle);
410 m_handle = 0;
411 m_state = Closed;
412 if (m_isSuspended) {
413 m_pendingEvents.append(PendingEvent(code, reason));
414 return;
415 }
416 WebSocketChannelClient* client = m_client;
417 m_client = 0;
418 ASSERT(client);
419 WebSocketChannelClient::ClosingHandshakeCompletionStatus status =
420 isClean(code) ? WebSocketChannelClient::ClosingHandshakeComplete : WebSo cketChannelClient::ClosingHandshakeIncomplete;
421 client->didClose(m_bufferedAmount, status, code, reason);
422 // client->didClose may delete this object.
423 }
424
425 void NewWebSocketChannelImpl::didFinishLoading()
426 {
427 // m_client can be invalid here.
428 LOG(Network, "NewWebSocketChannelImpl %p didFinishLoading()", this);
429 if (m_state == Open) {
430 ASSERT(m_handle);
431 // The loaded blob is always placed on m_messages[0].
432 ASSERT(m_messages.size() > 0 && m_messages.first().type == MessageTypeBl ob);
433 // We replace it with the loaded blob.
434 m_messages.first() = Message(m_blobLoader->arrayBufferResult());
435 sendInternal();
436 }
437 m_blobLoader.clear();
438
439 deref();
440 // deref() may delete this object.
441 }
442
443 void NewWebSocketChannelImpl::didFail(FileError::ErrorCode errorCode)
444 {
445 // m_client can be invalid here.
446 LOG(Network, "NewWebSocketChannelImpl %p didFail(%d)", this, errorCode);
447 m_blobLoader.clear();
448 failAsError("Failed to load Blob: error code = " + String::number(errorCode) ); // FIXME: Generate human-friendly reason message.
449 deref();
450 // deref() may delete this object.
451 }
452
453 void NewWebSocketChannelImpl::resumeTimerFired(Timer<NewWebSocketChannelImpl>*)
454 {
455 RefPtr<NewWebSocketChannelImpl> protect(this);
456 deref();
457
458 if (!m_client) {
459 ASSERT(m_state == Closed);
460 ASSERT(m_pendingEvents.isEmpty());
tyoshino (SeeGerritForStatus) 2013/08/28 05:26:03 what's the reason these conditions are met
yhirano 2013/08/28 06:29:09 It was wrong. Done.
461 return;
462 }
463 if (m_isSuspended) {
464 return;
465 }
466 sendInternal();
467 flowControlIfNecessary();
468 processPendingEvents();
469 }
470
471 void NewWebSocketChannelImpl::handleTextMessage(Vector<char>* messageData)
472 {
473 ASSERT(messageData);
474 String message = "";
475 if (m_receivingMessageData.size() > 0) {
476 message = String::fromUTF8(m_receivingMessageData.data(), m_receivingMes sageData.size());
477 }
478 if (message.isNull()) {
479 failAsError("Could not decode a text frame as UTF-8.");
480 } else {
481 m_client->didReceiveMessage(message);
482 }
483 }
484
485 void NewWebSocketChannelImpl::handleBinaryMessage(Vector<char>* messageData)
486 {
487 ASSERT(messageData);
488 OwnPtr<Vector<char> > binaryData = adoptPtr(new Vector<char>);
489 messageData->swap(*binaryData);
490 m_client->didReceiveBinaryData(binaryData.release());
491 }
492
493 void NewWebSocketChannelImpl::startLoadingBlob(const Blob& blob)
494 {
495 LOG(Network, "NewWebSocketChannelImpl %p startLoadingBlob(%s)", this, blob.u rl().string().utf8().data());
496 ASSERT(!m_blobLoader);
497 // Protect this object until the loading completes or fails.
498 ref();
499
500 m_blobLoader = adoptPtr(new FileReaderLoader(FileReaderLoader::ReadAsArrayBu ffer, this));
501 m_blobLoader->start(m_context, blob);
502 }
503
504 void NewWebSocketChannelImpl::processPendingEvents()
505 {
506 RefPtr<NewWebSocketChannelImpl> protect(this);
507 ASSERT(!m_isSuspended);
508
509 for (size_t i = 0; i < m_pendingEvents.size(); ++i) {
510 ASSERT(m_client);
511 PendingEvent& event = m_pendingEvents[i];
512 switch (event.type) {
513 case PendingEvent::DidConnectComplete:
514 m_client->didConnect();
515 break;
516 case PendingEvent::DidReceiveTextMessage:
517 handleTextMessage(&event.message);
518 // m_client can be invalid here.
519 break;
520 case PendingEvent::DidReceiveBinaryMessage:
521 handleBinaryMessage(&event.message);
522 break;
523 case PendingEvent::DidReceiveError:
524 m_client->didReceiveMessageError();
525 break;
526 case PendingEvent::DidClose: {
527 ASSERT(m_state == Closed);
528 ASSERT(!m_handle);
529 WebSocketChannelClient::ClosingHandshakeCompletionStatus status =
530 isClean(event.closingCode) ? WebSocketChannelClient::ClosingHand shakeComplete : WebSocketChannelClient::ClosingHandshakeIncomplete;
531 m_client->didClose(m_bufferedAmount, status, event.closingCode, even t.closingReason);
532 // m_client can be invalid here.
533 m_client = 0;
534 break;
535 }
536 default:
537 ASSERT_NOT_REACHED();
538 break;
539 }
540
541 if (event.type == PendingEvent::DidClose || !m_client) {
542 // Drop remaining messages.
543 break;
544 }
545 }
546 m_pendingEvents.clear();
547 }
548
549 } // namespace WebCore
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698