Changeset 122199 in webkit
- Timestamp:
- Jul 10, 2012 12:31:02 AM (12 years ago)
- Location:
- trunk
- Files:
-
- 1 deleted
- 15 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r122188 r122199 1 2012-07-10 Yuta Kitamura <yutak@chromium.org> 2 3 WebSocket: Remove hixie76 protocol implementation 4 https://bugs.webkit.org/show_bug.cgi?id=88620 5 6 Reviewed by Adam Barth. 7 8 Skip tests under hixie76 directory in all ports, because hixie-76 protocol support is 9 being dropped. These tests will be removed later. 10 11 * platform/chromium/TestExpectations: 12 * platform/gtk/TestExpectations: 13 * platform/mac/TestExpectations: 14 * platform/qt/TestExpectations: 15 * platform/win/Skipped: 16 * platform/wk2/Skipped: 17 1 18 2012-07-09 Kent Tamura <tkent@chromium.org> 2 19 -
trunk/LayoutTests/platform/chromium/TestExpectations
r122186 r122199 16 16 BUGCR24182 SLOW WIN : http/tests/local/file-url-sent-as-referer.html = PASS 17 17 BUGCR24182 SLOW WIN DEBUG : http/tests/misc/uncacheable-script-repeated.html = PASS 18 BUGCR24182 SLOW : http/tests/websocket/tests/hixie76/frame-lengths.html = PASS19 BUGCR24182 SLOW WIN DEBUG : http/tests/websocket/tests/hixie76/simple-stress.html = PASS20 18 BUGCR24182 SLOW WIN : http/tests/xmlhttprequest/simple-cross-origin-progress-events.html = PASS 21 19 BUGCR24182 SLOW WIN : http/tests/xmlhttprequest/supported-xml-content-types.html = PASS … … 509 507 WONTFIX SKIP : http/tests/xmlhttprequest/workers/shared-worker-referer.html = TIMEOUT 510 508 WONTFIX SKIP : http/tests/xmlhttprequest/workers/shared-worker-xhr-file-not-found.html = TIMEOUT 511 WONTFIX SKIP : http/tests/websocket/tests/hixie76/workers/close-in-shared-worker.html = TIMEOUT512 WONTFIX SKIP : http/tests/websocket/tests/hixie76/workers/shared-worker-simple.html = TIMEOUT513 509 WONTFIX SKIP : http/tests/websocket/tests/hybi/workers/close-in-shared-worker.html = TIMEOUT 514 510 WONTFIX SKIP : http/tests/websocket/tests/hybi/workers/shared-worker-simple.html = TIMEOUT -
trunk/LayoutTests/platform/win/Skipped
r121907 r122199 426 426 # Sometimes crashes http://webkit.org/b/62024 427 427 http/tests/media/video-cross-site.html 428 429 # Sometimes crashes http://webkit.org/b/48996430 http/tests/websocket/tests/hixie76/workers/close-in-onmessage-crash.html431 428 432 429 # Sometimes times out http://webkit.org/b/48997 … … 969 966 # Accept header is handled by the browser 970 967 http/tests/misc/image-checks-for-accept.html 971 972 # Tests timeout: https://bugs.webkit.org/show_bug.cgi?id=35041973 http/tests/websocket/tests/hixie76/frame-lengths.html974 http/tests/websocket/tests/hixie76/simple-stress.html975 968 976 969 # Needs platform specific API implemented in DRT, maybe not relevant for non-Mac and non-Windows ports -
trunk/Source/WebCore/ChangeLog
r122198 r122199 1 2012-07-10 Yuta Kitamura <yutak@chromium.org> 2 3 WebSocket: Remove hixie76 protocol implementation 4 https://bugs.webkit.org/show_bug.cgi?id=88620 5 6 Reviewed by Adam Barth. 7 8 This change removes code that implements the old hixie-76 WebSocket protocol which 9 isn't used anymore. 10 11 No new tests are added, because the code using the current protocol should not be 12 affected. Tests for hixie-76 protocol are skipped (these tests will be removed 13 eventually). 14 15 * Modules/websockets/ThreadableWebSocketChannel.h: 16 * Modules/websockets/ThreadableWebSocketChannelClientWrapper.cpp: 17 (WebCore::ThreadableWebSocketChannelClientWrapper::ThreadableWebSocketChannelClientWrapper): 18 (WebCore::ThreadableWebSocketChannelClientWrapper::didCreateWebSocketChannel): 19 * Modules/websockets/ThreadableWebSocketChannelClientWrapper.h: 20 (ThreadableWebSocketChannelClientWrapper): 21 * Modules/websockets/WebSocket.cpp: 22 (WebCore::WebSocket::WebSocket): 23 (WebCore::WebSocket::connect): 24 (WebCore::WebSocket::send): 25 (WebCore::WebSocket::protocol): 26 (WebCore::WebSocket::extensions): 27 (WebCore::WebSocket::binaryType): 28 (WebCore::WebSocket::setBinaryType): 29 (WebCore::WebSocket::didReceiveMessageError): 30 (WebCore::WebSocket::didClose): 31 (WebCore::WebSocket::getFramingOverhead): 32 * Modules/websockets/WebSocket.h: 33 * Modules/websockets/WebSocketChannel.cpp: 34 (WebCore::WebSocketChannel::WebSocketChannel): 35 (WebCore::WebSocketChannel::connect): 36 (WebCore::WebSocketChannel::send): 37 (WebCore::WebSocketChannel::fail): 38 (WebCore::WebSocketChannel::didCloseSocketStream): 39 (WebCore::WebSocketChannel::processBuffer): 40 (WebCore::WebSocketChannel::startClosingHandshake): 41 (WebCore::WebSocketChannel::enqueueTextFrame): 42 (WebCore::WebSocketChannel::enqueueRawFrame): 43 (WebCore::WebSocketChannel::enqueueBlobFrame): 44 (WebCore::WebSocketChannel::processOutgoingFrameQueue): 45 (WebCore::WebSocketChannel::abortOutgoingFrameQueue): 46 * Modules/websockets/WebSocketChannel.h: 47 (WebSocketChannel): 48 * Modules/websockets/WebSocketHandshake.cpp: 49 (WebCore::WebSocketHandshake::WebSocketHandshake): 50 (WebCore::WebSocketHandshake::clientHandshakeMessage): 51 (WebCore::WebSocketHandshake::clientHandshakeRequest): 52 (WebCore::WebSocketHandshake::readServerHandshake): 53 (WebCore::WebSocketHandshake::checkResponseHeaders): 54 * Modules/websockets/WebSocketHandshake.h: 55 * Modules/websockets/WorkerThreadableWebSocketChannel.cpp: 56 (WebCore::WorkerThreadableWebSocketChannel::WorkerContextDidInitializeTask::create): 57 (WebCore::WorkerThreadableWebSocketChannel::WorkerContextDidInitializeTask::WorkerContextDidInitializeTask): 58 (WorkerThreadableWebSocketChannel::WorkerContextDidInitializeTask): 59 (WebCore::WorkerThreadableWebSocketChannel::Bridge::mainThreadInitialize): 60 * Modules/websockets/WorkerThreadableWebSocketChannel.h: 61 (WorkerThreadableWebSocketChannel): 62 (Peer): 63 (Bridge): 64 1 65 2012-07-09 Gavin Barraclough <barraclough@apple.com> 2 66 -
trunk/Source/WebCore/Modules/websockets/ThreadableWebSocketChannel.h
r110012 r122199 57 57 }; 58 58 59 virtual bool useHixie76Protocol() = 0;60 59 virtual void connect(const KURL&, const String& protocol) = 0; 61 60 virtual String subprotocol() = 0; // Will be available after didConnect() callback is invoked. -
trunk/Source/WebCore/Modules/websockets/ThreadableWebSocketChannelClientWrapper.cpp
r120291 r122199 48 48 , m_failedWebSocketChannelCreation(false) 49 49 , m_syncMethodDone(true) 50 , m_useHixie76Protocol(true)51 50 , m_sendRequestResult(ThreadableWebSocketChannel::SendFail) 52 51 , m_bufferedAmount(0) … … 80 79 } 81 80 82 void ThreadableWebSocketChannelClientWrapper::didCreateWebSocketChannel(WorkerThreadableWebSocketChannel::Peer* peer , bool useHixie76Protocol)81 void ThreadableWebSocketChannelClientWrapper::didCreateWebSocketChannel(WorkerThreadableWebSocketChannel::Peer* peer) 83 82 { 84 83 m_peer = peer; 85 m_useHixie76Protocol = useHixie76Protocol;86 84 m_syncMethodDone = true; 87 85 } … … 100 98 { 101 99 m_failedWebSocketChannelCreation = true; 102 }103 104 bool ThreadableWebSocketChannelClientWrapper::useHixie76Protocol() const105 {106 return m_useHixie76Protocol;107 100 } 108 101 -
trunk/Source/WebCore/Modules/websockets/ThreadableWebSocketChannelClientWrapper.h
r120291 r122199 59 59 60 60 WorkerThreadableWebSocketChannel::Peer* peer() const; 61 void didCreateWebSocketChannel(WorkerThreadableWebSocketChannel::Peer* , bool useHixie76Protocol);61 void didCreateWebSocketChannel(WorkerThreadableWebSocketChannel::Peer*); 62 62 void clearPeer(); 63 63 … … 65 65 void setFailedWebSocketChannelCreation(); 66 66 67 // The value of useHixie76Protocol flag is cachable; this value is saved after WebSocketChannel (on the main 68 // thread) is constructed. 69 bool useHixie76Protocol() const; 70 71 // Subprotocol and extensions are cached too. Will be available when didConnect() callback is invoked. 67 // Subprotocol and extensions will be available when didConnect() callback is invoked. 72 68 String subprotocol() const; 73 69 void setSubprotocol(const String&); … … 113 109 bool m_failedWebSocketChannelCreation; 114 110 bool m_syncMethodDone; 115 bool m_useHixie76Protocol;116 111 // ThreadSafeRefCounted must not have String member variables. 117 112 Vector<UChar> m_subprotocol; -
trunk/Source/WebCore/Modules/websockets/WebSocket.cpp
r118723 r122199 90 90 } 91 91 92 static bool isValidProtocolStringHixie76(const String& protocol)93 {94 if (protocol.isNull())95 return true;96 if (protocol.isEmpty())97 return false;98 const UChar* characters = protocol.characters();99 for (size_t i = 0; i < protocol.length(); i++) {100 if (characters[i] < 0x20 || characters[i] > 0x7E)101 return false;102 }103 return true;104 }105 106 92 static String encodeProtocolString(const String& protocol) 107 93 { … … 159 145 , m_bufferedAmountAfterClose(0) 160 146 , m_binaryType(BinaryTypeBlob) 161 , m_useHixie76Protocol(true)162 147 , m_subprotocol("") 163 148 , m_extensions("") … … 231 216 232 217 m_channel = ThreadableWebSocketChannel::create(scriptExecutionContext(), this); 233 m_useHixie76Protocol = m_channel->useHixie76Protocol(); 234 235 String protocolString; 236 if (m_useHixie76Protocol) { 237 if (!protocols.isEmpty()) { 238 // Emulate JavaScript's Array.toString() behavior. 239 protocolString = joinStrings(protocols, ","); 240 } 241 if (!isValidProtocolStringHixie76(protocolString)) { 242 scriptExecutionContext()->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong protocol for WebSocket '" + encodeProtocolString(protocolString) + "'", scriptExecutionContext()->securityOrigin()->toString()); 218 219 // FIXME: There is a disagreement about restriction of subprotocols between WebSocket API and hybi-10 protocol 220 // draft. The former simply says "only characters in the range U+0021 to U+007E are allowed," while the latter 221 // imposes a stricter rule: "the elements MUST be non-empty strings with characters as defined in [RFC2616], 222 // and MUST all be unique strings." 223 // 224 // Here, we throw SYNTAX_ERR if the given protocols do not meet the latter criteria. This behavior does not 225 // comply with WebSocket API specification, but it seems to be the only reasonable way to handle this conflict. 226 for (size_t i = 0; i < protocols.size(); ++i) { 227 if (!isValidProtocolString(protocols[i])) { 228 scriptExecutionContext()->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong protocol for WebSocket '" + encodeProtocolString(protocols[i]) + "'", scriptExecutionContext()->securityOrigin()->toString()); 243 229 m_state = CLOSED; 244 230 ec = SYNTAX_ERR; 245 231 return; 246 232 } 247 } else { 248 // FIXME: There is a disagreement about restriction of subprotocols between WebSocket API and hybi-10 protocol 249 // draft. The former simply says "only characters in the range U+0021 to U+007E are allowed," while the latter 250 // imposes a stricter rule: "the elements MUST be non-empty strings with characters as defined in [RFC2616], 251 // and MUST all be unique strings." 252 // 253 // Here, we throw SYNTAX_ERR if the given protocols do not meet the latter criteria. This behavior does not 254 // comply with WebSocket API specification, but it seems to be the only reasonable way to handle this conflict. 255 for (size_t i = 0; i < protocols.size(); ++i) { 256 if (!isValidProtocolString(protocols[i])) { 257 scriptExecutionContext()->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong protocol for WebSocket '" + encodeProtocolString(protocols[i]) + "'", scriptExecutionContext()->securityOrigin()->toString()); 258 m_state = CLOSED; 259 ec = SYNTAX_ERR; 260 return; 261 } 233 } 234 HashSet<String> visited; 235 for (size_t i = 0; i < protocols.size(); ++i) { 236 if (visited.contains(protocols[i])) { 237 scriptExecutionContext()->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "WebSocket protocols contain duplicates: '" + encodeProtocolString(protocols[i]) + "'", scriptExecutionContext()->securityOrigin()->toString()); 238 m_state = CLOSED; 239 ec = SYNTAX_ERR; 240 return; 262 241 } 263 HashSet<String> visited; 264 for (size_t i = 0; i < protocols.size(); ++i) { 265 if (visited.contains(protocols[i])) { 266 scriptExecutionContext()->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "WebSocket protocols contain duplicates: '" + encodeProtocolString(protocols[i]) + "'", scriptExecutionContext()->securityOrigin()->toString()); 267 m_state = CLOSED; 268 ec = SYNTAX_ERR; 269 return; 270 } 271 visited.add(protocols[i]); 272 } 273 274 if (!protocols.isEmpty()) 275 protocolString = joinStrings(protocols, subProtocolSeperator()); 276 } 242 visited.add(protocols[i]); 243 } 244 245 String protocolString; 246 if (!protocols.isEmpty()) 247 protocolString = joinStrings(protocols, subProtocolSeperator()); 277 248 278 249 m_channel->connect(m_url, protocolString); … … 308 279 LOG(Network, "WebSocket %p send arraybuffer %p", this, binaryData); 309 280 ASSERT(binaryData); 310 if (m_useHixie76Protocol)311 return send("[object ArrayBuffer]", ec);312 281 if (m_state == CONNECTING) { 313 282 ec = INVALID_STATE_ERR; … … 328 297 LOG(Network, "WebSocket %p send blob %s", this, binaryData->url().string().utf8().data()); 329 298 ASSERT(binaryData); 330 if (m_useHixie76Protocol)331 return send("[object Blob]", ec);332 299 if (m_state == CONNECTING) { 333 300 ec = INVALID_STATE_ERR; … … 397 364 String WebSocket::protocol() const 398 365 { 399 if (m_useHixie76Protocol)400 return String();401 366 return m_subprotocol; 402 367 } … … 404 369 String WebSocket::extensions() const 405 370 { 406 if (m_useHixie76Protocol)407 return String();408 371 return m_extensions; 409 372 } … … 411 374 String WebSocket::binaryType() const 412 375 { 413 if (m_useHixie76Protocol)414 return String();415 376 switch (m_binaryType) { 416 377 case BinaryTypeBlob: … … 425 386 void WebSocket::setBinaryType(const String& binaryType, ExceptionCode& ec) 426 387 { 427 if (m_useHixie76Protocol)428 return;429 388 if (binaryType == "blob") { 430 389 m_binaryType = BinaryTypeBlob; … … 532 491 { 533 492 LOG(Network, "WebSocket %p didReceiveErrorMessage", this); 534 if (m_useHixie76Protocol && m_state != OPEN && m_state != CLOSING)535 return;536 493 ASSERT(scriptExecutionContext()); 537 494 dispatchEvent(Event::create(eventNames().errorEvent, false, false)); … … 557 514 if (!m_channel) 558 515 return; 559 bool wasClean = m_state == CLOSING && !unhandledBufferedAmount && closingHandshakeCompletion == ClosingHandshakeComplete; 560 if (!m_useHixie76Protocol) 561 wasClean = wasClean && code != WebSocketChannel::CloseEventCodeAbnormalClosure; 516 bool wasClean = m_state == CLOSING && !unhandledBufferedAmount && closingHandshakeCompletion == ClosingHandshakeComplete && code != WebSocketChannel::CloseEventCodeAbnormalClosure; 562 517 m_state = CLOSED; 563 518 m_bufferedAmount = unhandledBufferedAmount; … … 585 540 size_t WebSocket::getFramingOverhead(size_t payloadSize) 586 541 { 587 static const size_t hixie76FramingOverhead = 2; // Payload is surrounded by 0x00 and 0xFF.588 if (m_useHixie76Protocol)589 return hixie76FramingOverhead;590 591 542 static const size_t hybiBaseFramingOverhead = 2; // Every frame has at least two-byte header. 592 543 static const size_t hybiMaskingKeyLength = 4; // Every frame from client must have masking key. -
trunk/Source/WebCore/Modules/websockets/WebSocket.h
r112499 r122199 136 136 unsigned long m_bufferedAmountAfterClose; 137 137 BinaryType m_binaryType; 138 bool m_useHixie76Protocol;139 138 String m_subprotocol; 140 139 String m_extensions; -
trunk/Source/WebCore/Modules/websockets/WebSocketChannel.cpp
r118723 r122199 85 85 , m_unhandledBufferedAmount(0) 86 86 , m_identifier(0) 87 , m_useHixie76Protocol(true)88 87 , m_hasContinuousFrame(false) 89 88 , m_closeEventCode(CloseEventCodeAbnormalClosure) … … 93 92 #endif 94 93 { 95 if (Settings* settings = m_document->settings())96 m_useHixie76Protocol = settings->useHixie76WebSocketProtocol();97 98 94 if (Page* page = m_document->page()) 99 95 m_identifier = page->progress()->createUniqueIdentifier(); … … 103 99 { 104 100 fastFree(m_buffer); 105 }106 107 bool WebSocketChannel::useHixie76Protocol()108 {109 return m_useHixie76Protocol;110 101 } 111 102 … … 115 106 ASSERT(!m_handle); 116 107 ASSERT(!m_suspended); 117 m_handshake = adoptPtr(new WebSocketHandshake(url, protocol, m_document , m_useHixie76Protocol));108 m_handshake = adoptPtr(new WebSocketHandshake(url, protocol, m_document)); 118 109 m_handshake->reset(); 119 if ( !m_useHixie76Protocol &&m_deflateFramer.canDeflate())110 if (m_deflateFramer.canDeflate()) 120 111 m_handshake->addExtensionProcessor(m_deflateFramer.createExtensionProcessor()); 121 112 if (m_identifier) … … 153 144 if (utf8.isNull() && message.length()) 154 145 return InvalidMessage; 155 if (m_useHixie76Protocol) {156 return sendFrameHixie76(utf8.data(), utf8.length()) ? ThreadableWebSocketChannel::SendSuccess : ThreadableWebSocketChannel::SendFail;157 }158 146 enqueueTextFrame(utf8); 159 147 // According to WebSocket API specification, WebSocket.send() should return void instead … … 169 157 { 170 158 LOG(Network, "WebSocketChannel %p send arraybuffer %p", this, &binaryData); 171 ASSERT(!m_useHixie76Protocol);172 159 enqueueRawFrame(WebSocketFrame::OpCodeBinary, static_cast<const char*>(binaryData.data()), binaryData.byteLength()); 173 160 return ThreadableWebSocketChannel::SendSuccess; … … 177 164 { 178 165 LOG(Network, "WebSocketChannel %p send blob %s", this, binaryData.url().string().utf8().data()); 179 ASSERT(!m_useHixie76Protocol);180 166 enqueueBlobFrame(WebSocketFrame::OpCodeBinary, binaryData); 181 167 return ThreadableWebSocketChannel::SendSuccess; … … 185 171 { 186 172 LOG(Network, "WebSocketChannel %p send binary %p (%dB)", this, data, length); 187 ASSERT(!m_useHixie76Protocol);188 173 enqueueRawFrame(WebSocketFrame::OpCodeBinary, data, length); 189 174 return true; … … 217 202 m_document->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, reason, m_handshake->clientOrigin()); 218 203 } 219 if (!m_useHixie76Protocol) { 220 // Hybi-10 specification explicitly states we must not continue to handle incoming data 221 // once the WebSocket connection is failed (section 7.1.7). 222 // FIXME: Should we do this in hixie-76 too? 223 RefPtr<WebSocketChannel> protect(this); // The client can close the channel, potentially removing the last reference. 224 m_shouldDiscardReceivedData = true; 225 if (m_buffer) 226 skipBuffer(m_bufferSize); // Save memory. 227 m_deflateFramer.didFail(); 228 m_hasContinuousFrame = false; 229 m_continuousFrameData.clear(); 230 m_client->didReceiveMessageError(); 231 } 204 205 // Hybi-10 specification explicitly states we must not continue to handle incoming data 206 // once the WebSocket connection is failed (section 7.1.7). 207 RefPtr<WebSocketChannel> protect(this); // The client can close the channel, potentially removing the last reference. 208 m_shouldDiscardReceivedData = true; 209 if (m_buffer) 210 skipBuffer(m_bufferSize); // Save memory. 211 m_deflateFramer.didFail(); 212 m_hasContinuousFrame = false; 213 m_continuousFrameData.clear(); 214 m_client->didReceiveMessageError(); 215 232 216 if (m_handle && !m_closed) 233 217 m_handle->disconnect(); // Will call didClose(). … … 289 273 if (m_closingTimer.isActive()) 290 274 m_closingTimer.stop(); 291 if ( !m_useHixie76Protocol &&m_outgoingFrameQueueStatus != OutgoingFrameQueueClosed)275 if (m_outgoingFrameQueueStatus != OutgoingFrameQueueClosed) 292 276 abortOutgoingFrameQueue(); 293 277 if (m_handle) { … … 486 470 return false; 487 471 488 if (m_useHixie76Protocol)489 return processFrameHixie76();490 491 472 return processFrame(); 492 473 } … … 510 491 return; 511 492 ASSERT(m_handle); 512 if (m_useHixie76Protocol) { 513 Vector<char> buf; 514 buf.append('\xff'); 515 buf.append('\0'); 516 if (!m_handle->send(buf.data(), buf.size())) { 517 m_handle->disconnect(); 518 return; 519 } 520 } else { 521 Vector<char> buf; 522 if (!m_receivedClosingHandshake && code != CloseEventCodeNotSpecified) { 523 unsigned char highByte = code >> 8; 524 unsigned char lowByte = code; 525 buf.append(static_cast<char>(highByte)); 526 buf.append(static_cast<char>(lowByte)); 527 buf.append(reason.utf8().data(), reason.utf8().length()); 528 } 529 enqueueRawFrame(WebSocketFrame::OpCodeClose, buf.data(), buf.size()); 530 } 493 494 Vector<char> buf; 495 if (!m_receivedClosingHandshake && code != CloseEventCodeNotSpecified) { 496 unsigned char highByte = code >> 8; 497 unsigned char lowByte = code; 498 buf.append(static_cast<char>(highByte)); 499 buf.append(static_cast<char>(lowByte)); 500 buf.append(reason.utf8().data(), reason.utf8().length()); 501 } 502 enqueueRawFrame(WebSocketFrame::OpCodeClose, buf.data(), buf.size()); 503 531 504 m_closing = true; 532 505 if (m_client) … … 724 697 } 725 698 726 bool WebSocketChannel::processFrameHixie76()727 {728 const char* nextFrame = m_buffer;729 const char* p = m_buffer;730 const char* end = p + m_bufferSize;731 732 unsigned char frameByte = static_cast<unsigned char>(*p++);733 if ((frameByte & 0x80) == 0x80) {734 size_t length = 0;735 bool errorFrame = false;736 bool lengthFinished = false;737 while (p < end) {738 if (length > numeric_limits<size_t>::max() / 128) {739 LOG(Network, "frame length overflow %lu", static_cast<unsigned long>(length));740 errorFrame = true;741 break;742 }743 size_t newLength = length * 128;744 unsigned char msgByte = static_cast<unsigned char>(*p);745 unsigned int lengthMsgByte = msgByte & 0x7f;746 if (newLength > numeric_limits<size_t>::max() - lengthMsgByte) {747 LOG(Network, "frame length overflow %lu+%u", static_cast<unsigned long>(newLength), lengthMsgByte);748 errorFrame = true;749 break;750 }751 newLength += lengthMsgByte;752 if (newLength < length) { // sanity check753 LOG(Network, "frame length integer wrap %lu->%lu", static_cast<unsigned long>(length), static_cast<unsigned long>(newLength));754 errorFrame = true;755 break;756 }757 length = newLength;758 ++p;759 if (!(msgByte & 0x80)) {760 lengthFinished = true;761 break;762 }763 }764 if (!errorFrame && !lengthFinished)765 return false;766 if (p + length < p) {767 LOG(Network, "frame buffer pointer wrap %p+%lu->%p", p, static_cast<unsigned long>(length), p + length);768 errorFrame = true;769 }770 if (errorFrame) {771 skipBuffer(m_bufferSize); // Save memory.772 m_shouldDiscardReceivedData = true;773 m_client->didReceiveMessageError();774 fail("WebSocket frame length too large");775 return false;776 }777 ASSERT(p + length >= p);778 if (p + length <= end) {779 p += length;780 nextFrame = p;781 ASSERT(nextFrame > m_buffer);782 skipBuffer(nextFrame - m_buffer);783 if (frameByte == 0xff && !length) {784 m_receivedClosingHandshake = true;785 startClosingHandshake(CloseEventCodeNotSpecified, "");786 if (m_closing)787 m_handle->close(); // close after sending FF 00.788 } else789 m_client->didReceiveMessageError();790 return m_buffer;791 }792 return false;793 }794 795 const char* msgStart = p;796 while (p < end && *p != '\xff')797 ++p;798 if (p < end && *p == '\xff') {799 int msgLength = p - msgStart;800 ++p;801 nextFrame = p;802 if (frameByte == 0x00) {803 String msg = String::fromUTF8(msgStart, msgLength);804 skipBuffer(nextFrame - m_buffer);805 m_client->didReceiveMessage(msg);806 } else {807 skipBuffer(nextFrame - m_buffer);808 m_client->didReceiveMessageError();809 }810 return m_buffer;811 }812 return false;813 }814 815 699 void WebSocketChannel::enqueueTextFrame(const CString& string) 816 700 { 817 ASSERT(!m_useHixie76Protocol);818 701 ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen); 819 702 OwnPtr<QueuedFrame> frame = adoptPtr(new QueuedFrame); … … 827 710 void WebSocketChannel::enqueueRawFrame(WebSocketFrame::OpCode opCode, const char* data, size_t dataLength) 828 711 { 829 ASSERT(!m_useHixie76Protocol);830 712 ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen); 831 713 OwnPtr<QueuedFrame> frame = adoptPtr(new QueuedFrame); … … 841 723 void WebSocketChannel::enqueueBlobFrame(WebSocketFrame::OpCode opCode, const Blob& blob) 842 724 { 843 ASSERT(!m_useHixie76Protocol);844 725 ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen); 845 726 OwnPtr<QueuedFrame> frame = adoptPtr(new QueuedFrame); … … 853 734 void WebSocketChannel::processOutgoingFrameQueue() 854 735 { 855 ASSERT(!m_useHixie76Protocol);856 736 if (m_outgoingFrameQueueStatus == OutgoingFrameQueueClosed) 857 737 return; … … 918 798 void WebSocketChannel::abortOutgoingFrameQueue() 919 799 { 920 ASSERT(!m_useHixie76Protocol);921 800 m_outgoingFrameQueue.clear(); 922 801 m_outgoingFrameQueueStatus = OutgoingFrameQueueClosed; … … 949 828 } 950 829 951 bool WebSocketChannel::sendFrameHixie76(const char* data, size_t dataLength)952 {953 ASSERT(m_handle);954 ASSERT(!m_suspended);955 956 Vector<char> frame;957 frame.append('\0'); // Frame type.958 frame.append(data, dataLength);959 frame.append('\xff'); // Frame end.960 return m_handle->send(frame.data(), frame.size());961 }962 963 830 } // namespace WebCore 964 831 -
trunk/Source/WebCore/Modules/websockets/WebSocketChannel.h
r118723 r122199 69 69 70 70 // ThreadableWebSocketChannel functions. 71 virtual bool useHixie76Protocol() OVERRIDE;72 71 virtual void connect(const KURL&, const String& protocol) OVERRIDE; 73 72 virtual String subprotocol() OVERRIDE; … … 139 138 140 139 bool processFrame(); 141 bool processFrameHixie76();142 140 143 141 // It is allowed to send a Blob as a binary frame if hybi-10 protocol is in use. Sending a Blob … … 149 147 // in the order they were put into the queue. Sending request of a Blob blocks further processing 150 148 // until the Blob is completely read and sent to the socket stream. 151 //152 // When hixie-76 protocol is chosen, the queue is not used and messages are sent directly.153 149 enum QueuedFrameType { 154 150 QueuedFrameTypeString, … … 186 182 // instead of call sendFrame() directly. 187 183 bool sendFrame(WebSocketFrame::OpCode, const char* data, size_t dataLength); 188 bool sendFrameHixie76(const char* data, size_t dataLength);189 184 190 185 #if ENABLE(BLOB) … … 215 210 unsigned long m_identifier; // m_identifier == 0 means that we could not obtain a valid identifier. 216 211 217 bool m_useHixie76Protocol;218 219 212 // Private members only for hybi-10 protocol. 220 213 bool m_hasContinuousFrame; -
trunk/Source/WebCore/Modules/websockets/WebSocketHandshake.cpp
r113025 r122199 96 96 } 97 97 98 static uint32_t randomNumberLessThan(uint32_t n)99 {100 if (!n)101 return 0;102 if (n == std::numeric_limits<uint32_t>::max())103 return cryptographicallyRandomNumber();104 uint32_t max = std::numeric_limits<uint32_t>::max() - (std::numeric_limits<uint32_t>::max() % n);105 ASSERT(!(max % n));106 uint32_t v;107 do {108 v = cryptographicallyRandomNumber();109 } while (v >= max);110 return v % n;111 }112 113 static void generateHixie76SecWebSocketKey(uint32_t& number, String& key)114 {115 uint32_t space = randomNumberLessThan(12) + 1;116 uint32_t max = 4294967295U / space;117 number = randomNumberLessThan(max);118 uint32_t product = number * space;119 120 String s = String::number(product);121 int n = randomNumberLessThan(12) + 1;122 DEFINE_STATIC_LOCAL(String, randomChars, (randomCharacterInSecWebSocketKey));123 for (int i = 0; i < n; i++) {124 int pos = randomNumberLessThan(s.length() + 1);125 int chpos = randomNumberLessThan(randomChars.length());126 s.insert(randomChars.substring(chpos, 1), pos);127 }128 DEFINE_STATIC_LOCAL(String, spaceChar, (" "));129 for (uint32_t i = 0; i < space; i++) {130 int pos = randomNumberLessThan(s.length() - 1) + 1;131 s.insert(spaceChar, pos);132 }133 ASSERT(s[0] != ' ');134 ASSERT(s[s.length() - 1] != ' ');135 key = s;136 }137 138 static void generateHixie76Key3(unsigned char key3[8])139 {140 cryptographicallyRandomValues(key3, 8);141 }142 143 static void setChallengeNumber(unsigned char* buf, uint32_t number)144 {145 unsigned char* p = buf + 3;146 for (int i = 0; i < 4; i++) {147 *p = number & 0xFF;148 --p;149 number >>= 8;150 }151 }152 153 static void generateHixie76ExpectedChallengeResponse(uint32_t number1, uint32_t number2, unsigned char key3[8], unsigned char expectedChallenge[16])154 {155 unsigned char challenge[16];156 setChallengeNumber(&challenge[0], number1);157 setChallengeNumber(&challenge[4], number2);158 memcpy(&challenge[8], key3, 8);159 MD5 md5;160 md5.addBytes(challenge, sizeof(challenge));161 Vector<uint8_t, 16> digest;162 md5.checksum(digest);163 memcpy(expectedChallenge, digest.data(), 16);164 }165 166 98 static String generateSecWebSocketKey() 167 99 { … … 185 117 } 186 118 187 WebSocketHandshake::WebSocketHandshake(const KURL& url, const String& protocol, ScriptExecutionContext* context , bool useHixie76Protocol)119 WebSocketHandshake::WebSocketHandshake(const KURL& url, const String& protocol, ScriptExecutionContext* context) 188 120 : m_url(url) 189 121 , m_clientProtocol(protocol) 190 122 , m_secure(m_url.protocolIs("wss")) 191 123 , m_context(context) 192 , m_useHixie76Protocol(useHixie76Protocol)193 124 , m_mode(Incomplete) 194 125 { 195 if (m_useHixie76Protocol) { 196 uint32_t number1; 197 uint32_t number2; 198 generateHixie76SecWebSocketKey(number1, m_hixie76SecWebSocketKey1); 199 generateHixie76SecWebSocketKey(number2, m_hixie76SecWebSocketKey2); 200 generateHixie76Key3(m_hixie76Key3); 201 generateHixie76ExpectedChallengeResponse(number1, number2, m_hixie76Key3, m_hixie76ExpectedChallengeResponse); 202 } else { 203 m_secWebSocketKey = generateSecWebSocketKey(); 204 m_expectedAccept = getExpectedWebSocketAccept(m_secWebSocketKey); 205 } 126 m_secWebSocketKey = generateSecWebSocketKey(); 127 m_expectedAccept = getExpectedWebSocketAccept(m_secWebSocketKey); 206 128 } 207 129 … … 265 187 266 188 Vector<String> fields; 267 if (m_useHixie76Protocol) 268 fields.append("Upgrade: WebSocket"); 269 else 270 fields.append("Upgrade: websocket"); 189 fields.append("Upgrade: websocket"); 271 190 fields.append("Connection: Upgrade"); 272 191 fields.append("Host: " + hostName(m_url, m_secure)); … … 284 203 } 285 204 286 if (m_useHixie76Protocol) { 287 fields.append("Sec-WebSocket-Key1: " + m_hixie76SecWebSocketKey1); 288 fields.append("Sec-WebSocket-Key2: " + m_hixie76SecWebSocketKey2); 289 } else { 290 fields.append("Sec-WebSocket-Key: " + m_secWebSocketKey); 291 fields.append("Sec-WebSocket-Version: 13"); 292 const String extensionValue = m_extensionDispatcher.createHeaderValue(); 293 if (extensionValue.length()) 294 fields.append("Sec-WebSocket-Extensions: " + extensionValue); 295 } 205 fields.append("Sec-WebSocket-Key: " + m_secWebSocketKey); 206 fields.append("Sec-WebSocket-Version: 13"); 207 const String extensionValue = m_extensionDispatcher.createHeaderValue(); 208 if (extensionValue.length()) 209 fields.append("Sec-WebSocket-Extensions: " + extensionValue); 296 210 297 211 // Fields in the handshake are sent by the client in a random order; the … … 306 220 builder.append("\r\n"); 307 221 308 CString handshakeHeader = builder.toString().utf8(); 309 // Hybi-10 handshake is complete at this point. 310 if (!m_useHixie76Protocol) 311 return handshakeHeader; 312 // Hixie-76 protocol requires sending eight-byte data (so-called "key3") after the request header fields. 313 char* characterBuffer = 0; 314 CString msg = CString::newUninitialized(handshakeHeader.length() + sizeof(m_hixie76Key3), characterBuffer); 315 memcpy(characterBuffer, handshakeHeader.data(), handshakeHeader.length()); 316 memcpy(characterBuffer + handshakeHeader.length(), m_hixie76Key3, sizeof(m_hixie76Key3)); 317 return msg; 222 return builder.toString().utf8(); 318 223 } 319 224 … … 324 229 // m_key3 in WebSocketHandshakeRequest? 325 230 RefPtr<WebSocketHandshakeRequest> request = WebSocketHandshakeRequest::create("GET", m_url); 326 if (m_useHixie76Protocol) 327 request->addHeaderField("Upgrade", "WebSocket"); 328 else 329 request->addHeaderField("Upgrade", "websocket"); 231 request->addHeaderField("Upgrade", "websocket"); 330 232 request->addHeaderField("Connection", "Upgrade"); 331 233 request->addHeaderField("Host", hostName(m_url, m_secure)); … … 343 245 } 344 246 345 if (m_useHixie76Protocol) { 346 request->addHeaderField("Sec-WebSocket-Key1", m_hixie76SecWebSocketKey1); 347 request->addHeaderField("Sec-WebSocket-Key2", m_hixie76SecWebSocketKey2); 348 request->setKey3(m_hixie76Key3); 349 } else { 350 request->addHeaderField("Sec-WebSocket-Key", m_secWebSocketKey); 351 request->addHeaderField("Sec-WebSocket-Version", "13"); 352 const String extensionValue = m_extensionDispatcher.createHeaderValue(); 353 if (extensionValue.length()) 354 request->addHeaderField("Sec-WebSocket-Extensions", extensionValue); 355 } 247 request->addHeaderField("Sec-WebSocket-Key", m_secWebSocketKey); 248 request->addHeaderField("Sec-WebSocket-Version", "13"); 249 const String extensionValue = m_extensionDispatcher.createHeaderValue(); 250 if (extensionValue.length()) 251 request->addHeaderField("Sec-WebSocket-Extensions", extensionValue); 356 252 357 253 return request.release(); … … 407 303 } 408 304 409 if (!m_useHixie76Protocol) { // Hybi-10 handshake is complete at this point.410 m_mode = Connected;411 return p - header;412 }413 414 // In hixie-76 protocol, server's handshake contains sixteen-byte data (called "challenge response")415 // after the header fields.416 if (len < static_cast<size_t>(p - header + sizeof(m_hixie76ExpectedChallengeResponse))) {417 // Just hasn't been received /expected/ yet.418 m_mode = Incomplete;419 return -1;420 }421 422 m_response.setChallengeResponse(static_cast<const unsigned char*>(static_cast<const void*>(p)));423 if (memcmp(p, m_hixie76ExpectedChallengeResponse, sizeof(m_hixie76ExpectedChallengeResponse))) {424 m_mode = Failed;425 return (p - header) + sizeof(m_hixie76ExpectedChallengeResponse);426 }427 305 m_mode = Connected; 428 return (p - header) + sizeof(m_hixie76ExpectedChallengeResponse);306 return p - header; 429 307 } 430 308 … … 437 315 { 438 316 return m_failureReason; 439 }440 441 String WebSocketHandshake::serverWebSocketOrigin() const442 {443 return m_response.headerFields().get("sec-websocket-origin");444 }445 446 String WebSocketHandshake::serverWebSocketLocation() const447 {448 return m_response.headerFields().get("sec-websocket-location");449 317 } 450 318 … … 620 488 bool WebSocketHandshake::checkResponseHeaders() 621 489 { 622 const String& serverWebSocketLocation = this->serverWebSocketLocation();623 const String& serverWebSocketOrigin = this->serverWebSocketOrigin();624 490 const String& serverWebSocketProtocol = this->serverWebSocketProtocol(); 625 491 const String& serverUpgrade = this->serverUpgrade(); … … 635 501 return false; 636 502 } 637 if (m_useHixie76Protocol) { 638 if (serverWebSocketOrigin.isNull()) { 639 m_failureReason = "Error during WebSocket handshake: 'Sec-WebSocket-Origin' header is missing"; 640 return false; 641 } 642 if (serverWebSocketLocation.isNull()) { 643 m_failureReason = "Error during WebSocket handshake: 'Sec-WebSocket-Location' header is missing"; 644 return false; 645 } 646 } else { 647 if (serverWebSocketAccept.isNull()) { 648 m_failureReason = "Error during WebSocket handshake: 'Sec-WebSocket-Accept' header is missing"; 649 return false; 650 } 503 if (serverWebSocketAccept.isNull()) { 504 m_failureReason = "Error during WebSocket handshake: 'Sec-WebSocket-Accept' header is missing"; 505 return false; 651 506 } 652 507 … … 660 515 } 661 516 662 if (m_useHixie76Protocol) { 663 if (clientOrigin() != serverWebSocketOrigin) { 664 m_failureReason = "Error during WebSocket handshake: origin mismatch: " + clientOrigin() + " != " + serverWebSocketOrigin; 517 if (serverWebSocketAccept != m_expectedAccept) { 518 m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Accept mismatch"; 519 return false; 520 } 521 if (!serverWebSocketProtocol.isNull()) { 522 if (m_clientProtocol.isEmpty()) { 523 m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch"; 665 524 return false; 666 525 } 667 if (clientLocation() != serverWebSocketLocation) { 668 m_failureReason = "Error during WebSocket handshake: location mismatch: " + clientLocation() + " != " + serverWebSocketLocation; 526 Vector<String> result; 527 m_clientProtocol.split(String(WebSocket::subProtocolSeperator()), result); 528 if (!result.contains(serverWebSocketProtocol)) { 529 m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch"; 669 530 return false; 670 531 } 671 if (!m_clientProtocol.isEmpty() && m_clientProtocol != serverWebSocketProtocol) {672 m_failureReason = "Error during WebSocket handshake: protocol mismatch: " + m_clientProtocol + " != " + serverWebSocketProtocol;673 return false;674 }675 } else {676 if (serverWebSocketAccept != m_expectedAccept) {677 m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Accept mismatch";678 return false;679 }680 if (!serverWebSocketProtocol.isNull()) {681 if (m_clientProtocol.isEmpty()) {682 m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch";683 return false;684 }685 Vector<String> result;686 m_clientProtocol.split(String(WebSocket::subProtocolSeperator()), result);687 if (!result.contains(serverWebSocketProtocol)) {688 m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch";689 return false;690 }691 }692 532 } 693 533 return true; -
trunk/Source/WebCore/Modules/websockets/WebSocketHandshake.h
r113025 r122199 52 52 Incomplete, Normal, Failed, Connected 53 53 }; 54 WebSocketHandshake(const KURL&, const String& protocol, ScriptExecutionContext* , bool useHixie76Protocol);54 WebSocketHandshake(const KURL&, const String& protocol, ScriptExecutionContext*); 55 55 ~WebSocketHandshake(); 56 56 … … 77 77 String failureReason() const; // Returns a string indicating the reason of failure if mode() == Failed. 78 78 79 String serverWebSocketOrigin() const; // Only for hixie-76 handshake.80 String serverWebSocketLocation() const; // Only for hixie-76 handshake.81 79 String serverWebSocketProtocol() const; 82 80 String serverSetCookie() const; … … 84 82 String serverUpgrade() const; 85 83 String serverConnection() const; 86 String serverWebSocketAccept() const; // Only for hybi-10 handshake.84 String serverWebSocketAccept() const; 87 85 String acceptedExtensions() const; 88 86 … … 107 105 bool m_secure; 108 106 ScriptExecutionContext* m_context; 109 bool m_useHixie76Protocol;110 107 111 108 Mode m_mode; … … 115 112 String m_failureReason; 116 113 117 // For hixie-76 handshake.118 String m_hixie76SecWebSocketKey1;119 String m_hixie76SecWebSocketKey2;120 unsigned char m_hixie76Key3[8];121 unsigned char m_hixie76ExpectedChallengeResponse[16];122 123 // For hybi-10 handshake.124 114 String m_secWebSocketKey; 125 115 String m_expectedAccept; -
trunk/Source/WebCore/Modules/websockets/WorkerThreadableWebSocketChannel.cpp
r120291 r122199 67 67 } 68 68 69 bool WorkerThreadableWebSocketChannel::useHixie76Protocol()70 {71 ASSERT(m_workerClientWrapper);72 return m_workerClientWrapper->useHixie76Protocol();73 }74 75 69 void WorkerThreadableWebSocketChannel::connect(const KURL& url, const String& protocol) 76 70 { … … 165 159 if (m_mainWebSocketChannel) 166 160 m_mainWebSocketChannel->disconnect(); 167 }168 169 bool WorkerThreadableWebSocketChannel::Peer::useHixie76Protocol()170 {171 ASSERT(isMainThread());172 ASSERT(m_mainWebSocketChannel);173 return m_mainWebSocketChannel->useHixie76Protocol();174 161 } 175 162 … … 377 364 static PassOwnPtr<ScriptExecutionContext::Task> create(WorkerThreadableWebSocketChannel::Peer* peer, 378 365 WorkerLoaderProxy* loaderProxy, 379 PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, 380 bool useHixie76Protocol) 366 PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper) 381 367 { 382 return adoptPtr(new WorkerContextDidInitializeTask(peer, loaderProxy, workerClientWrapper , useHixie76Protocol));368 return adoptPtr(new WorkerContextDidInitializeTask(peer, loaderProxy, workerClientWrapper)); 383 369 } 384 370 … … 393 379 m_loaderProxy->postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadDestroy, peer.release())); 394 380 } else 395 m_workerClientWrapper->didCreateWebSocketChannel(m_peer , m_useHixie76Protocol);381 m_workerClientWrapper->didCreateWebSocketChannel(m_peer); 396 382 } 397 383 virtual bool isCleanupTask() const OVERRIDE { return true; } … … 400 386 WorkerContextDidInitializeTask(WorkerThreadableWebSocketChannel::Peer* peer, 401 387 WorkerLoaderProxy* loaderProxy, 402 PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, 403 bool useHixie76Protocol) 388 PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper) 404 389 : m_peer(peer) 405 390 , m_loaderProxy(loaderProxy) 406 391 , m_workerClientWrapper(workerClientWrapper) 407 , m_useHixie76Protocol(useHixie76Protocol)408 392 { 409 393 } … … 412 396 WorkerLoaderProxy* m_loaderProxy; 413 397 RefPtr<ThreadableWebSocketChannelClientWrapper> m_workerClientWrapper; 414 bool m_useHixie76Protocol;415 398 }; 416 399 … … 424 407 Peer* peer = Peer::create(clientWrapper, *loaderProxy, context, taskMode); 425 408 bool sent = loaderProxy->postTaskForModeToWorkerContext( 426 WorkerThreadableWebSocketChannel::WorkerContextDidInitializeTask::create(peer, loaderProxy, clientWrapper , peer->useHixie76Protocol()), taskMode);409 WorkerThreadableWebSocketChannel::WorkerContextDidInitializeTask::create(peer, loaderProxy, clientWrapper), taskMode); 427 410 if (!sent) { 428 411 clientWrapper->clearPeer(); -
trunk/Source/WebCore/Modules/websockets/WorkerThreadableWebSocketChannel.h
r120291 r122199 63 63 64 64 // ThreadableWebSocketChannel functions. 65 virtual bool useHixie76Protocol() OVERRIDE;66 65 virtual void connect(const KURL&, const String& protocol) OVERRIDE; 67 66 virtual String subprotocol() OVERRIDE; … … 88 87 ~Peer(); 89 88 90 bool useHixie76Protocol();91 89 void connect(const KURL&, const String& protocol); 92 90 void send(const String& message); … … 152 150 Bridge(PassRefPtr<ThreadableWebSocketChannelClientWrapper>, PassRefPtr<WorkerContext>, const String& taskMode); 153 151 154 static void setWebSocketChannel(ScriptExecutionContext*, Bridge* thisPtr, Peer*, PassRefPtr<ThreadableWebSocketChannelClientWrapper> , bool useHixie76Protocol);152 static void setWebSocketChannel(ScriptExecutionContext*, Bridge* thisPtr, Peer*, PassRefPtr<ThreadableWebSocketChannelClientWrapper>); 155 153 156 154 // Executed on the main thread to create a Peer for this bridge.
Note: See TracChangeset
for help on using the changeset viewer.