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/Event.h" 41#include "core/events/MessageEvent.h" 42#include "core/fileapi/Blob.h" 43#include "core/frame/ConsoleTypes.h" 44#include "core/frame/ContentSecurityPolicy.h" 45#include "core/frame/DOMWindow.h" 46#include "core/frame/Frame.h" 47#include "core/inspector/ScriptCallStack.h" 48#include "modules/websockets/CloseEvent.h" 49#include "platform/Logging.h" 50#include "platform/blob/BlobData.h" 51#include "platform/weborigin/KnownPorts.h" 52#include "platform/weborigin/SecurityOrigin.h" 53#include "wtf/ArrayBuffer.h" 54#include "wtf/ArrayBufferView.h" 55#include "wtf/HashSet.h" 56#include "wtf/PassOwnPtr.h" 57#include "wtf/StdLibExtras.h" 58#include "wtf/text/CString.h" 59#include "wtf/text/StringBuilder.h" 60#include "wtf/text/WTFString.h" 61 62using namespace std; 63 64namespace WebCore { 65 66WebSocket::EventQueue::EventQueue(EventTarget* target) 67 : m_state(Active) 68 , m_target(target) 69 , m_resumeTimer(this, &EventQueue::resumeTimerFired) { } 70 71WebSocket::EventQueue::~EventQueue() { stop(); } 72 73void WebSocket::EventQueue::dispatch(PassRefPtr<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 91void WebSocket::EventQueue::suspend() 92{ 93 if (m_state != Active) 94 return; 95 96 m_state = Suspended; 97} 98 99void WebSocket::EventQueue::resume() 100{ 101 if (m_state != Suspended || m_resumeTimer.isActive()) 102 return; 103 104 m_resumeTimer.startOneShot(0); 105} 106 107void WebSocket::EventQueue::stop() 108{ 109 if (m_state == Stopped) 110 return; 111 112 m_state = Stopped; 113 m_resumeTimer.stop(); 114 m_events.clear(); 115} 116 117void WebSocket::EventQueue::dispatchQueuedEvents() 118{ 119 if (m_state != Active) 120 return; 121 122 RefPtr<EventQueue> protect(this); 123 124 Deque<RefPtr<Event> > events; 125 events.swap(m_events); 126 while (!events.isEmpty()) { 127 if (m_state == Stopped || m_state == Suspended) 128 break; 129 ASSERT(m_state == Active); 130 ASSERT(m_target->executionContext()); 131 m_target->dispatchEvent(events.takeFirst()); 132 // |this| can be stopped here. 133 } 134 if (m_state == Suspended) { 135 while (!m_events.isEmpty()) 136 events.append(m_events.takeFirst()); 137 events.swap(m_events); 138 } 139} 140 141void WebSocket::EventQueue::resumeTimerFired(Timer<EventQueue>*) 142{ 143 ASSERT(m_state == Suspended); 144 m_state = Active; 145 dispatchQueuedEvents(); 146} 147 148const size_t maxReasonSizeInBytes = 123; 149 150static inline bool isValidProtocolCharacter(UChar character) 151{ 152 // Hybi-10 says "(Subprotocol string must consist of) characters in the range U+0021 to U+007E not including 153 // separator characters as defined in [RFC2616]." 154 const UChar minimumProtocolCharacter = '!'; // U+0021. 155 const UChar maximumProtocolCharacter = '~'; // U+007E. 156 return character >= minimumProtocolCharacter && character <= maximumProtocolCharacter 157 && character != '"' && character != '(' && character != ')' && character != ',' && character != '/' 158 && !(character >= ':' && character <= '@') // U+003A - U+0040 (':', ';', '<', '=', '>', '?', '@'). 159 && !(character >= '[' && character <= ']') // U+005B - U+005D ('[', '\\', ']'). 160 && character != '{' && character != '}'; 161} 162 163static bool isValidProtocolString(const String& protocol) 164{ 165 if (protocol.isEmpty()) 166 return false; 167 for (size_t i = 0; i < protocol.length(); ++i) { 168 if (!isValidProtocolCharacter(protocol[i])) 169 return false; 170 } 171 return true; 172} 173 174static String encodeProtocolString(const String& protocol) 175{ 176 StringBuilder builder; 177 for (size_t i = 0; i < protocol.length(); i++) { 178 if (protocol[i] < 0x20 || protocol[i] > 0x7E) 179 builder.append(String::format("\\u%04X", protocol[i])); 180 else if (protocol[i] == 0x5c) 181 builder.append("\\\\"); 182 else 183 builder.append(protocol[i]); 184 } 185 return builder.toString(); 186} 187 188static String joinStrings(const Vector<String>& strings, const char* separator) 189{ 190 StringBuilder builder; 191 for (size_t i = 0; i < strings.size(); ++i) { 192 if (i) 193 builder.append(separator); 194 builder.append(strings[i]); 195 } 196 return builder.toString(); 197} 198 199static unsigned long saturateAdd(unsigned long a, unsigned long b) 200{ 201 if (numeric_limits<unsigned long>::max() - a < b) 202 return numeric_limits<unsigned long>::max(); 203 return a + b; 204} 205 206static void setInvalidStateErrorForSendMethod(ExceptionState& exceptionState) 207{ 208 exceptionState.throwDOMException(InvalidStateError, "Still in CONNECTING state."); 209} 210 211const char* WebSocket::subProtocolSeperator() 212{ 213 return ", "; 214} 215 216WebSocket::WebSocket(ExecutionContext* context) 217 : ActiveDOMObject(context) 218 , m_state(CONNECTING) 219 , m_bufferedAmount(0) 220 , m_bufferedAmountAfterClose(0) 221 , m_binaryType(BinaryTypeBlob) 222 , m_subprotocol("") 223 , m_extensions("") 224 , m_eventQueue(EventQueue::create(this)) 225{ 226 ScriptWrappable::init(this); 227} 228 229WebSocket::~WebSocket() 230{ 231 if (m_channel) 232 m_channel->disconnect(); 233} 234 235void WebSocket::logError(const String& message) 236{ 237 executionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, message); 238} 239 240PassRefPtr<WebSocket> WebSocket::create(ExecutionContext* context, const String& url, ExceptionState& exceptionState) 241{ 242 Vector<String> protocols; 243 return create(context, url, protocols, exceptionState); 244} 245 246PassRefPtr<WebSocket> WebSocket::create(ExecutionContext* context, const String& url, const Vector<String>& protocols, ExceptionState& exceptionState) 247{ 248 if (url.isNull()) { 249 exceptionState.throwDOMException(SyntaxError, "Failed to create a WebSocket: the provided URL is invalid."); 250 return 0; 251 } 252 253 RefPtr<WebSocket> webSocket(adoptRef(new WebSocket(context))); 254 webSocket->suspendIfNeeded(); 255 256 webSocket->connect(context->completeURL(url), protocols, exceptionState); 257 if (exceptionState.hadException()) 258 return 0; 259 260 return webSocket.release(); 261} 262 263PassRefPtr<WebSocket> WebSocket::create(ExecutionContext* context, const String& url, const String& protocol, ExceptionState& exceptionState) 264{ 265 Vector<String> protocols; 266 protocols.append(protocol); 267 return create(context, url, protocols, exceptionState); 268} 269 270void WebSocket::connect(const String& url, ExceptionState& exceptionState) 271{ 272 Vector<String> protocols; 273 connect(url, protocols, exceptionState); 274} 275 276void WebSocket::connect(const String& url, const String& protocol, ExceptionState& exceptionState) 277{ 278 Vector<String> protocols; 279 protocols.append(protocol); 280 connect(url, protocols, exceptionState); 281} 282 283void WebSocket::connect(const String& url, const Vector<String>& protocols, ExceptionState& exceptionState) 284{ 285 WTF_LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data()); 286 m_url = KURL(KURL(), url); 287 288 if (!m_url.isValid()) { 289 m_state = CLOSED; 290 exceptionState.throwDOMException(SyntaxError, "The URL '" + url + "' is invalid."); 291 return; 292 } 293 if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { 294 m_state = CLOSED; 295 exceptionState.throwDOMException(SyntaxError, "The URL's scheme must be either 'ws' or 'wss'. '" + m_url.protocol() + "' is not allowed."); 296 return; 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 = WebSocketChannel::create(executionContext(), this); 323 324 // FIXME: There is a disagreement about restriction of subprotocols between WebSocket API and hybi-10 protocol 325 // draft. The former simply says "only characters in the range U+0021 to U+007E are allowed," while the latter 326 // imposes a stricter rule: "the elements MUST be non-empty strings with characters as defined in [RFC2616], 327 // and MUST all be unique strings." 328 // 329 // Here, we throw SyntaxError if the given protocols do not meet the latter criteria. This behavior does not 330 // comply with WebSocket API specification, but it seems to be the only reasonable way to handle this conflict. 331 for (size_t i = 0; i < protocols.size(); ++i) { 332 if (!isValidProtocolString(protocols[i])) { 333 m_state = CLOSED; 334 exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeProtocolString(protocols[i]) + "' is invalid."); 335 return; 336 } 337 } 338 HashSet<String> visited; 339 for (size_t i = 0; i < protocols.size(); ++i) { 340 if (!visited.add(protocols[i]).isNewEntry) { 341 m_state = CLOSED; 342 exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeProtocolString(protocols[i]) + "' is duplicated."); 343 return; 344 } 345 } 346 347 String protocolString; 348 if (!protocols.isEmpty()) 349 protocolString = joinStrings(protocols, subProtocolSeperator()); 350 351 m_channel->connect(m_url, protocolString); 352} 353 354void WebSocket::handleSendResult(WebSocketChannel::SendResult result, ExceptionState& exceptionState) 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 return; 365 } 366 ASSERT_NOT_REACHED(); 367} 368 369void WebSocket::updateBufferedAmountAfterClose(unsigned long payloadSize) 370{ 371 m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize); 372 m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize)); 373 374 logError("WebSocket is already in CLOSING or CLOSED state."); 375} 376 377void WebSocket::send(const String& message, ExceptionState& exceptionState) 378{ 379 WTF_LOG(Network, "WebSocket %p send() Sending String '%s'", this, message.utf8().data()); 380 if (m_state == CONNECTING) { 381 setInvalidStateErrorForSendMethod(exceptionState); 382 return; 383 } 384 // No exception is raised if the connection was once established but has subsequently been closed. 385 if (m_state == CLOSING || m_state == CLOSED) { 386 updateBufferedAmountAfterClose(message.utf8().length()); 387 return; 388 } 389 ASSERT(m_channel); 390 handleSendResult(m_channel->send(message), exceptionState); 391} 392 393void WebSocket::send(ArrayBuffer* binaryData, ExceptionState& exceptionState) 394{ 395 WTF_LOG(Network, "WebSocket %p send() Sending ArrayBuffer %p", this, binaryData); 396 ASSERT(binaryData); 397 if (m_state == CONNECTING) { 398 setInvalidStateErrorForSendMethod(exceptionState); 399 return; 400 } 401 if (m_state == CLOSING || m_state == CLOSED) { 402 updateBufferedAmountAfterClose(binaryData->byteLength()); 403 return; 404 } 405 ASSERT(m_channel); 406 handleSendResult(m_channel->send(*binaryData, 0, binaryData->byteLength()), exceptionState); 407} 408 409void WebSocket::send(ArrayBufferView* arrayBufferView, ExceptionState& exceptionState) 410{ 411 WTF_LOG(Network, "WebSocket %p send() Sending ArrayBufferView %p", this, arrayBufferView); 412 ASSERT(arrayBufferView); 413 if (m_state == CONNECTING) { 414 setInvalidStateErrorForSendMethod(exceptionState); 415 return; 416 } 417 if (m_state == CLOSING || m_state == CLOSED) { 418 updateBufferedAmountAfterClose(arrayBufferView->byteLength()); 419 return; 420 } 421 ASSERT(m_channel); 422 RefPtr<ArrayBuffer> arrayBuffer(arrayBufferView->buffer()); 423 handleSendResult(m_channel->send(*arrayBuffer, arrayBufferView->byteOffset(), arrayBufferView->byteLength()), exceptionState); 424} 425 426void WebSocket::send(Blob* binaryData, ExceptionState& exceptionState) 427{ 428 WTF_LOG(Network, "WebSocket %p send() Sending Blob '%s'", this, binaryData->uuid().utf8().data()); 429 ASSERT(binaryData); 430 if (m_state == CONNECTING) { 431 setInvalidStateErrorForSendMethod(exceptionState); 432 return; 433 } 434 if (m_state == CLOSING || m_state == CLOSED) { 435 updateBufferedAmountAfterClose(static_cast<unsigned long>(binaryData->size())); 436 return; 437 } 438 ASSERT(m_channel); 439 handleSendResult(m_channel->send(binaryData->blobDataHandle()), exceptionState); 440} 441 442void WebSocket::close(unsigned short code, const String& reason, ExceptionState& exceptionState) 443{ 444 closeInternal(code, reason, exceptionState); 445} 446 447void WebSocket::close(ExceptionState& exceptionState) 448{ 449 closeInternal(WebSocketChannel::CloseEventCodeNotSpecified, String(), exceptionState); 450} 451 452void WebSocket::close(unsigned short code, ExceptionState& exceptionState) 453{ 454 closeInternal(code, String(), exceptionState); 455} 456 457void WebSocket::closeInternal(int code, const String& reason, ExceptionState& exceptionState) 458{ 459 if (code == WebSocketChannel::CloseEventCodeNotSpecified) { 460 WTF_LOG(Network, "WebSocket %p close() without code and reason", this); 461 } else { 462 WTF_LOG(Network, "WebSocket %p close() code=%d reason='%s'", this, code, reason.utf8().data()); 463 if (!(code == WebSocketChannel::CloseEventCodeNormalClosure || (WebSocketChannel::CloseEventCodeMinimumUserDefined <= code && code <= WebSocketChannel::CloseEventCodeMaximumUserDefined))) { 464 exceptionState.throwDOMException(InvalidAccessError, "The code must be either 1000, or between 3000 and 4999. " + String::number(code) + " is neither."); 465 return; 466 } 467 CString utf8 = reason.utf8(String::StrictConversionReplacingUnpairedSurrogatesWithFFFD); 468 if (utf8.length() > maxReasonSizeInBytes) { 469 exceptionState.throwDOMException(SyntaxError, "The message must not be greater than " + String::number(maxReasonSizeInBytes) + " bytes."); 470 return; 471 } 472 } 473 474 if (m_state == CLOSING || m_state == CLOSED) 475 return; 476 if (m_state == CONNECTING) { 477 m_state = CLOSING; 478 m_channel->fail("WebSocket is closed before the connection is established.", WarningMessageLevel); 479 return; 480 } 481 m_state = CLOSING; 482 if (m_channel) 483 m_channel->close(code, reason); 484} 485 486const KURL& WebSocket::url() const 487{ 488 return m_url; 489} 490 491WebSocket::State WebSocket::readyState() const 492{ 493 return m_state; 494} 495 496unsigned long WebSocket::bufferedAmount() const 497{ 498 return saturateAdd(m_bufferedAmount, m_bufferedAmountAfterClose); 499} 500 501String WebSocket::protocol() const 502{ 503 return m_subprotocol; 504} 505 506String WebSocket::extensions() const 507{ 508 return m_extensions; 509} 510 511String WebSocket::binaryType() const 512{ 513 switch (m_binaryType) { 514 case BinaryTypeBlob: 515 return "blob"; 516 case BinaryTypeArrayBuffer: 517 return "arraybuffer"; 518 } 519 ASSERT_NOT_REACHED(); 520 return String(); 521} 522 523void WebSocket::setBinaryType(const String& binaryType) 524{ 525 if (binaryType == "blob") { 526 m_binaryType = BinaryTypeBlob; 527 return; 528 } 529 if (binaryType == "arraybuffer") { 530 m_binaryType = BinaryTypeArrayBuffer; 531 return; 532 } 533 logError("'" + binaryType + "' is not a valid value for binaryType; binaryType remains unchanged."); 534} 535 536const AtomicString& WebSocket::interfaceName() const 537{ 538 return EventTargetNames::WebSocket; 539} 540 541ExecutionContext* WebSocket::executionContext() const 542{ 543 return ActiveDOMObject::executionContext(); 544} 545 546void WebSocket::contextDestroyed() 547{ 548 WTF_LOG(Network, "WebSocket %p contextDestroyed()", this); 549 ASSERT(!m_channel); 550 ASSERT(m_state == CLOSED); 551 ActiveDOMObject::contextDestroyed(); 552} 553 554bool WebSocket::hasPendingActivity() const 555{ 556 return m_state != CLOSED; 557} 558 559void WebSocket::suspend() 560{ 561 if (m_channel) 562 m_channel->suspend(); 563 m_eventQueue->suspend(); 564} 565 566void WebSocket::resume() 567{ 568 if (m_channel) 569 m_channel->resume(); 570 m_eventQueue->resume(); 571} 572 573void WebSocket::stop() 574{ 575 m_eventQueue->stop(); 576 577 if (!hasPendingActivity()) { 578 ASSERT(!m_channel); 579 return; 580 } 581 if (m_channel) { 582 m_channel->close(WebSocketChannel::CloseEventCodeGoingAway, String()); 583 m_channel->disconnect(); 584 m_channel = 0; 585 } 586 m_state = CLOSED; 587} 588 589void WebSocket::didConnect() 590{ 591 WTF_LOG(Network, "WebSocket %p didConnect()", this); 592 if (m_state != CONNECTING) 593 return; 594 m_state = OPEN; 595 m_subprotocol = m_channel->subprotocol(); 596 m_extensions = m_channel->extensions(); 597 m_eventQueue->dispatch(Event::create(EventTypeNames::open)); 598} 599 600void WebSocket::didReceiveMessage(const String& msg) 601{ 602 WTF_LOG(Network, "WebSocket %p didReceiveMessage() Text message '%s'", this, msg.utf8().data()); 603 if (m_state != OPEN) 604 return; 605 m_eventQueue->dispatch(MessageEvent::create(msg, SecurityOrigin::create(m_url)->toString())); 606} 607 608void WebSocket::didReceiveBinaryData(PassOwnPtr<Vector<char> > binaryData) 609{ 610 WTF_LOG(Network, "WebSocket %p didReceiveBinaryData() %lu byte binary message", this, static_cast<unsigned long>(binaryData->size())); 611 switch (m_binaryType) { 612 case BinaryTypeBlob: { 613 size_t size = binaryData->size(); 614 RefPtr<RawData> rawData = RawData::create(); 615 binaryData->swap(*rawData->mutableData()); 616 OwnPtr<BlobData> blobData = BlobData::create(); 617 blobData->appendData(rawData.release(), 0, BlobDataItem::toEndOfFile); 618 RefPtr<Blob> blob = Blob::create(BlobDataHandle::create(blobData.release(), size)); 619 m_eventQueue->dispatch(MessageEvent::create(blob.release(), SecurityOrigin::create(m_url)->toString())); 620 break; 621 } 622 623 case BinaryTypeArrayBuffer: 624 m_eventQueue->dispatch(MessageEvent::create(ArrayBuffer::create(binaryData->data(), binaryData->size()), SecurityOrigin::create(m_url)->toString())); 625 break; 626 } 627} 628 629void WebSocket::didReceiveMessageError() 630{ 631 WTF_LOG(Network, "WebSocket %p didReceiveMessageError()", this); 632 m_eventQueue->dispatch(Event::create(EventTypeNames::error)); 633} 634 635void WebSocket::didUpdateBufferedAmount(unsigned long bufferedAmount) 636{ 637 WTF_LOG(Network, "WebSocket %p didUpdateBufferedAmount() New bufferedAmount is %lu", this, bufferedAmount); 638 if (m_state == CLOSED) 639 return; 640 m_bufferedAmount = bufferedAmount; 641} 642 643void WebSocket::didStartClosingHandshake() 644{ 645 WTF_LOG(Network, "WebSocket %p didStartClosingHandshake()", this); 646 m_state = CLOSING; 647} 648 649void WebSocket::didClose(unsigned long unhandledBufferedAmount, ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason) 650{ 651 WTF_LOG(Network, "WebSocket %p didClose()", this); 652 if (!m_channel) 653 return; 654 bool wasClean = m_state == CLOSING && !unhandledBufferedAmount && closingHandshakeCompletion == ClosingHandshakeComplete && code != WebSocketChannel::CloseEventCodeAbnormalClosure; 655 656 // hasPendingActivity() returns false when m_state is CLOSED. So, it's 657 // possible that the wrapper gets garbage-collected during execution of the 658 // event. We need to keep this object alive to continue the rest of this 659 // method. Since the event we dispatch below sets "this" to the wrapper 660 // object, it doesn't get collected, but just to be sure, put a reference 661 // for protection. 662 // 663 // We can move m_channel clean up code before event dispatching, but it 664 // makes it harder to check correctness. Comparing cost, we now have this 665 // temporary reference. 666 RefPtr<WebSocket> protect(this); 667 668 m_state = CLOSED; 669 m_bufferedAmount = unhandledBufferedAmount; 670 m_eventQueue->dispatch(CloseEvent::create(wasClean, code, reason)); 671 672 if (m_channel) { 673 m_channel->disconnect(); 674 m_channel = 0; 675 } 676} 677 678size_t WebSocket::getFramingOverhead(size_t payloadSize) 679{ 680 static const size_t hybiBaseFramingOverhead = 2; // Every frame has at least two-byte header. 681 static const size_t hybiMaskingKeyLength = 4; // Every frame from client must have masking key. 682 static const size_t minimumPayloadSizeWithTwoByteExtendedPayloadLength = 126; 683 static const size_t minimumPayloadSizeWithEightByteExtendedPayloadLength = 0x10000; 684 size_t overhead = hybiBaseFramingOverhead + hybiMaskingKeyLength; 685 if (payloadSize >= minimumPayloadSizeWithEightByteExtendedPayloadLength) 686 overhead += 8; 687 else if (payloadSize >= minimumPayloadSizeWithTwoByteExtendedPayloadLength) 688 overhead += 2; 689 return overhead; 690} 691 692} // namespace WebCore 693