1 /* 2 * Copyright (C) 2011 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 33 #include "modules/websockets/WebSocket.h" 34 35 #include "bindings/v8/ExceptionState.h" 36 #include "bindings/v8/ScriptController.h" 37 #include "core/dom/Document.h" 38 #include "core/dom/ExceptionCode.h" 39 #include "core/dom/ExecutionContext.h" 40 #include "core/events/MessageEvent.h" 41 #include "core/fileapi/Blob.h" 42 #include "core/frame/ConsoleTypes.h" 43 #include "core/frame/LocalDOMWindow.h" 44 #include "core/frame/LocalFrame.h" 45 #include "core/frame/csp/ContentSecurityPolicy.h" 46 #include "core/inspector/ScriptCallStack.h" 47 #include "modules/websockets/CloseEvent.h" 48 #include "platform/Logging.h" 49 #include "platform/blob/BlobData.h" 50 #include "platform/heap/Handle.h" 51 #include "platform/weborigin/KnownPorts.h" 52 #include "platform/weborigin/SecurityOrigin.h" 53 #include "public/platform/Platform.h" 54 #include "wtf/ArrayBuffer.h" 55 #include "wtf/ArrayBufferView.h" 56 #include "wtf/Assertions.h" 57 #include "wtf/HashSet.h" 58 #include "wtf/PassOwnPtr.h" 59 #include "wtf/StdLibExtras.h" 60 #include "wtf/text/CString.h" 61 #include "wtf/text/StringBuilder.h" 62 #include "wtf/text/WTFString.h" 63 64 namespace WebCore { 65 66 WebSocket::EventQueue::EventQueue(EventTarget* target) 67 : m_state(Active) 68 , m_target(target) 69 , m_resumeTimer(this, &EventQueue::resumeTimerFired) { } 70 71 WebSocket::EventQueue::~EventQueue() { stop(); } 72 73 void WebSocket::EventQueue::dispatch(PassRefPtrWillBeRawPtr<Event> event) 74 { 75 switch (m_state) { 76 case Active: 77 ASSERT(m_events.isEmpty()); 78 ASSERT(m_target->executionContext()); 79 m_target->dispatchEvent(event); 80 break; 81 case Suspended: 82 m_events.append(event); 83 break; 84 case Stopped: 85 ASSERT(m_events.isEmpty()); 86 // Do nothing. 87 break; 88 } 89 } 90 91 bool WebSocket::EventQueue::isEmpty() const 92 { 93 return m_events.isEmpty(); 94 } 95 96 void WebSocket::EventQueue::suspend() 97 { 98 m_resumeTimer.stop(); 99 if (m_state != Active) 100 return; 101 102 m_state = Suspended; 103 } 104 105 void WebSocket::EventQueue::resume() 106 { 107 if (m_state != Suspended || m_resumeTimer.isActive()) 108 return; 109 110 m_resumeTimer.startOneShot(0, FROM_HERE); 111 } 112 113 void WebSocket::EventQueue::stop() 114 { 115 if (m_state == Stopped) 116 return; 117 118 m_state = Stopped; 119 m_resumeTimer.stop(); 120 m_events.clear(); 121 } 122 123 void WebSocket::EventQueue::dispatchQueuedEvents() 124 { 125 if (m_state != Active) 126 return; 127 128 RefPtrWillBeRawPtr<EventQueue> protect(this); 129 130 WillBeHeapDeque<RefPtrWillBeMember<Event> > events; 131 events.swap(m_events); 132 while (!events.isEmpty()) { 133 if (m_state == Stopped || m_state == Suspended) 134 break; 135 ASSERT(m_state == Active); 136 ASSERT(m_target->executionContext()); 137 m_target->dispatchEvent(events.takeFirst()); 138 // |this| can be stopped here. 139 } 140 if (m_state == Suspended) { 141 while (!m_events.isEmpty()) 142 events.append(m_events.takeFirst()); 143 events.swap(m_events); 144 } 145 } 146 147 void WebSocket::EventQueue::resumeTimerFired(Timer<EventQueue>*) 148 { 149 ASSERT(m_state == Suspended); 150 m_state = Active; 151 dispatchQueuedEvents(); 152 } 153 154 void WebSocket::EventQueue::trace(Visitor* visitor) 155 { 156 visitor->trace(m_events); 157 } 158 159 const size_t maxReasonSizeInBytes = 123; 160 161 static inline bool isValidSubprotocolCharacter(UChar character) 162 { 163 const UChar minimumProtocolCharacter = '!'; // U+0021. 164 const UChar maximumProtocolCharacter = '~'; // U+007E. 165 // Set to true if character does not matches "separators" ABNF defined in 166 // RFC2616. SP and HT are excluded since the range check excludes them. 167 bool isNotSeparator = character != '"' && character != '(' && character != ')' && character != ',' && character != '/' 168 && !(character >= ':' && character <= '@') // U+003A - U+0040 (':', ';', '<', '=', '>', '?', '@'). 169 && !(character >= '[' && character <= ']') // U+005B - U+005D ('[', '\\', ']'). 170 && character != '{' && character != '}'; 171 return character >= minimumProtocolCharacter && character <= maximumProtocolCharacter && isNotSeparator; 172 } 173 174 bool WebSocket::isValidSubprotocolString(const String& protocol) 175 { 176 if (protocol.isEmpty()) 177 return false; 178 for (size_t i = 0; i < protocol.length(); ++i) { 179 if (!isValidSubprotocolCharacter(protocol[i])) 180 return false; 181 } 182 return true; 183 } 184 185 static String encodeSubprotocolString(const String& protocol) 186 { 187 StringBuilder builder; 188 for (size_t i = 0; i < protocol.length(); i++) { 189 if (protocol[i] < 0x20 || protocol[i] > 0x7E) 190 builder.append(String::format("\\u%04X", protocol[i])); 191 else if (protocol[i] == 0x5c) 192 builder.append("\\\\"); 193 else 194 builder.append(protocol[i]); 195 } 196 return builder.toString(); 197 } 198 199 static String joinStrings(const Vector<String>& strings, const char* separator) 200 { 201 StringBuilder builder; 202 for (size_t i = 0; i < strings.size(); ++i) { 203 if (i) 204 builder.append(separator); 205 builder.append(strings[i]); 206 } 207 return builder.toString(); 208 } 209 210 static unsigned long saturateAdd(unsigned long a, unsigned long b) 211 { 212 if (std::numeric_limits<unsigned long>::max() - a < b) 213 return std::numeric_limits<unsigned long>::max(); 214 return a + b; 215 } 216 217 static void setInvalidStateErrorForSendMethod(ExceptionState& exceptionState) 218 { 219 exceptionState.throwDOMException(InvalidStateError, "Still in CONNECTING state."); 220 } 221 222 const char* WebSocket::subprotocolSeperator() 223 { 224 return ", "; 225 } 226 227 WebSocket::WebSocket(ExecutionContext* context) 228 : ActiveDOMObject(context) 229 , m_state(CONNECTING) 230 , m_bufferedAmount(0) 231 , m_consumedBufferedAmount(0) 232 , m_bufferedAmountAfterClose(0) 233 , m_binaryType(BinaryTypeBlob) 234 , m_subprotocol("") 235 , m_extensions("") 236 , m_eventQueue(EventQueue::create(this)) 237 , m_bufferedAmountConsumeTimer(this, &WebSocket::reflectBufferedAmountConsumption) 238 { 239 ScriptWrappable::init(this); 240 } 241 242 WebSocket::~WebSocket() 243 { 244 ASSERT(!m_channel); 245 } 246 247 void WebSocket::logError(const String& message) 248 { 249 executionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, message); 250 } 251 252 PassRefPtrWillBeRawPtr<WebSocket> WebSocket::create(ExecutionContext* context, const String& url, ExceptionState& exceptionState) 253 { 254 Vector<String> protocols; 255 return create(context, url, protocols, exceptionState); 256 } 257 258 PassRefPtrWillBeRawPtr<WebSocket> WebSocket::create(ExecutionContext* context, const String& url, const Vector<String>& protocols, ExceptionState& exceptionState) 259 { 260 if (url.isNull()) { 261 exceptionState.throwDOMException(SyntaxError, "Failed to create a WebSocket: the provided URL is invalid."); 262 return nullptr; 263 } 264 265 RefPtrWillBeRawPtr<WebSocket> webSocket(adoptRefWillBeRefCountedGarbageCollected(new WebSocket(context))); 266 webSocket->suspendIfNeeded(); 267 268 webSocket->connect(url, protocols, exceptionState); 269 if (exceptionState.hadException()) 270 return nullptr; 271 272 return webSocket.release(); 273 } 274 275 PassRefPtrWillBeRawPtr<WebSocket> WebSocket::create(ExecutionContext* context, const String& url, const String& protocol, ExceptionState& exceptionState) 276 { 277 Vector<String> protocols; 278 protocols.append(protocol); 279 return create(context, url, protocols, exceptionState); 280 } 281 282 void WebSocket::connect(const String& url, const Vector<String>& protocols, ExceptionState& exceptionState) 283 { 284 WTF_LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data()); 285 m_url = KURL(KURL(), url); 286 287 if (!m_url.isValid()) { 288 m_state = CLOSED; 289 exceptionState.throwDOMException(SyntaxError, "The URL '" + url + "' is invalid."); 290 return; 291 } 292 if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { 293 m_state = CLOSED; 294 exceptionState.throwDOMException(SyntaxError, "The URL's scheme must be either 'ws' or 'wss'. '" + m_url.protocol() + "' is not allowed."); 295 return; 296 } 297 298 if (m_url.hasFragmentIdentifier()) { 299 m_state = CLOSED; 300 exceptionState.throwDOMException(SyntaxError, "The URL contains a fragment identifier ('" + m_url.fragmentIdentifier() + "'). Fragment identifiers are not allowed in WebSocket URLs."); 301 return; 302 } 303 if (!portAllowed(m_url)) { 304 m_state = CLOSED; 305 exceptionState.throwSecurityError("The port " + String::number(m_url.port()) + " is not allowed."); 306 return; 307 } 308 309 // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved. 310 bool shouldBypassMainWorldContentSecurityPolicy = false; 311 if (executionContext()->isDocument()) { 312 Document* document = toDocument(executionContext()); 313 shouldBypassMainWorldContentSecurityPolicy = document->frame()->script().shouldBypassMainWorldContentSecurityPolicy(); 314 } 315 if (!shouldBypassMainWorldContentSecurityPolicy && !executionContext()->contentSecurityPolicy()->allowConnectToSource(m_url)) { 316 m_state = CLOSED; 317 // The URL is safe to expose to JavaScript, as this check happens synchronously before redirection. 318 exceptionState.throwSecurityError("Refused to connect to '" + m_url.elidedString() + "' because it violates the document's Content Security Policy."); 319 return; 320 } 321 322 m_channel = createChannel(executionContext(), this); 323 324 for (size_t i = 0; i < protocols.size(); ++i) { 325 if (!isValidSubprotocolString(protocols[i])) { 326 m_state = CLOSED; 327 exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeSubprotocolString(protocols[i]) + "' is invalid."); 328 releaseChannel(); 329 return; 330 } 331 } 332 HashSet<String> visited; 333 for (size_t i = 0; i < protocols.size(); ++i) { 334 if (!visited.add(protocols[i]).isNewEntry) { 335 m_state = CLOSED; 336 exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeSubprotocolString(protocols[i]) + "' is duplicated."); 337 releaseChannel(); 338 return; 339 } 340 } 341 342 String protocolString; 343 if (!protocols.isEmpty()) 344 protocolString = joinStrings(protocols, subprotocolSeperator()); 345 346 if (!m_channel->connect(m_url, protocolString)) { 347 m_state = CLOSED; 348 exceptionState.throwSecurityError("An insecure WebSocket connection may not be initiated from a page loaded over HTTPS."); 349 releaseChannel(); 350 return; 351 } 352 } 353 354 void WebSocket::handleSendResult(WebSocketChannel::SendResult result, ExceptionState& exceptionState, WebSocketSendType dataType) 355 { 356 switch (result) { 357 case WebSocketChannel::InvalidMessage: 358 exceptionState.throwDOMException(SyntaxError, "The message contains invalid characters."); 359 return; 360 case WebSocketChannel::SendFail: 361 logError("WebSocket send() failed."); 362 return; 363 case WebSocketChannel::SendSuccess: 364 blink::Platform::current()->histogramEnumeration("WebCore.WebSocket.SendType", dataType, WebSocketSendTypeMax); 365 return; 366 } 367 ASSERT_NOT_REACHED(); 368 } 369 370 void WebSocket::updateBufferedAmountAfterClose(unsigned long payloadSize) 371 { 372 m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize); 373 m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize)); 374 375 logError("WebSocket is already in CLOSING or CLOSED state."); 376 } 377 378 void WebSocket::reflectBufferedAmountConsumption(Timer<WebSocket>*) 379 { 380 ASSERT(m_bufferedAmount >= m_consumedBufferedAmount); 381 WTF_LOG(Network, "WebSocket %p reflectBufferedAmountConsumption() %lu => %lu", this, m_bufferedAmount, m_bufferedAmount - m_consumedBufferedAmount); 382 383 m_bufferedAmount -= m_consumedBufferedAmount; 384 m_consumedBufferedAmount = 0; 385 } 386 387 void WebSocket::releaseChannel() 388 { 389 ASSERT(m_channel); 390 m_channel->disconnect(); 391 m_channel = nullptr; 392 } 393 394 void WebSocket::send(const String& message, ExceptionState& exceptionState) 395 { 396 WTF_LOG(Network, "WebSocket %p send() Sending String '%s'", this, message.utf8().data()); 397 if (m_state == CONNECTING) { 398 setInvalidStateErrorForSendMethod(exceptionState); 399 return; 400 } 401 // No exception is raised if the connection was once established but has subsequently been closed. 402 if (m_state == CLOSING || m_state == CLOSED) { 403 updateBufferedAmountAfterClose(message.utf8().length()); 404 return; 405 } 406 ASSERT(m_channel); 407 m_bufferedAmount += message.utf8().length(); 408 handleSendResult(m_channel->send(message), exceptionState, WebSocketSendTypeString); 409 } 410 411 void WebSocket::send(ArrayBuffer* binaryData, ExceptionState& exceptionState) 412 { 413 WTF_LOG(Network, "WebSocket %p send() Sending ArrayBuffer %p", this, binaryData); 414 ASSERT(binaryData); 415 if (m_state == CONNECTING) { 416 setInvalidStateErrorForSendMethod(exceptionState); 417 return; 418 } 419 if (m_state == CLOSING || m_state == CLOSED) { 420 updateBufferedAmountAfterClose(binaryData->byteLength()); 421 return; 422 } 423 ASSERT(m_channel); 424 m_bufferedAmount += binaryData->byteLength(); 425 handleSendResult(m_channel->send(*binaryData, 0, binaryData->byteLength()), exceptionState, WebSocketSendTypeArrayBuffer); 426 } 427 428 void WebSocket::send(ArrayBufferView* arrayBufferView, ExceptionState& exceptionState) 429 { 430 WTF_LOG(Network, "WebSocket %p send() Sending ArrayBufferView %p", this, arrayBufferView); 431 ASSERT(arrayBufferView); 432 if (m_state == CONNECTING) { 433 setInvalidStateErrorForSendMethod(exceptionState); 434 return; 435 } 436 if (m_state == CLOSING || m_state == CLOSED) { 437 updateBufferedAmountAfterClose(arrayBufferView->byteLength()); 438 return; 439 } 440 ASSERT(m_channel); 441 m_bufferedAmount += arrayBufferView->byteLength(); 442 RefPtr<ArrayBuffer> arrayBuffer(arrayBufferView->buffer()); 443 handleSendResult(m_channel->send(*arrayBuffer, arrayBufferView->byteOffset(), arrayBufferView->byteLength()), exceptionState, WebSocketSendTypeArrayBufferView); 444 } 445 446 void WebSocket::send(Blob* binaryData, ExceptionState& exceptionState) 447 { 448 WTF_LOG(Network, "WebSocket %p send() Sending Blob '%s'", this, binaryData->uuid().utf8().data()); 449 ASSERT(binaryData); 450 if (m_state == CONNECTING) { 451 setInvalidStateErrorForSendMethod(exceptionState); 452 return; 453 } 454 if (m_state == CLOSING || m_state == CLOSED) { 455 updateBufferedAmountAfterClose(static_cast<unsigned long>(binaryData->size())); 456 return; 457 } 458 m_bufferedAmount += binaryData->size(); 459 ASSERT(m_channel); 460 handleSendResult(m_channel->send(binaryData->blobDataHandle()), exceptionState, WebSocketSendTypeBlob); 461 } 462 463 void WebSocket::close(unsigned short code, const String& reason, ExceptionState& exceptionState) 464 { 465 closeInternal(code, reason, exceptionState); 466 } 467 468 void WebSocket::close(ExceptionState& exceptionState) 469 { 470 closeInternal(WebSocketChannel::CloseEventCodeNotSpecified, String(), exceptionState); 471 } 472 473 void WebSocket::close(unsigned short code, ExceptionState& exceptionState) 474 { 475 closeInternal(code, String(), exceptionState); 476 } 477 478 void WebSocket::closeInternal(int code, const String& reason, ExceptionState& exceptionState) 479 { 480 if (code == WebSocketChannel::CloseEventCodeNotSpecified) { 481 WTF_LOG(Network, "WebSocket %p close() without code and reason", this); 482 } else { 483 WTF_LOG(Network, "WebSocket %p close() code=%d reason='%s'", this, code, reason.utf8().data()); 484 if (!(code == WebSocketChannel::CloseEventCodeNormalClosure || (WebSocketChannel::CloseEventCodeMinimumUserDefined <= code && code <= WebSocketChannel::CloseEventCodeMaximumUserDefined))) { 485 exceptionState.throwDOMException(InvalidAccessError, "The code must be either 1000, or between 3000 and 4999. " + String::number(code) + " is neither."); 486 return; 487 } 488 CString utf8 = reason.utf8(StrictUTF8ConversionReplacingUnpairedSurrogatesWithFFFD); 489 if (utf8.length() > maxReasonSizeInBytes) { 490 exceptionState.throwDOMException(SyntaxError, "The message must not be greater than " + String::number(maxReasonSizeInBytes) + " bytes."); 491 return; 492 } 493 } 494 495 if (m_state == CLOSING || m_state == CLOSED) 496 return; 497 if (m_state == CONNECTING) { 498 m_state = CLOSING; 499 m_channel->fail("WebSocket is closed before the connection is established.", WarningMessageLevel, String(), 0); 500 return; 501 } 502 m_state = CLOSING; 503 if (m_channel) 504 m_channel->close(code, reason); 505 } 506 507 const KURL& WebSocket::url() const 508 { 509 return m_url; 510 } 511 512 WebSocket::State WebSocket::readyState() const 513 { 514 return m_state; 515 } 516 517 unsigned long WebSocket::bufferedAmount() const 518 { 519 return saturateAdd(m_bufferedAmount, m_bufferedAmountAfterClose); 520 } 521 522 String WebSocket::protocol() const 523 { 524 return m_subprotocol; 525 } 526 527 String WebSocket::extensions() const 528 { 529 return m_extensions; 530 } 531 532 String WebSocket::binaryType() const 533 { 534 switch (m_binaryType) { 535 case BinaryTypeBlob: 536 return "blob"; 537 case BinaryTypeArrayBuffer: 538 return "arraybuffer"; 539 } 540 ASSERT_NOT_REACHED(); 541 return String(); 542 } 543 544 void WebSocket::setBinaryType(const String& binaryType) 545 { 546 if (binaryType == "blob") { 547 m_binaryType = BinaryTypeBlob; 548 return; 549 } 550 if (binaryType == "arraybuffer") { 551 m_binaryType = BinaryTypeArrayBuffer; 552 return; 553 } 554 logError("'" + binaryType + "' is not a valid value for binaryType; binaryType remains unchanged."); 555 } 556 557 const AtomicString& WebSocket::interfaceName() const 558 { 559 return EventTargetNames::WebSocket; 560 } 561 562 ExecutionContext* WebSocket::executionContext() const 563 { 564 return ActiveDOMObject::executionContext(); 565 } 566 567 void WebSocket::contextDestroyed() 568 { 569 WTF_LOG(Network, "WebSocket %p contextDestroyed()", this); 570 ASSERT(!m_channel); 571 ASSERT(m_state == CLOSED); 572 ActiveDOMObject::contextDestroyed(); 573 } 574 575 bool WebSocket::hasPendingActivity() const 576 { 577 return m_channel || !m_eventQueue->isEmpty(); 578 } 579 580 void WebSocket::suspend() 581 { 582 if (m_channel) 583 m_channel->suspend(); 584 m_eventQueue->suspend(); 585 } 586 587 void WebSocket::resume() 588 { 589 if (m_channel) 590 m_channel->resume(); 591 m_eventQueue->resume(); 592 } 593 594 void WebSocket::stop() 595 { 596 m_eventQueue->stop(); 597 if (m_channel) { 598 m_channel->close(WebSocketChannel::CloseEventCodeGoingAway, String()); 599 releaseChannel(); 600 } 601 m_state = CLOSED; 602 } 603 604 void WebSocket::didConnect(const String& subprotocol, const String& extensions) 605 { 606 WTF_LOG(Network, "WebSocket %p didConnect()", this); 607 if (m_state != CONNECTING) 608 return; 609 m_state = OPEN; 610 m_subprotocol = subprotocol; 611 m_extensions = extensions; 612 m_eventQueue->dispatch(Event::create(EventTypeNames::open)); 613 } 614 615 void WebSocket::didReceiveMessage(const String& msg) 616 { 617 WTF_LOG(Network, "WebSocket %p didReceiveMessage() Text message '%s'", this, msg.utf8().data()); 618 if (m_state != OPEN) 619 return; 620 m_eventQueue->dispatch(MessageEvent::create(msg, SecurityOrigin::create(m_url)->toString())); 621 } 622 623 void WebSocket::didReceiveBinaryData(PassOwnPtr<Vector<char> > binaryData) 624 { 625 WTF_LOG(Network, "WebSocket %p didReceiveBinaryData() %lu byte binary message", this, static_cast<unsigned long>(binaryData->size())); 626 switch (m_binaryType) { 627 case BinaryTypeBlob: { 628 size_t size = binaryData->size(); 629 RefPtr<RawData> rawData = RawData::create(); 630 binaryData->swap(*rawData->mutableData()); 631 OwnPtr<BlobData> blobData = BlobData::create(); 632 blobData->appendData(rawData.release(), 0, BlobDataItem::toEndOfFile); 633 RefPtrWillBeRawPtr<Blob> blob = Blob::create(BlobDataHandle::create(blobData.release(), size)); 634 m_eventQueue->dispatch(MessageEvent::create(blob.release(), SecurityOrigin::create(m_url)->toString())); 635 break; 636 } 637 638 case BinaryTypeArrayBuffer: 639 RefPtr<ArrayBuffer> arrayBuffer = ArrayBuffer::create(binaryData->data(), binaryData->size()); 640 if (!arrayBuffer) { 641 // Failed to allocate an ArrayBuffer. We need to crash the renderer 642 // since there's no way defined in the spec to tell this to the 643 // user. 644 CRASH(); 645 } 646 m_eventQueue->dispatch(MessageEvent::create(arrayBuffer.release(), SecurityOrigin::create(m_url)->toString())); 647 break; 648 } 649 } 650 651 void WebSocket::didReceiveMessageError() 652 { 653 WTF_LOG(Network, "WebSocket %p didReceiveMessageError()", this); 654 m_state = CLOSED; 655 m_eventQueue->dispatch(Event::create(EventTypeNames::error)); 656 } 657 658 void WebSocket::didConsumeBufferedAmount(unsigned long consumed) 659 { 660 ASSERT(m_bufferedAmount >= consumed); 661 WTF_LOG(Network, "WebSocket %p didConsumeBufferedAmount(%lu)", this, consumed); 662 if (m_state == CLOSED) 663 return; 664 m_consumedBufferedAmount += consumed; 665 if (!m_bufferedAmountConsumeTimer.isActive()) 666 m_bufferedAmountConsumeTimer.startOneShot(0, FROM_HERE); 667 } 668 669 void WebSocket::didStartClosingHandshake() 670 { 671 WTF_LOG(Network, "WebSocket %p didStartClosingHandshake()", this); 672 m_state = CLOSING; 673 } 674 675 void WebSocket::didClose(ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason) 676 { 677 WTF_LOG(Network, "WebSocket %p didClose()", this); 678 if (!m_channel) 679 return; 680 bool hasAllDataConsumed = m_bufferedAmount == m_consumedBufferedAmount; 681 bool wasClean = m_state == CLOSING && hasAllDataConsumed && closingHandshakeCompletion == ClosingHandshakeComplete && code != WebSocketChannel::CloseEventCodeAbnormalClosure; 682 m_state = CLOSED; 683 684 m_eventQueue->dispatch(CloseEvent::create(wasClean, code, reason)); 685 releaseChannel(); 686 } 687 688 size_t WebSocket::getFramingOverhead(size_t payloadSize) 689 { 690 static const size_t hybiBaseFramingOverhead = 2; // Every frame has at least two-byte header. 691 static const size_t hybiMaskingKeyLength = 4; // Every frame from client must have masking key. 692 static const size_t minimumPayloadSizeWithTwoByteExtendedPayloadLength = 126; 693 static const size_t minimumPayloadSizeWithEightByteExtendedPayloadLength = 0x10000; 694 size_t overhead = hybiBaseFramingOverhead + hybiMaskingKeyLength; 695 if (payloadSize >= minimumPayloadSizeWithEightByteExtendedPayloadLength) 696 overhead += 8; 697 else if (payloadSize >= minimumPayloadSizeWithTwoByteExtendedPayloadLength) 698 overhead += 2; 699 return overhead; 700 } 701 702 void WebSocket::trace(Visitor* visitor) 703 { 704 visitor->trace(m_channel); 705 visitor->trace(m_eventQueue); 706 EventTargetWithInlineData::trace(visitor); 707 } 708 709 } // namespace WebCore 710