| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2011 Google Inc. All rights reserved. | 2 * Copyright (C) 2011 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 */ | 29 */ |
| 30 | 30 |
| 31 #include "config.h" | 31 #include "config.h" |
| 32 | 32 |
| 33 #include "modules/websockets/WebSocket.h" | 33 #include "modules/websockets/WebSocket.h" |
| 34 | 34 |
| 35 #include "bindings/v8/ExceptionMessages.h" | |
| 36 #include "bindings/v8/ExceptionState.h" | 35 #include "bindings/v8/ExceptionState.h" |
| 37 #include "bindings/v8/ScriptController.h" | 36 #include "bindings/v8/ScriptController.h" |
| 38 #include "core/dom/Document.h" | 37 #include "core/dom/Document.h" |
| 39 #include "core/dom/ExceptionCode.h" | 38 #include "core/dom/ExceptionCode.h" |
| 40 #include "core/dom/ExecutionContext.h" | 39 #include "core/dom/ExecutionContext.h" |
| 41 #include "core/events/Event.h" | 40 #include "core/events/Event.h" |
| 42 #include "core/events/MessageEvent.h" | 41 #include "core/events/MessageEvent.h" |
| 43 #include "core/fileapi/Blob.h" | 42 #include "core/fileapi/Blob.h" |
| 44 #include "core/frame/ConsoleTypes.h" | 43 #include "core/frame/ConsoleTypes.h" |
| 45 #include "core/frame/ContentSecurityPolicy.h" | 44 #include "core/frame/ContentSecurityPolicy.h" |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 199 | 198 |
| 200 static unsigned long saturateAdd(unsigned long a, unsigned long b) | 199 static unsigned long saturateAdd(unsigned long a, unsigned long b) |
| 201 { | 200 { |
| 202 if (numeric_limits<unsigned long>::max() - a < b) | 201 if (numeric_limits<unsigned long>::max() - a < b) |
| 203 return numeric_limits<unsigned long>::max(); | 202 return numeric_limits<unsigned long>::max(); |
| 204 return a + b; | 203 return a + b; |
| 205 } | 204 } |
| 206 | 205 |
| 207 static void setInvalidStateErrorForSendMethod(ExceptionState& exceptionState) | 206 static void setInvalidStateErrorForSendMethod(ExceptionState& exceptionState) |
| 208 { | 207 { |
| 209 exceptionState.throwDOMException(InvalidStateError, ExceptionMessages::faile
dToExecute("send", "WebSocket", "Still in CONNECTING state.")); | 208 exceptionState.throwDOMException(InvalidStateError, "Still in CONNECTING sta
te."); |
| 210 } | 209 } |
| 211 | 210 |
| 212 const char* WebSocket::subProtocolSeperator() | 211 const char* WebSocket::subProtocolSeperator() |
| 213 { | 212 { |
| 214 return ", "; | 213 return ", "; |
| 215 } | 214 } |
| 216 | 215 |
| 217 WebSocket::WebSocket(ExecutionContext* context) | 216 WebSocket::WebSocket(ExecutionContext* context) |
| 218 : ActiveDOMObject(context) | 217 : ActiveDOMObject(context) |
| 219 , m_state(CONNECTING) | 218 , m_state(CONNECTING) |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 281 connect(url, protocols, exceptionState); | 280 connect(url, protocols, exceptionState); |
| 282 } | 281 } |
| 283 | 282 |
| 284 void WebSocket::connect(const String& url, const Vector<String>& protocols, Exce
ptionState& exceptionState) | 283 void WebSocket::connect(const String& url, const Vector<String>& protocols, Exce
ptionState& exceptionState) |
| 285 { | 284 { |
| 286 WTF_LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data())
; | 285 WTF_LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data())
; |
| 287 m_url = KURL(KURL(), url); | 286 m_url = KURL(KURL(), url); |
| 288 | 287 |
| 289 if (!m_url.isValid()) { | 288 if (!m_url.isValid()) { |
| 290 m_state = CLOSED; | 289 m_state = CLOSED; |
| 291 exceptionState.throwDOMException(SyntaxError, ExceptionMessages::failedT
oExecute("connect", "WebSocket", "The URL '" + url + "' is invalid.")); | 290 exceptionState.throwDOMException(SyntaxError, "The URL '" + url + "' is
invalid."); |
| 292 return; | 291 return; |
| 293 } | 292 } |
| 294 if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { | 293 if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { |
| 295 m_state = CLOSED; | 294 m_state = CLOSED; |
| 296 exceptionState.throwDOMException(SyntaxError, ExceptionMessages::failedT
oExecute("connect", "WebSocket", "The URL's scheme must be either 'ws' or 'wss'.
'" + m_url.protocol() + "' is not allowed.")); | 295 exceptionState.throwDOMException(SyntaxError, "The URL's scheme must be
either 'ws' or 'wss'. '" + m_url.protocol() + "' is not allowed."); |
| 297 return; | 296 return; |
| 298 } | 297 } |
| 299 if (m_url.hasFragmentIdentifier()) { | 298 if (m_url.hasFragmentIdentifier()) { |
| 300 m_state = CLOSED; | 299 m_state = CLOSED; |
| 301 exceptionState.throwDOMException(SyntaxError, ExceptionMessages::failedT
oExecute("connect", "WebSocket", "The URL contains a fragment identifier ('" + m
_url.fragmentIdentifier() + "'). Fragment identifiers are not allowed in WebSock
et URLs.")); | 300 exceptionState.throwDOMException(SyntaxError, "The URL contains a fragme
nt identifier ('" + m_url.fragmentIdentifier() + "'). Fragment identifiers are n
ot allowed in WebSocket URLs."); |
| 302 return; | 301 return; |
| 303 } | 302 } |
| 304 if (!portAllowed(m_url)) { | 303 if (!portAllowed(m_url)) { |
| 305 m_state = CLOSED; | 304 m_state = CLOSED; |
| 306 exceptionState.throwSecurityError(ExceptionMessages::failedToExecute("co
nnect", "WebSocket", "The port " + String::number(m_url.port()) + " is not allow
ed.")); | 305 exceptionState.throwSecurityError("The port " + String::number(m_url.por
t()) + " is not allowed."); |
| 307 return; | 306 return; |
| 308 } | 307 } |
| 309 | 308 |
| 310 // FIXME: Convert this to check the isolated world's Content Security Policy
once webkit.org/b/104520 is solved. | 309 // FIXME: Convert this to check the isolated world's Content Security Policy
once webkit.org/b/104520 is solved. |
| 311 bool shouldBypassMainWorldContentSecurityPolicy = false; | 310 bool shouldBypassMainWorldContentSecurityPolicy = false; |
| 312 if (executionContext()->isDocument()) { | 311 if (executionContext()->isDocument()) { |
| 313 Document* document = toDocument(executionContext()); | 312 Document* document = toDocument(executionContext()); |
| 314 shouldBypassMainWorldContentSecurityPolicy = document->frame()->script()
.shouldBypassMainWorldContentSecurityPolicy(); | 313 shouldBypassMainWorldContentSecurityPolicy = document->frame()->script()
.shouldBypassMainWorldContentSecurityPolicy(); |
| 315 } | 314 } |
| 316 if (!shouldBypassMainWorldContentSecurityPolicy && !executionContext()->cont
entSecurityPolicy()->allowConnectToSource(m_url)) { | 315 if (!shouldBypassMainWorldContentSecurityPolicy && !executionContext()->cont
entSecurityPolicy()->allowConnectToSource(m_url)) { |
| 317 m_state = CLOSED; | 316 m_state = CLOSED; |
| 318 // The URL is safe to expose to JavaScript, as this check happens synchr
onously before redirection. | 317 // The URL is safe to expose to JavaScript, as this check happens synchr
onously before redirection. |
| 319 exceptionState.throwSecurityError(ExceptionMessages::failedToExecute("co
nnect", "WebSocket", "Refused to connect to '" + m_url.elidedString() + "' becau
se it violates the document's Content Security Policy.")); | 318 exceptionState.throwSecurityError("Refused to connect to '" + m_url.elid
edString() + "' because it violates the document's Content Security Policy."); |
| 320 return; | 319 return; |
| 321 } | 320 } |
| 322 | 321 |
| 323 m_channel = WebSocketChannel::create(executionContext(), this); | 322 m_channel = WebSocketChannel::create(executionContext(), this); |
| 324 | 323 |
| 325 // FIXME: There is a disagreement about restriction of subprotocols between
WebSocket API and hybi-10 protocol | 324 // FIXME: There is a disagreement about restriction of subprotocols between
WebSocket API and hybi-10 protocol |
| 326 // draft. The former simply says "only characters in the range U+0021 to U+0
07E are allowed," while the latter | 325 // draft. The former simply says "only characters in the range U+0021 to U+0
07E are allowed," while the latter |
| 327 // imposes a stricter rule: "the elements MUST be non-empty strings with cha
racters as defined in [RFC2616], | 326 // imposes a stricter rule: "the elements MUST be non-empty strings with cha
racters as defined in [RFC2616], |
| 328 // and MUST all be unique strings." | 327 // and MUST all be unique strings." |
| 329 // | 328 // |
| 330 // Here, we throw SyntaxError if the given protocols do not meet the latter
criteria. This behavior does not | 329 // Here, we throw SyntaxError if the given protocols do not meet the latter
criteria. This behavior does not |
| 331 // comply with WebSocket API specification, but it seems to be the only reas
onable way to handle this conflict. | 330 // comply with WebSocket API specification, but it seems to be the only reas
onable way to handle this conflict. |
| 332 for (size_t i = 0; i < protocols.size(); ++i) { | 331 for (size_t i = 0; i < protocols.size(); ++i) { |
| 333 if (!isValidProtocolString(protocols[i])) { | 332 if (!isValidProtocolString(protocols[i])) { |
| 334 m_state = CLOSED; | 333 m_state = CLOSED; |
| 335 exceptionState.throwDOMException(SyntaxError, ExceptionMessages::fai
ledToExecute("connect", "WebSocket", "The subprotocol '" + encodeProtocolString(
protocols[i]) + "' is invalid.")); | 334 exceptionState.throwDOMException(SyntaxError, "The subprotocol '" +
encodeProtocolString(protocols[i]) + "' is invalid."); |
| 336 return; | 335 return; |
| 337 } | 336 } |
| 338 } | 337 } |
| 339 HashSet<String> visited; | 338 HashSet<String> visited; |
| 340 for (size_t i = 0; i < protocols.size(); ++i) { | 339 for (size_t i = 0; i < protocols.size(); ++i) { |
| 341 if (!visited.add(protocols[i]).isNewEntry) { | 340 if (!visited.add(protocols[i]).isNewEntry) { |
| 342 m_state = CLOSED; | 341 m_state = CLOSED; |
| 343 exceptionState.throwDOMException(SyntaxError, ExceptionMessages::fai
ledToExecute("connect", "WebSocket", "The subprotocol '" + encodeProtocolString(
protocols[i]) + "' is duplicated.")); | 342 exceptionState.throwDOMException(SyntaxError, "The subprotocol '" +
encodeProtocolString(protocols[i]) + "' is duplicated."); |
| 344 return; | 343 return; |
| 345 } | 344 } |
| 346 } | 345 } |
| 347 | 346 |
| 348 String protocolString; | 347 String protocolString; |
| 349 if (!protocols.isEmpty()) | 348 if (!protocols.isEmpty()) |
| 350 protocolString = joinStrings(protocols, subProtocolSeperator()); | 349 protocolString = joinStrings(protocols, subProtocolSeperator()); |
| 351 | 350 |
| 352 m_channel->connect(m_url, protocolString); | 351 m_channel->connect(m_url, protocolString); |
| 353 } | 352 } |
| 354 | 353 |
| 355 void WebSocket::handleSendResult(WebSocketChannel::SendResult result, ExceptionS
tate& exceptionState) | 354 void WebSocket::handleSendResult(WebSocketChannel::SendResult result, ExceptionS
tate& exceptionState) |
| 356 { | 355 { |
| 357 switch (result) { | 356 switch (result) { |
| 358 case WebSocketChannel::InvalidMessage: | 357 case WebSocketChannel::InvalidMessage: |
| 359 exceptionState.throwDOMException(SyntaxError, ExceptionMessages::failedT
oExecute("send", "WebSocket", "The message contains invalid characters.")); | 358 exceptionState.throwDOMException(SyntaxError, "The message contains inva
lid characters."); |
| 360 return; | 359 return; |
| 361 case WebSocketChannel::SendFail: | 360 case WebSocketChannel::SendFail: |
| 362 logError("WebSocket send() failed."); | 361 logError("WebSocket send() failed."); |
| 363 return; | 362 return; |
| 364 case WebSocketChannel::SendSuccess: | 363 case WebSocketChannel::SendSuccess: |
| 365 return; | 364 return; |
| 366 } | 365 } |
| 367 ASSERT_NOT_REACHED(); | 366 ASSERT_NOT_REACHED(); |
| 368 } | 367 } |
| 369 | 368 |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 455 closeInternal(code, String(), exceptionState); | 454 closeInternal(code, String(), exceptionState); |
| 456 } | 455 } |
| 457 | 456 |
| 458 void WebSocket::closeInternal(int code, const String& reason, ExceptionState& ex
ceptionState) | 457 void WebSocket::closeInternal(int code, const String& reason, ExceptionState& ex
ceptionState) |
| 459 { | 458 { |
| 460 if (code == WebSocketChannel::CloseEventCodeNotSpecified) { | 459 if (code == WebSocketChannel::CloseEventCodeNotSpecified) { |
| 461 WTF_LOG(Network, "WebSocket %p close() without code and reason", this); | 460 WTF_LOG(Network, "WebSocket %p close() without code and reason", this); |
| 462 } else { | 461 } else { |
| 463 WTF_LOG(Network, "WebSocket %p close() code=%d reason='%s'", this, code,
reason.utf8().data()); | 462 WTF_LOG(Network, "WebSocket %p close() code=%d reason='%s'", this, code,
reason.utf8().data()); |
| 464 if (!(code == WebSocketChannel::CloseEventCodeNormalClosure || (WebSocke
tChannel::CloseEventCodeMinimumUserDefined <= code && code <= WebSocketChannel::
CloseEventCodeMaximumUserDefined))) { | 463 if (!(code == WebSocketChannel::CloseEventCodeNormalClosure || (WebSocke
tChannel::CloseEventCodeMinimumUserDefined <= code && code <= WebSocketChannel::
CloseEventCodeMaximumUserDefined))) { |
| 465 exceptionState.throwDOMException(InvalidAccessError, ExceptionMessag
es::failedToExecute("close", "WebSocket", "The code must be either 1000, or betw
een 3000 and 4999. " + String::number(code) + " is neither.")); | 464 exceptionState.throwDOMException(InvalidAccessError, "The code must
be either 1000, or between 3000 and 4999. " + String::number(code) + " is neithe
r."); |
| 466 return; | 465 return; |
| 467 } | 466 } |
| 468 CString utf8 = reason.utf8(String::StrictConversionReplacingUnpairedSurr
ogatesWithFFFD); | 467 CString utf8 = reason.utf8(String::StrictConversionReplacingUnpairedSurr
ogatesWithFFFD); |
| 469 if (utf8.length() > maxReasonSizeInBytes) { | 468 if (utf8.length() > maxReasonSizeInBytes) { |
| 470 exceptionState.throwDOMException(SyntaxError, ExceptionMessages::fai
ledToExecute("close", "WebSocket", "The message must not be greater than " + Str
ing::number(maxReasonSizeInBytes) + " bytes.")); | 469 exceptionState.throwDOMException(SyntaxError, "The message must not
be greater than " + String::number(maxReasonSizeInBytes) + " bytes."); |
| 471 return; | 470 return; |
| 472 } | 471 } |
| 473 } | 472 } |
| 474 | 473 |
| 475 if (m_state == CLOSING || m_state == CLOSED) | 474 if (m_state == CLOSING || m_state == CLOSED) |
| 476 return; | 475 return; |
| 477 if (m_state == CONNECTING) { | 476 if (m_state == CONNECTING) { |
| 478 m_state = CLOSING; | 477 m_state = CLOSING; |
| 479 m_channel->fail("WebSocket is closed before the connection is establishe
d.", WarningMessageLevel); | 478 m_channel->fail("WebSocket is closed before the connection is establishe
d.", WarningMessageLevel); |
| 480 return; | 479 return; |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 671 static const size_t minimumPayloadSizeWithEightByteExtendedPayloadLength = 0
x10000; | 670 static const size_t minimumPayloadSizeWithEightByteExtendedPayloadLength = 0
x10000; |
| 672 size_t overhead = hybiBaseFramingOverhead + hybiMaskingKeyLength; | 671 size_t overhead = hybiBaseFramingOverhead + hybiMaskingKeyLength; |
| 673 if (payloadSize >= minimumPayloadSizeWithEightByteExtendedPayloadLength) | 672 if (payloadSize >= minimumPayloadSizeWithEightByteExtendedPayloadLength) |
| 674 overhead += 8; | 673 overhead += 8; |
| 675 else if (payloadSize >= minimumPayloadSizeWithTwoByteExtendedPayloadLength) | 674 else if (payloadSize >= minimumPayloadSizeWithTwoByteExtendedPayloadLength) |
| 676 overhead += 2; | 675 overhead += 2; |
| 677 return overhead; | 676 return overhead; |
| 678 } | 677 } |
| 679 | 678 |
| 680 } // namespace WebCore | 679 } // namespace WebCore |
| OLD | NEW |