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 |