void WebSocket::connect(const KURL& url, const String& protocol, ExceptionCode& ec) { LOG(Network, "WebSocket %p connect to %s protocol=%s", this, url.string().utf8().data(), protocol.utf8().data()); m_url = url; m_protocol = protocol; if (!m_url.isValid()) { scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Invalid url for WebSocket " + url.string(), 0, scriptExecutionContext()->securityOrigin()->toString(), 0); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong url scheme for WebSocket " + url.string(), 0, scriptExecutionContext()->securityOrigin()->toString(), 0); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (m_url.hasFragmentIdentifier()) { scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "URL has fragment component " + url.string(), 0, scriptExecutionContext()->securityOrigin()->toString(), 0); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!isValidProtocolString(m_protocol)) { scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong protocol for WebSocket '" + encodeProtocolString(m_protocol) + "'", 0, scriptExecutionContext()->securityOrigin()->toString(), 0); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!portAllowed(url)) { scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, makeString("WebSocket port ", String::number(url.port()), " blocked"), 0, scriptExecutionContext()->securityOrigin()->toString(), 0); m_state = CLOSED; ec = SECURITY_ERR; return; } m_channel = ThreadableWebSocketChannel::create(scriptExecutionContext(), this, m_url, m_protocol); m_channel->connect(); ActiveDOMObject::setPendingActivity(this); }
void WebSocket::connect(const KURL& url, const String& protocol, ExceptionCode& ec) { LOG(Network, "WebSocket %p connect to %s protocol=%s", this, url.string().utf8().data(), protocol.utf8().data()); m_url = url; m_protocol = protocol; if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { LOG_ERROR("Error: wrong url for WebSocket %s", url.string().utf8().data()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!isValidProtocolString(m_protocol)) { LOG_ERROR("Error: wrong protocol for WebSocket %s", m_protocol.utf8().data()); m_state = CLOSED; ec = SYNTAX_ERR; return; } // FIXME: if m_url.port() is blocking port, raise SECURITY_ERR. m_channel = WebSocketChannel::create(scriptExecutionContext(), this, m_url, m_protocol); m_channel->connect(); }
void WebSocket::connect(const String& url, const Vector<String>& protocols, ExceptionCode& ec) { LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data()); m_url = URL(URL(), url); if (!m_url.isValid()) { scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Invalid url for WebSocket " + m_url.stringCenterEllipsizedToLength()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Wrong url scheme for WebSocket " + m_url.stringCenterEllipsizedToLength()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (m_url.hasFragmentIdentifier()) { scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "URL has fragment component " + m_url.stringCenterEllipsizedToLength()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!portAllowed(m_url)) { scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "WebSocket port " + String::number(m_url.port()) + " blocked"); m_state = CLOSED; ec = SECURITY_ERR; return; } // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved. bool shouldBypassMainWorldContentSecurityPolicy = false; if (is<Document>(*scriptExecutionContext())) { Document& document = downcast<Document>(*scriptExecutionContext()); shouldBypassMainWorldContentSecurityPolicy = document.frame()->script().shouldBypassMainWorldContentSecurityPolicy(); } if (!shouldBypassMainWorldContentSecurityPolicy && !scriptExecutionContext()->contentSecurityPolicy()->allowConnectToSource(m_url)) { m_state = CLOSED; // FIXME: Should this be throwing an exception? ec = SECURITY_ERR; return; } m_channel = ThreadableWebSocketChannel::create(scriptExecutionContext(), this); // FIXME: There is a disagreement about restriction of subprotocols between WebSocket API and hybi-10 protocol // draft. The former simply says "only characters in the range U+0021 to U+007E are allowed," while the latter // imposes a stricter rule: "the elements MUST be non-empty strings with characters as defined in [RFC2616], // and MUST all be unique strings." // // Here, we throw SYNTAX_ERR if the given protocols do not meet the latter criteria. This behavior does not // comply with WebSocket API specification, but it seems to be the only reasonable way to handle this conflict. for (auto& protocol : protocols) { if (!isValidProtocolString(protocol)) { scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Wrong protocol for WebSocket '" + encodeProtocolString(protocol) + "'"); m_state = CLOSED; ec = SYNTAX_ERR; return; } } HashSet<String> visited; for (auto& protocol : protocols) { if (!visited.add(protocol).isNewEntry) { scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "WebSocket protocols contain duplicates: '" + encodeProtocolString(protocol) + "'"); m_state = CLOSED; ec = SYNTAX_ERR; return; } } if (is<Document>(*scriptExecutionContext())) { Document& document = downcast<Document>(*scriptExecutionContext()); if (!document.frame()->loader().mixedContentChecker().canRunInsecureContent(document.securityOrigin(), m_url)) { // Balanced by the call to ActiveDOMObject::unsetPendingActivity() in WebSocket::stop(). ActiveDOMObject::setPendingActivity(this); // We must block this connection. Instead of throwing an exception, we indicate this // using the error event. But since this code executes as part of the WebSocket's // constructor, we have to wait until the constructor has completed before firing the // event; otherwise, users can't connect to the event. RunLoop::main().dispatch([this]() { dispatchEvent(Event::create(eventNames().errorEvent, false, false)); stop(); }); return; } } String protocolString; if (!protocols.isEmpty()) protocolString = joinStrings(protocols, subProtocolSeperator()); m_channel->connect(m_url, protocolString); ActiveDOMObject::setPendingActivity(this); }
void WebSocket::connect(const String& url, const Vector<String>& protocols, ExceptionCode& ec) { LOG(Network, "WebSocket %p connect to %s", this, url.utf8().data()); m_url = KURL(KURL(), url); if (!m_url.isValid()) { scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Invalid url for WebSocket " + m_url.string(), 0, scriptExecutionContext()->securityOrigin()->toString(), 0); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong url scheme for WebSocket " + m_url.string(), 0, scriptExecutionContext()->securityOrigin()->toString(), 0); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (m_url.hasFragmentIdentifier()) { scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "URL has fragment component " + m_url.string(), 0, scriptExecutionContext()->securityOrigin()->toString(), 0); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!portAllowed(m_url)) { scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "WebSocket port " + String::number(m_url.port()) + " blocked", 0, scriptExecutionContext()->securityOrigin()->toString(), 0); m_state = CLOSED; ec = SECURITY_ERR; return; } if (!scriptExecutionContext()->contentSecurityPolicy()->allowConnectFromSource(m_url)) { m_state = CLOSED; // FIXME: Should this be throwing an exception? ec = SECURITY_ERR; return; } m_channel = ThreadableWebSocketChannel::create(scriptExecutionContext(), this); m_useHixie76Protocol = m_channel->useHixie76Protocol(); String protocolString; if (m_useHixie76Protocol) { if (!protocols.isEmpty()) { // Emulate JavaScript's Array.toString() behavior. protocolString = joinStrings(protocols, ","); } if (!isValidProtocolStringHixie76(protocolString)) { scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong protocol for WebSocket '" + encodeProtocolString(protocolString) + "'", 0, scriptExecutionContext()->securityOrigin()->toString(), 0); m_state = CLOSED; ec = SYNTAX_ERR; return; } } else { // FIXME: There is a disagreement about restriction of subprotocols between WebSocket API and hybi-10 protocol // draft. The former simply says "only characters in the range U+0021 to U+007E are allowed," while the latter // imposes a stricter rule: "the elements MUST be non-empty strings with characters as defined in [RFC2616], // and MUST all be unique strings." // // Here, we throw SYNTAX_ERR if the given protocols do not meet the latter criteria. This behavior does not // comply with WebSocket API specification, but it seems to be the only reasonable way to handle this conflict. for (size_t i = 0; i < protocols.size(); ++i) { if (!isValidProtocolString(protocols[i])) { scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong protocol for WebSocket '" + encodeProtocolString(protocols[i]) + "'", 0, scriptExecutionContext()->securityOrigin()->toString(), 0); m_state = CLOSED; ec = SYNTAX_ERR; return; } } HashSet<String> visited; for (size_t i = 0; i < protocols.size(); ++i) { if (visited.contains(protocols[i])) { scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "WebSocket protocols contain duplicates: '" + encodeProtocolString(protocols[i]) + "'", 0, scriptExecutionContext()->securityOrigin()->toString(), 0); m_state = CLOSED; ec = SYNTAX_ERR; return; } visited.add(protocols[i]); } if (!protocols.isEmpty()) protocolString = joinStrings(protocols, ", "); } m_channel->connect(m_url, protocolString); ActiveDOMObject::setPendingActivity(this); }
void WebSocket::connect(const String& url, const Vector<String>& protocols, ExceptionCode& ec) { LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data()); m_url = KURL(KURL(), url); if (!m_url.isValid()) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Invalid url for WebSocket " + m_url.elidedString()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Wrong url scheme for WebSocket " + m_url.elidedString()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (m_url.hasFragmentIdentifier()) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "URL has fragment component " + m_url.elidedString()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!portAllowed(m_url)) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "WebSocket port " + String::number(m_url.port()) + " blocked"); m_state = CLOSED; ec = SECURITY_ERR; return; } // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved. bool shouldBypassMainWorldContentSecurityPolicy = false; if (scriptExecutionContext()->isDocument()) { Document* document = toDocument(scriptExecutionContext()); shouldBypassMainWorldContentSecurityPolicy = document->frame()->script()->shouldBypassMainWorldContentSecurityPolicy(); } if (!shouldBypassMainWorldContentSecurityPolicy && !scriptExecutionContext()->contentSecurityPolicy()->allowConnectToSource(m_url)) { m_state = CLOSED; // FIXME: Should this be throwing an exception? ec = SECURITY_ERR; return; } m_channel = WebSocketChannel::create(scriptExecutionContext(), this); // FIXME: There is a disagreement about restriction of subprotocols between WebSocket API and hybi-10 protocol // draft. The former simply says "only characters in the range U+0021 to U+007E are allowed," while the latter // imposes a stricter rule: "the elements MUST be non-empty strings with characters as defined in [RFC2616], // and MUST all be unique strings." // // Here, we throw SYNTAX_ERR if the given protocols do not meet the latter criteria. This behavior does not // comply with WebSocket API specification, but it seems to be the only reasonable way to handle this conflict. for (size_t i = 0; i < protocols.size(); ++i) { if (!isValidProtocolString(protocols[i])) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Wrong protocol for WebSocket '" + encodeProtocolString(protocols[i]) + "'"); m_state = CLOSED; ec = SYNTAX_ERR; return; } } HashSet<String> visited; for (size_t i = 0; i < protocols.size(); ++i) { if (visited.contains(protocols[i])) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "WebSocket protocols contain duplicates: '" + encodeProtocolString(protocols[i]) + "'"); m_state = CLOSED; ec = SYNTAX_ERR; return; } visited.add(protocols[i]); } String protocolString; if (!protocols.isEmpty()) protocolString = joinStrings(protocols, subProtocolSeperator()); m_channel->connect(m_url, protocolString); ActiveDOMObject::setPendingActivity(this); }
void WebSocket::connect(const String& url, const Vector<String>& protocols, ExceptionState& exceptionState) { WTF_LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data()); m_url = KURL(KURL(), url); if (!m_url.isValid()) { m_state = CLOSED; exceptionState.throwDOMException(SyntaxError, "The URL '" + url + "' is invalid."); return; } if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { m_state = CLOSED; exceptionState.throwDOMException(SyntaxError, "The URL's scheme must be either 'ws' or 'wss'. '" + m_url.protocol() + "' is not allowed."); return; } if (MixedContentChecker::isMixedContent(executionContext()->securityOrigin(), m_url)) { // FIXME: Throw an exception and close the connection. String message = "Connecting to a non-secure WebSocket server from a secure origin is deprecated."; executionContext()->addConsoleMessage(JSMessageSource, WarningMessageLevel, message); } if (m_url.hasFragmentIdentifier()) { m_state = CLOSED; exceptionState.throwDOMException(SyntaxError, "The URL contains a fragment identifier ('" + m_url.fragmentIdentifier() + "'). Fragment identifiers are not allowed in WebSocket URLs."); return; } if (!portAllowed(m_url)) { m_state = CLOSED; exceptionState.throwSecurityError("The port " + String::number(m_url.port()) + " is not allowed."); return; } // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved. bool shouldBypassMainWorldContentSecurityPolicy = false; if (executionContext()->isDocument()) { Document* document = toDocument(executionContext()); shouldBypassMainWorldContentSecurityPolicy = document->frame()->script().shouldBypassMainWorldContentSecurityPolicy(); } if (!shouldBypassMainWorldContentSecurityPolicy && !executionContext()->contentSecurityPolicy()->allowConnectToSource(m_url)) { m_state = CLOSED; // The URL is safe to expose to JavaScript, as this check happens synchronously before redirection. exceptionState.throwSecurityError("Refused to connect to '" + m_url.elidedString() + "' because it violates the document's Content Security Policy."); return; } m_channel = WebSocketChannel::create(executionContext(), this); // FIXME: There is a disagreement about restriction of subprotocols between WebSocket API and hybi-10 protocol // draft. The former simply says "only characters in the range U+0021 to U+007E are allowed," while the latter // imposes a stricter rule: "the elements MUST be non-empty strings with characters as defined in [RFC2616], // and MUST all be unique strings." // // Here, we throw SyntaxError if the given protocols do not meet the latter criteria. This behavior does not // comply with WebSocket API specification, but it seems to be the only reasonable way to handle this conflict. for (size_t i = 0; i < protocols.size(); ++i) { if (!isValidProtocolString(protocols[i])) { m_state = CLOSED; exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeProtocolString(protocols[i]) + "' is invalid."); releaseChannel(); return; } } HashSet<String> visited; for (size_t i = 0; i < protocols.size(); ++i) { if (!visited.add(protocols[i]).isNewEntry) { m_state = CLOSED; exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeProtocolString(protocols[i]) + "' is duplicated."); releaseChannel(); return; } } String protocolString; if (!protocols.isEmpty()) protocolString = joinStrings(protocols, subProtocolSeperator()); m_channel->connect(m_url, protocolString); }