Changeset 91243 in webkit
- Timestamp:
- Jul 19, 2011 1:57:29 AM (13 years ago)
- Location:
- trunk
- Files:
-
- 23 added
- 11 edited
- 7 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r91242 r91243 1 2011-07-19 Yuta Kitamura <yutak@chromium.org> 2 3 WebSocket: Implement hybi framing 4 https://bugs.webkit.org/show_bug.cgi?id=64522 5 6 Reviewed by Kent Tamura. 7 8 Fix existing tests so they match the new frame format, and add tests for the new frame types 9 and error conditions related to the new frame format. 10 11 Unskip hybi tests on mac, win and chromium. Other ports (wk2, qt and gtk) still skip these tests 12 because they do not support changing the value of "WebKitHixie76WebSocketProtocolEnabled" 13 preference key via layoutTestController.overridePreferences() yet. 14 15 * http/tests/websocket/tests/hybi/broken-utf8-expected.txt: Added. 16 * http/tests/websocket/tests/hybi/broken-utf8.html: Added. 17 * http/tests/websocket/tests/hybi/broken-utf8_wsh.py: Added. 18 * http/tests/websocket/tests/hybi/client-close-expected.txt: 19 * http/tests/websocket/tests/hybi/client-close.html: 20 The format of a close frame has been changed. Currently, we do not include any payload 21 in a close frame, thus it must start with "\x88\x80" (see section 4.1 of hybi-10 22 specification for more details). 23 * http/tests/websocket/tests/hybi/client-close_wsh.py: 24 * http/tests/websocket/tests/hybi/fragmented-control-frame-expected.txt: Added. 25 * http/tests/websocket/tests/hybi/fragmented-control-frame.html: Added. 26 * http/tests/websocket/tests/hybi/fragmented-control-frame_wsh.py: Added. 27 * http/tests/websocket/tests/hybi/fragmented-frames-expected.txt: Added. 28 * http/tests/websocket/tests/hybi/fragmented-frames.html: Added. 29 * http/tests/websocket/tests/hybi/fragmented-frames_wsh.py: Added. 30 * http/tests/websocket/tests/hybi/interleaved-fragments-expected.txt: Added. 31 * http/tests/websocket/tests/hybi/interleaved-fragments.html: Added. 32 * http/tests/websocket/tests/hybi/interleaved-fragments_wsh.py: Added. 33 * http/tests/websocket/tests/hybi/long-control-frame-expected.txt: Added. 34 * http/tests/websocket/tests/hybi/long-control-frame.html: Added. 35 * http/tests/websocket/tests/hybi/long-control-frame_wsh.py: Added. 36 * http/tests/websocket/tests/hybi/masked-frames-expected.txt: Added. 37 * http/tests/websocket/tests/hybi/masked-frames.html: Added. 38 * http/tests/websocket/tests/hybi/masked-frames_wsh.py: Added. 39 * http/tests/websocket/tests/hybi/pong-expected.txt: Added. 40 * http/tests/websocket/tests/hybi/pong.html: Added. 41 * http/tests/websocket/tests/hybi/pong_wsh.py: Added. 42 * http/tests/websocket/tests/hybi/reserved-bits-expected.txt: Added. 43 * http/tests/websocket/tests/hybi/reserved-bits.html: Added. 44 * http/tests/websocket/tests/hybi/reserved-bits_wsh.py: Added. 45 * http/tests/websocket/tests/hybi/reserved-opcodes-expected.txt: Added. 46 * http/tests/websocket/tests/hybi/reserved-opcodes.html: Added. 47 * http/tests/websocket/tests/hybi/reserved-opcodes_wsh.py: Added. 48 * http/tests/websocket/tests/hybi/send2_wsh.py: 49 Send two text frames at once. 50 * http/tests/websocket/tests/hybi/too-long-payload-expected.txt: Added. 51 * http/tests/websocket/tests/hybi/too-long-payload.html: Added. 52 * http/tests/websocket/tests/hybi/too-long-payload_wsh.py: Added. 53 * platform/chromium/test_expectations.txt: 54 Derive test expectations of hixie76 tests, because these tests are likely to behave 55 the same way as hixie76 tests. Will be checked later whether they really do. 56 * platform/mac/Skipped: 57 * platform/win/Skipped: 58 1 59 2011-07-19 Nikolas Zimmermann <nzimmermann@rim.com> 2 60 -
trunk/LayoutTests/http/tests/websocket/tests/hybi/broken-utf8.html
r91242 r91243 1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">1 <!DOCTYPE HTML> 2 2 <html> 3 3 <head> … … 8 8 <div id="description"></div> 9 9 <div id="console"></div> 10 <script type="text/javascript">11 description(" WebSocket: Test client-initiated close.");10 <script> 11 description("Test whether WebSocket aborts the connection when it receives a text frame containing broken UTF-8 data."); 12 12 13 13 window.jsTestIsAsync = true; … … 15 15 layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0); 16 16 17 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/ client-close");17 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/broken-utf8"); 18 18 var closeEvent; 19 var receivedMessage;20 19 21 20 ws.onopen = function() 22 21 { 23 debug("Connected"); 24 ws.close(); 22 debug("onopen() was called."); 25 23 }; 26 24 27 ws.onmessage = function( messageEvent)25 ws.onmessage = function(event) 28 26 { 29 debug("Received: " + messageEvent.data);30 receivedMessage = messageEvent.data;27 var message = event.data; 28 testFailed("onmessage() was called. (message = \"" + message + "\")"); 31 29 }; 32 30 33 31 ws.onclose = function(event) 34 32 { 35 debug(" Closed");33 debug("onclose() was called."); 36 34 closeEvent = event; 37 shouldBeEqualToString("receivedMessage", "close_frame='\\xff\\x00'"); 38 shouldBeTrue("closeEvent.wasClean"); 35 shouldBeFalse("closeEvent.wasClean"); 39 36 finishJSTest(); 40 37 }; -
trunk/LayoutTests/http/tests/websocket/tests/hybi/client-close-expected.txt
r90726 r91243 4 4 5 5 Connected 6 Received: close_frame ='\xff\x00'6 Received: close_frame[:2]='\x88\x80' 7 7 Closed 8 PASS receivedMessage is "close_frame ='\\xff\\x00'"8 PASS receivedMessage is "close_frame[:2]='\\x88\\x80'" 9 9 PASS closeEvent.wasClean is true 10 10 PASS successfullyParsed is true -
trunk/LayoutTests/http/tests/websocket/tests/hybi/client-close.html
r90726 r91243 35 35 debug("Closed"); 36 36 closeEvent = event; 37 shouldBeEqualToString("receivedMessage", "close_frame ='\\xff\\x00'");37 shouldBeEqualToString("receivedMessage", "close_frame[:2]='\\x88\\x80'"); 38 38 shouldBeTrue("closeEvent.wasClean"); 39 39 finishJSTest(); -
trunk/LayoutTests/http/tests/websocket/tests/hybi/client-close_wsh.py
r90726 r91243 8 8 def web_socket_transfer_data(request): 9 9 # Wait for a close frame sent from the client. 10 close_frame = request.ws_stream.receive_bytes( 2)10 close_frame = request.ws_stream.receive_bytes(6) 11 11 12 # Tell the client what we have received. 13 msgutil.send_message(request, 'close_frame=%r' % close_frame) 12 # Send only first two bytes of the received frame. The remaining four bytes are 13 # "masking key", which changes every time the test runs. 14 msgutil.send_message(request, 'close_frame[:2]=%r' % close_frame[:2]) 14 15 15 16 # If the following assertion fails, AssertionError will be raised, … … 17 18 # In this case, the client will fail to finish closing handshake, thus 18 19 # closeEvent.wasClean will become false. 19 assert close_frame == '\xff\x00'20 assert close_frame[:2] == '\x88\x80' 20 21 21 22 # Pretend we have received a close frame from the client. -
trunk/LayoutTests/http/tests/websocket/tests/hybi/fragmented-control-frame.html
r91242 r91243 1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">1 <!DOCTYPE HTML> 2 2 <html> 3 3 <head> … … 8 8 <div id="description"></div> 9 9 <div id="console"></div> 10 <script type="text/javascript">11 description(" WebSocket: Test client-initiated close.");10 <script> 11 description("Test whether WebSocket rejects a fragmented control frame and aborts the connection."); 12 12 13 13 window.jsTestIsAsync = true; … … 15 15 layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0); 16 16 17 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/client-close"); 17 var url = "ws://localhost:8880/websocket/tests/hybi/fragmented-control-frame"; 18 var ws = new WebSocket(url); 18 19 var closeEvent; 19 var receivedMessage;20 20 21 21 ws.onopen = function() 22 22 { 23 debug("Connected"); 24 ws.close(); 23 debug("onopen() was called."); 25 24 }; 26 25 27 ws.onmessage = function( messageEvent)26 ws.onmessage = function(event) 28 27 { 29 debug("Received: " + messageEvent.data);30 receivedMessage = messageEvent.data;28 var message = event.data; 29 testFailed("onmessage() was called. (message = \"" + message + "\")"); 31 30 }; 32 31 33 32 ws.onclose = function(event) 34 33 { 35 debug("Closed");36 34 closeEvent = event; 37 shouldBeEqualToString("receivedMessage", "close_frame='\\xff\\x00'"); 38 shouldBeTrue("closeEvent.wasClean"); 35 shouldBeFalse("closeEvent.wasClean"); 39 36 finishJSTest(); 40 37 }; -
trunk/LayoutTests/http/tests/websocket/tests/hybi/fragmented-frames.html
r91242 r91243 1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">1 <!DOCTYPE HTML> 2 2 <html> 3 3 <head> … … 8 8 <div id="description"></div> 9 9 <div id="console"></div> 10 <script type="text/javascript">11 description(" WebSocket: Test client-initiated close.");10 <script> 11 description("Receive fragmented WebSocket frames."); 12 12 13 13 window.jsTestIsAsync = true; … … 15 15 layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0); 16 16 17 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/client-close"); 17 var url = "ws://localhost:8880/websocket/tests/hybi/fragmented-frames"; 18 var ws = new WebSocket(url); 18 19 var closeEvent; 19 var receivedMessage; 20 var expectedMessages = ["First message", "Second message", "Third message"]; 21 var actualMessages = []; 20 22 21 23 ws.onopen = function() 22 24 { 23 debug(" Connected");25 debug("onopen() was called."); 24 26 ws.close(); 25 27 }; 26 28 27 ws.onmessage = function( messageEvent)29 ws.onmessage = function(event) 28 30 { 29 debug("Received: " + messageEvent.data); 30 receivedMessage = messageEvent.data; 31 var message = event.data; 32 debug("onmessage() was called. (message = \"" + message + "\")"); 33 actualMessages.push(message); 31 34 }; 32 35 33 36 ws.onclose = function(event) 34 37 { 35 debug("Closed");36 38 closeEvent = event; 37 shouldBeEqualToString("receivedMessage", "close_frame='\\xff\\x00'");38 39 shouldBeTrue("closeEvent.wasClean"); 40 shouldBeTrue("actualMessages.length === expectedMessages.length"); 41 for (var i = 0; i < expectedMessages.length; ++i) 42 shouldBeEqualToString("actualMessages[" + i + "]", expectedMessages[i]); 39 43 finishJSTest(); 40 44 }; -
trunk/LayoutTests/http/tests/websocket/tests/hybi/interleaved-fragments.html
r91242 r91243 1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">1 <!DOCTYPE HTML> 2 2 <html> 3 3 <head> … … 8 8 <div id="description"></div> 9 9 <div id="console"></div> 10 <script type="text/javascript">11 description(" WebSocket: Test client-initiated close.");10 <script> 11 description("Test whether WebSocket rejects interleaved fragmented frames."); 12 12 13 13 window.jsTestIsAsync = true; … … 15 15 layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0); 16 16 17 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/client-close"); 17 var url = "ws://localhost:8880/websocket/tests/hybi/interleaved-fragments"; 18 var ws = new WebSocket(url); 18 19 var closeEvent; 19 var receivedMessage;20 20 21 21 ws.onopen = function() 22 22 { 23 debug("Connected"); 24 ws.close(); 23 debug("onopen() was called."); 25 24 }; 26 25 27 ws.onmessage = function( messageEvent)26 ws.onmessage = function(event) 28 27 { 29 debug("Received: " + messageEvent.data);30 receivedMessage = messageEvent.data;28 var message = event.data; 29 testFailed("onmessage() was called. (message = \"" + message + "\")"); 31 30 }; 32 31 33 32 ws.onclose = function(event) 34 33 { 35 debug("Closed");36 34 closeEvent = event; 37 shouldBeEqualToString("receivedMessage", "close_frame='\\xff\\x00'"); 38 shouldBeTrue("closeEvent.wasClean"); 35 shouldBeFalse("closeEvent.wasClean"); 39 36 finishJSTest(); 40 37 }; -
trunk/LayoutTests/http/tests/websocket/tests/hybi/long-control-frame.html
r91242 r91243 1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">1 <!DOCTYPE HTML> 2 2 <html> 3 3 <head> … … 8 8 <div id="description"></div> 9 9 <div id="console"></div> 10 <script type="text/javascript">11 description(" WebSocket: Test client-initiated close.");10 <script> 11 description("Test whether WebSocket rejects control frames longer than 125 bytes."); 12 12 13 13 window.jsTestIsAsync = true; … … 15 15 layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0); 16 16 17 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/client-close"); 17 var url = "ws://localhost:8880/websocket/tests/hybi/long-control-frame"; 18 var ws = new WebSocket(url); 18 19 var closeEvent; 19 var receivedMessage;20 var message; 20 21 21 22 ws.onopen = function() 22 23 { 23 debug("Connected"); 24 ws.close(); 25 }; 26 27 ws.onmessage = function(messageEvent) 28 { 29 debug("Received: " + messageEvent.data); 30 receivedMessage = messageEvent.data; 24 debug("onopen() was called."); 31 25 }; 32 26 33 27 ws.onclose = function(event) 34 28 { 35 debug("Closed");36 29 closeEvent = event; 37 shouldBeEqualToString("receivedMessage", "close_frame='\\xff\\x00'"); 38 shouldBeTrue("closeEvent.wasClean"); 30 shouldBeFalse("closeEvent.wasClean"); 39 31 finishJSTest(); 40 32 }; -
trunk/LayoutTests/http/tests/websocket/tests/hybi/pong.html
r91242 r91243 1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">1 <!DOCTYPE HTML> 2 2 <html> 3 3 <head> … … 8 8 <div id="description"></div> 9 9 <div id="console"></div> 10 <script type="text/javascript">11 description(" WebSocket: Test client-initiated close.");10 <script> 11 description("Test whether WebSocket correctly responds to a ping message sent from the server."); 12 12 13 13 window.jsTestIsAsync = true; … … 15 15 layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0); 16 16 17 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/client-close"); 17 var url = "ws://localhost:8880/websocket/tests/hybi/pong"; 18 var ws = new WebSocket(url); 18 19 var closeEvent; 19 var receivedMessage;20 var message; 20 21 21 22 ws.onopen = function() 22 23 { 23 debug("Connected"); 24 ws.close(); 24 debug("onopen() was called."); 25 25 }; 26 26 27 ws.onmessage = function( messageEvent)27 ws.onmessage = function(event) 28 28 { 29 debug("Received: " + messageEvent.data); 30 receivedMessage = messageEvent.data; 29 message = event.data; 30 debug("onmessage() was called. (message = \"" + message + "\")"); 31 shouldBeEqualToString("message", "PASS"); 31 32 }; 32 33 33 34 ws.onclose = function(event) 34 35 { 35 debug("Closed");36 36 closeEvent = event; 37 shouldBeEqualToString("receivedMessage", "close_frame='\\xff\\x00'");38 37 shouldBeTrue("closeEvent.wasClean"); 39 38 finishJSTest(); -
trunk/LayoutTests/http/tests/websocket/tests/hybi/send2_wsh.py
r90726 r91243 1 from mod_pywebsocket import stream 2 3 1 4 def web_socket_do_extra_handshake(request): 2 5 pass # Always accept. … … 5 8 def web_socket_transfer_data(request): 6 9 # send 2 messages in one packet. 7 request.connection.write("\x00" + "first message" + "\xff" + 8 "\x00" + "second message" + "\xff") 10 request.connection.write(stream.create_text_frame("first message") + stream.create_text_frame("second message")) -
trunk/LayoutTests/http/tests/websocket/tests/hybi/too-long-payload.html
r91242 r91243 1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">1 <!DOCTYPE HTML> 2 2 <html> 3 3 <head> … … 9 9 <div id="console"></div> 10 10 <script type="text/javascript"> 11 description(" WebSocket: Test client-initiated close.");11 description("Tests whether WebSocket correctly aborts the connection when it receives a frame with too long payload."); 12 12 13 13 window.jsTestIsAsync = true; … … 15 15 layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0); 16 16 17 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/ client-close");17 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/too-long-payload"); 18 18 var closeEvent; 19 var receivedMessage;20 19 21 20 ws.onopen = function() 22 21 { 23 debug("Connected"); 24 ws.close(); 22 debug("onopen() was called."); 25 23 }; 26 24 27 ws.onmessage = function( messageEvent)25 ws.onmessage = function(event) 28 26 { 29 debug("Received: " + messageEvent.data);30 receivedMessage = messageEvent.data;27 var message = event.data; 28 testFailed("onmessage() was called. (message = \"" + message + "\")"); 31 29 }; 32 30 33 31 ws.onclose = function(event) 34 32 { 35 debug(" Closed");33 debug("onclose() was called."); 36 34 closeEvent = event; 37 shouldBeEqualToString("receivedMessage", "close_frame='\\xff\\x00'"); 38 shouldBeTrue("closeEvent.wasClean"); 35 shouldBeFalse("closeEvent.wasClean"); 39 36 finishJSTest(); 40 37 }; -
trunk/LayoutTests/platform/chromium/test_expectations.txt
r91238 r91243 47 47 48 48 BUGCR32018 SLOW : http/tests/websocket/tests/hixie76/frame-lengths.html = PASS 49 BUGCR32018 SLOW : http/tests/websocket/tests/hybi/frame-lengths.html = PASS 49 50 BUGCR69513 DEBUG SLOW : html5lib/webkit-resumer.html = PASS 50 51 … … 106 107 WONTFIX SKIP : http/tests/eventsource/workers = PASS TIMEOUT FAIL 107 108 WONTFIX SKIP : http/tests/websocket/tests/hixie76/workers/ = PASS TIMEOUT FAIL 109 WONTFIX SKIP : http/tests/websocket/tests/hybi/workers/ = PASS TIMEOUT FAIL 108 110 WONTFIX SKIP : inspector/debugger/script-formatter.html = PASS TIMEOUT FAIL 109 111 … … 156 158 BUGWK60877 SKIP : loader/navigation-while-deferring-loads.html = FAIL 157 159 BUGWK60877 SKIP : loader/load-defer-resume-crash.html = FAIL 158 159 // Skipped until new WebSocket protocol is implemented.160 BUGWK50099 SKIP : http/tests/websocket/tests/hybi/ = PASS FAIL TIMEOUT161 160 162 161 // CSS3 Selectors3 test suite … … 1722 1721 // WebKit roll 52852 -> 52867 1723 1722 BUGCR32018 DEBUG : http/tests/websocket/tests/hixie76/simple-stress.html = PASS TEXT TIMEOUT 1723 BUGCR32018 DEBUG : http/tests/websocket/tests/hybi/simple-stress.html = PASS TEXT TIMEOUT 1724 1724 1725 1725 // V8's implementation of getOwnPropertyNames has different results for built-in … … 2801 2801 BUGCR73092 WIN : fast/dom/gc-11.html = PASS TEXT 2802 2802 BUGCR73094 LINUX : http/tests/websocket/tests/hixie76/send-after-close-on-unload.html = PASS TIMEOUT 2803 BUGCR73094 LINUX : http/tests/websocket/tests/hybi/send-after-close-on-unload.html = PASS TIMEOUT 2803 2804 2804 2805 BUGV8_1168 : fast/js/mozilla/eval/exhaustive-fun-normalcaller-indirect-strictcode.html = TEXT … … 3646 3647 3647 3648 BUGWK61767 WIN DEBUG : http/tests/websocket/tests/hixie76/client-close.html = PASS TEXT 3649 BUGWK61767 WIN DEBUG : http/tests/websocket/tests/hybi/client-close.html = PASS TEXT 3648 3650 3649 3651 BUGWK59782 WIN LINUX DEBUG : svg/dynamic-updates/SVGFEDropShadowElement-dom-dx-attr.html = TIMEOUT -
trunk/LayoutTests/platform/mac/Skipped
r90946 r91243 387 387 compositing/rtl/rtl-iframe-fixed.html 388 388 compositing/rtl/rtl-iframe-relative.html 389 390 # Skipped until new WebSocket protocol is implemented. http://webkit.org/b/50099391 http/tests/websocket/tests/hybi/ -
trunk/LayoutTests/platform/win/Skipped
r91104 r91243 1347 1347 plugins/form-value.html 1348 1348 1349 # Skipped until new WebSocket protocol is implemented. http://webkit.org/b/500991350 http/tests/websocket/tests/hybi/1351 1352 1349 # Needs generated results 1353 1350 css3/selectors3 -
trunk/Source/WebCore/ChangeLog
r91242 r91243 1 2011-07-19 Yuta Kitamura <yutak@chromium.org> 2 3 WebSocket: Implement hybi framing 4 https://bugs.webkit.org/show_bug.cgi?id=64522 5 6 Reviewed by Kent Tamura. 7 8 Implement WebSocket framing protocol which is mainly described in 9 <http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10#section-4> and 10 <http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10#section-6>. 11 12 Hybi protocol introduces a new frame format which is drastically different from 13 the old one. Notable differences are: 14 - Binary data support. 15 - Fragmentation support: a single message can be fragmented to multiple frames. 16 - Ping-pong support. 17 - Masking: frame content of a client must be masked to prevent cross-protocol attacks. 18 19 This patch covers the following features: 20 - Send a pong frame when a ping frame is received. 21 - Receive fragmented frames. 22 - Receive masked frames. (Servers do not have to mask frames, but they may if they wish.) 23 24 The following features are NOT implemented yet: 25 - Send or receive binary messages. 26 - Send a ping message. 27 - Send fragmented frames. (It is unclear whether this is necessary.) 28 - Rewrite the frame content by WebSocket protocol extensions (like frame compression). 29 30 New tests: http/tests/websocket/tests/hybi/broken-utf8.html 31 http/tests/websocket/tests/hybi/fragmented-control-frame.html 32 http/tests/websocket/tests/hybi/fragmented-frames.html 33 http/tests/websocket/tests/hybi/interleaved-fragments.html 34 http/tests/websocket/tests/hybi/long-control-frame.html 35 http/tests/websocket/tests/hybi/masked-frames.html 36 http/tests/websocket/tests/hybi/pong.html 37 http/tests/websocket/tests/hybi/reserved-bits.html 38 http/tests/websocket/tests/hybi/reserved-opcodes.html 39 http/tests/websocket/tests/hybi/too-long-payload.html 40 41 * websockets/WebSocketChannel.cpp: 42 (WebCore::WebSocketChannel::WebSocketChannel): 43 (WebCore::WebSocketChannel::send): 44 The original content of send() was moved to a private method sendFrameHixie76(). 45 (WebCore::WebSocketChannel::fail): 46 Stop handling incoming data after the WebSocket connection is failed. 47 It was unclear to me whether we should do the same thing for hixie-76 connection; 48 for now, I kept the original behavior. 49 (WebCore::WebSocketChannel::processBuffer): 50 (WebCore::WebSocketChannel::resumeTimerFired): 51 (WebCore::WebSocketChannel::startClosingHandshake): 52 (WebCore::WebSocketChannel::closingTimerFired): 53 (WebCore::WebSocketChannel::parseFrame): 54 (WebCore::WebSocketChannel::processFrame): 55 (WebCore::WebSocketChannel::processFrameHixie76): 56 (WebCore::WebSocketChannel::sendFrame): 57 (WebCore::WebSocketChannel::sendFrameHixie76): 58 * websockets/WebSocketChannel.h: 59 (WebCore::WebSocketChannel::isNonControlOpCode): 60 (WebCore::WebSocketChannel::isControlOpCode): 61 (WebCore::WebSocketChannel::isReservedOpCode): 62 1 63 2011-07-19 Nikolas Zimmermann <nzimmermann@rim.com> 2 64 -
trunk/Source/WebCore/websockets/WebSocketChannel.cpp
r90704 r91243 49 49 #include "WebSocketHandshake.h" 50 50 51 #include <wtf/text/CString.h> 52 #include <wtf/text/WTFString.h> 53 #include <wtf/text/StringHash.h> 51 #include <wtf/CryptographicallyRandomNumber.h> 54 52 #include <wtf/Deque.h> 55 53 #include <wtf/FastMalloc.h> 56 54 #include <wtf/HashMap.h> 55 #include <wtf/text/CString.h> 56 #include <wtf/text/StringHash.h> 57 #include <wtf/text/WTFString.h> 58 59 using namespace std; 57 60 58 61 namespace WebCore { 59 62 60 63 const double TCPMaximumSegmentLifetime = 2 * 60.0; 64 65 // Constants for hybi-10 frame format. 66 const unsigned char finalBit = 0x80; 67 const unsigned char reserved1Bit = 0x40; 68 const unsigned char reserved2Bit = 0x20; 69 const unsigned char reserved3Bit = 0x10; 70 const unsigned char opCodeMask = 0xF; 71 const unsigned char maskBit = 0x80; 72 const unsigned char payloadLengthMask = 0x7F; 73 const size_t maxPayloadLengthWithoutExtendedLengthField = 125; 74 const size_t payloadLengthWithTwoByteExtendedLengthField = 126; 75 const size_t payloadLengthWithEightByteExtendedLengthField = 127; 76 const size_t maskingKeyWidthInBytes = 4; 77 78 const WebSocketChannel::OpCode WebSocketChannel::OpCodeContinuation = 0x0; 79 const WebSocketChannel::OpCode WebSocketChannel::OpCodeText = 0x1; 80 const WebSocketChannel::OpCode WebSocketChannel::OpCodeBinary = 0x2; 81 const WebSocketChannel::OpCode WebSocketChannel::OpCodeClose = 0x8; 82 const WebSocketChannel::OpCode WebSocketChannel::OpCodePing = 0x9; 83 const WebSocketChannel::OpCode WebSocketChannel::OpCodePong = 0xA; 61 84 62 85 WebSocketChannel::WebSocketChannel(ScriptExecutionContext* context, WebSocketChannelClient* client, const KURL& url, const String& protocol) … … 75 98 , m_identifier(0) 76 99 , m_useHixie76Protocol(true) 100 , m_hasContinuousFrame(false) 77 101 { 78 102 ASSERT(m_context->isDocument()); … … 103 127 } 104 128 105 bool WebSocketChannel::send(const String& msg) 106 { 107 LOG(Network, "WebSocketChannel %p send %s", this, msg.utf8().data()); 108 ASSERT(m_handle); 109 ASSERT(!m_suspended); 110 Vector<char> buf; 111 buf.append('\0'); // frame type 112 CString utf8 = msg.utf8(); 113 buf.append(utf8.data(), utf8.length()); 114 buf.append('\xff'); // frame end 115 return m_handle->send(buf.data(), buf.size()); 129 bool WebSocketChannel::send(const String& message) 130 { 131 LOG(Network, "WebSocketChannel %p send %s", this, message.utf8().data()); 132 CString utf8 = message.utf8(); 133 if (m_useHixie76Protocol) 134 return sendFrameHixie76(utf8.data(), utf8.length()); 135 return sendFrame(OpCodeText, utf8.data(), utf8.length()); 116 136 } 117 137 … … 141 161 if (m_context) 142 162 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, reason, 0, m_handshake->clientOrigin(), 0); 163 if (!m_useHixie76Protocol) { 164 // Hybi-10 specification explicitly states we must not continue to handle incoming data 165 // once the WebSocket connection is failed (section 7.1.7). 166 // FIXME: Should we do this in hixie-76 too? 167 m_shouldDiscardReceivedData = true; 168 if (m_buffer) 169 skipBuffer(m_bufferSize); // Save memory. 170 m_hasContinuousFrame = false; 171 m_continuousFrameData.clear(); 172 } 143 173 if (m_handle && !m_closed) 144 174 m_handle->disconnect(); // Will call didClose(). … … 346 376 return false; 347 377 378 if (m_useHixie76Protocol) 379 return processFrameHixie76(); 380 381 return processFrame(); 382 } 383 384 void WebSocketChannel::resumeTimerFired(Timer<WebSocketChannel>* timer) 385 { 386 ASSERT_UNUSED(timer, timer == &m_resumeTimer); 387 388 RefPtr<WebSocketChannel> protect(this); // The client can close the channel, potentially removing the last reference. 389 while (!m_suspended && m_client && m_buffer) 390 if (!processBuffer()) 391 break; 392 if (!m_suspended && m_client && m_closed && m_handle) 393 didClose(m_handle.get()); 394 } 395 396 void WebSocketChannel::startClosingHandshake() 397 { 398 LOG(Network, "WebSocketChannel %p closing %d %d", this, m_closing, m_receivedClosingHandshake); 399 if (m_closing) 400 return; 401 ASSERT(m_handle); 402 bool sentSuccessfully; 403 if (m_useHixie76Protocol) { 404 Vector<char> buf; 405 buf.append('\xff'); 406 buf.append('\0'); 407 sentSuccessfully = m_handle->send(buf.data(), buf.size()); 408 } else 409 sentSuccessfully = sendFrame(OpCodeClose, "", 0); // FIXME: Send status code and reason message. 410 411 if (!sentSuccessfully) { 412 m_handle->disconnect(); 413 return; 414 } 415 m_closing = true; 416 if (m_client) 417 m_client->didStartClosingHandshake(); 418 } 419 420 void WebSocketChannel::closingTimerFired(Timer<WebSocketChannel>* timer) 421 { 422 LOG(Network, "WebSocketChannel %p closing timer", this); 423 ASSERT_UNUSED(timer, &m_closingTimer == timer); 424 if (m_handle) 425 m_handle->disconnect(); 426 } 427 428 WebSocketChannel::ParseFrameResult WebSocketChannel::parseFrame(FrameData& frame) 429 { 430 const char* p = m_buffer; 431 const char* bufferEnd = m_buffer + m_bufferSize; 432 433 if (m_bufferSize < 2) 434 return FrameIncomplete; 435 436 unsigned char firstByte = *p++; 437 unsigned char secondByte = *p++; 438 439 bool final = firstByte & finalBit; 440 bool reserved1 = firstByte & reserved1Bit; 441 bool reserved2 = firstByte & reserved2Bit; 442 bool reserved3 = firstByte & reserved3Bit; 443 OpCode opCode = firstByte & opCodeMask; 444 445 bool masked = secondByte & maskBit; 446 uint64_t payloadLength64 = secondByte & payloadLengthMask; 447 if (payloadLength64 > maxPayloadLengthWithoutExtendedLengthField) { 448 int extendedPayloadLengthSize; 449 if (payloadLength64 == payloadLengthWithTwoByteExtendedLengthField) 450 extendedPayloadLengthSize = 2; 451 else { 452 ASSERT(payloadLength64 == payloadLengthWithEightByteExtendedLengthField); 453 extendedPayloadLengthSize = 8; 454 } 455 if (bufferEnd - p < extendedPayloadLengthSize) 456 return FrameIncomplete; 457 payloadLength64 = 0; 458 for (int i = 0; i < extendedPayloadLengthSize; ++i) { 459 payloadLength64 <<= 8; 460 payloadLength64 |= static_cast<unsigned char>(*p++); 461 } 462 } 463 464 // FIXME: UINT64_C(0x7FFFFFFFFFFFFFFF) should be used but it did not compile on Qt bots. 465 #if COMPILER(MSVC) 466 static const uint64_t maxPayloadLength = 0x7FFFFFFFFFFFFFFFui64; 467 #else 468 static const uint64_t maxPayloadLength = 0x7FFFFFFFFFFFFFFFull; 469 #endif 470 size_t maskingKeyLength = masked ? maskingKeyWidthInBytes : 0; 471 if (payloadLength64 > maxPayloadLength || payloadLength64 + maskingKeyLength > numeric_limits<size_t>::max()) { 472 fail("WebSocket frame length too large: " + String::number(payloadLength64) + " bytes"); 473 return FrameError; 474 } 475 size_t payloadLength = static_cast<size_t>(payloadLength64); 476 477 if (static_cast<size_t>(bufferEnd - p) < maskingKeyLength + payloadLength) 478 return FrameIncomplete; 479 480 if (masked) { 481 const char* maskingKey = p; 482 char* payload = const_cast<char*>(p + maskingKeyWidthInBytes); 483 for (size_t i = 0; i < payloadLength; ++i) 484 payload[i] ^= maskingKey[i % maskingKeyWidthInBytes]; // Unmask the payload. 485 } 486 487 frame.opCode = opCode; 488 frame.final = final; 489 frame.reserved1 = reserved1; 490 frame.reserved2 = reserved2; 491 frame.reserved3 = reserved3; 492 frame.masked = masked; 493 frame.payload = p + maskingKeyLength; 494 frame.payloadLength = payloadLength; 495 frame.frameEnd = p + maskingKeyLength + payloadLength; 496 return FrameOK; 497 } 498 499 bool WebSocketChannel::processFrame() 500 { 501 ASSERT(m_buffer); 502 503 FrameData frame; 504 if (parseFrame(frame) != FrameOK) 505 return false; 506 507 // Validate the frame data. 508 if (isReservedOpCode(frame.opCode)) { 509 fail("Unrecognized frame opcode: " + String::number(frame.opCode)); 510 return false; 511 } 512 513 if (frame.reserved1 || frame.reserved2 || frame.reserved3) { 514 fail("One or more reserved bits are on: reserved1 = " + String::number(frame.reserved1) + ", reserved2 = " + String::number(frame.reserved2) + ", reserved3 = " + String::number(frame.reserved3)); 515 return false; 516 } 517 518 // All control frames must not be fragmented. 519 if (isControlOpCode(frame.opCode) && !frame.final) { 520 fail("Received fragmented control frame: opcode = " + String::number(frame.opCode)); 521 return false; 522 } 523 524 // All control frames must have a payload of 125 bytes or less, which means the frame must not contain 525 // the "extended payload length" field. 526 if (isControlOpCode(frame.opCode) && frame.payloadLength > maxPayloadLengthWithoutExtendedLengthField) { 527 fail("Received control frame having too long payload: " + String::number(frame.payloadLength) + " bytes"); 528 return false; 529 } 530 531 // A new data frame is received before the previous continuous frame finishes. 532 // Note that control frames are allowed to come in the middle of continuous frames. 533 if (m_hasContinuousFrame && frame.opCode != OpCodeContinuation && !isControlOpCode(frame.opCode)) { 534 fail("Received new data frame but previous continuous frame is unfinished."); 535 return false; 536 } 537 538 switch (frame.opCode) { 539 case OpCodeContinuation: 540 // Throw away content of a binary message because binary messages are not supported yet. 541 if (m_continuousFrameOpCode == OpCodeText) 542 m_continuousFrameData.append(frame.payload, frame.payloadLength); 543 skipBuffer(frame.frameEnd - m_buffer); 544 if (frame.final) { 545 // onmessage handler may eventually call the other methods of this channel, 546 // so we should pretend that we have finished to read this frame and 547 // make sure that the member variables are in a consistent state before 548 // the handler is invoked. 549 // Vector<char>::swap() is used here to clear m_continuousFrameData. 550 Vector<char> continuousFrameData; 551 m_continuousFrameData.swap(continuousFrameData); 552 m_hasContinuousFrame = false; 553 if (m_continuousFrameOpCode == OpCodeText) { 554 String message = String::fromUTF8(continuousFrameData.data(), continuousFrameData.size()); 555 if (message.isNull()) 556 fail("Could not decode a text frame as UTF-8."); 557 else 558 m_client->didReceiveMessage(message); 559 } else if (m_continuousFrameOpCode == OpCodeBinary) { 560 ASSERT(m_continuousFrameData.isEmpty()); 561 fail("Received a binary frame which is not supported yet."); 562 } 563 } 564 break; 565 566 case OpCodeText: 567 if (frame.final) { 568 String message = String::fromUTF8(frame.payload, frame.payloadLength); 569 skipBuffer(frame.frameEnd - m_buffer); 570 if (message.isNull()) 571 fail("Could not decode a text frame as UTF-8."); 572 else 573 m_client->didReceiveMessage(message); 574 } else { 575 m_hasContinuousFrame = true; 576 m_continuousFrameOpCode = OpCodeText; 577 ASSERT(m_continuousFrameData.isEmpty()); 578 m_continuousFrameData.append(frame.payload, frame.payloadLength); 579 skipBuffer(frame.frameEnd - m_buffer); 580 } 581 break; 582 583 case OpCodeBinary: 584 if (frame.final) 585 fail("Received a binary frame which is not supported yet."); 586 else { 587 m_hasContinuousFrame = true; 588 m_continuousFrameOpCode = OpCodeBinary; 589 ASSERT(m_continuousFrameData.isEmpty()); 590 // Do not store data of a binary message to m_continuousFrameData to save memory. 591 skipBuffer(frame.frameEnd - m_buffer); 592 } 593 break; 594 595 case OpCodeClose: 596 // FIXME: Handle payload. 597 skipBuffer(frame.frameEnd - m_buffer); 598 m_receivedClosingHandshake = true; 599 startClosingHandshake(); 600 if (m_closing) 601 m_handle->close(); // Close after sending a close frame. 602 break; 603 604 case OpCodePing: { 605 bool result = sendFrame(OpCodePong, frame.payload, frame.payloadLength); 606 skipBuffer(frame.frameEnd - m_buffer); 607 if (!result) 608 fail("Failed to send a pong frame."); 609 break; 610 } 611 612 case OpCodePong: 613 // A server may send a pong in response to our ping, or an unsolicited pong which is not associated with 614 // any specific ping. Either way, there's nothing to do on receipt of pong. 615 skipBuffer(frame.frameEnd - m_buffer); 616 break; 617 618 default: 619 ASSERT_NOT_REACHED(); 620 skipBuffer(frame.frameEnd - m_buffer); 621 break; 622 } 623 624 return m_buffer; 625 } 626 627 bool WebSocketChannel::processFrameHixie76() 628 { 348 629 const char* nextFrame = m_buffer; 349 630 const char* p = m_buffer; … … 355 636 bool errorFrame = false; 356 637 while (p < end) { 357 if (length > std::numeric_limits<size_t>::max() / 128) {638 if (length > numeric_limits<size_t>::max() / 128) { 358 639 LOG(Network, "frame length overflow %lu", static_cast<unsigned long>(length)); 359 640 errorFrame = true; … … 363 644 unsigned char msgByte = static_cast<unsigned char>(*p); 364 645 unsigned int lengthMsgByte = msgByte & 0x7f; 365 if (newLength > std::numeric_limits<size_t>::max() - lengthMsgByte) {646 if (newLength > numeric_limits<size_t>::max() - lengthMsgByte) { 366 647 LOG(Network, "frame length overflow %lu+%u", static_cast<unsigned long>(newLength), lengthMsgByte); 367 648 errorFrame = true; … … 428 709 } 429 710 430 void WebSocketChannel::resumeTimerFired(Timer<WebSocketChannel>* timer) 431 { 432 ASSERT_UNUSED(timer, timer == &m_resumeTimer); 433 434 RefPtr<WebSocketChannel> protect(this); // The client can close the channel, potentially removing the last reference. 435 while (!m_suspended && m_client && m_buffer) 436 if (!processBuffer()) 437 break; 438 if (!m_suspended && m_client && m_closed && m_handle) 439 didClose(m_handle.get()); 440 } 441 442 void WebSocketChannel::startClosingHandshake() 443 { 444 LOG(Network, "WebSocketChannel %p closing %d %d", this, m_closing, m_receivedClosingHandshake); 445 if (m_closing) 446 return; 711 bool WebSocketChannel::sendFrame(OpCode opCode, const char* data, size_t dataLength) 712 { 447 713 ASSERT(m_handle); 448 Vector<char> buf; 449 buf.append('\xff'); 450 buf.append('\0'); 451 if (!m_handle->send(buf.data(), buf.size())) { 452 m_handle->disconnect(); 453 return; 454 } 455 m_closing = true; 456 if (m_client) 457 m_client->didStartClosingHandshake(); 458 } 459 460 void WebSocketChannel::closingTimerFired(Timer<WebSocketChannel>* timer) 461 { 462 LOG(Network, "WebSocketChannel %p closing timer", this); 463 ASSERT_UNUSED(timer, &m_closingTimer == timer); 464 if (m_handle) 465 m_handle->disconnect(); 714 ASSERT(!m_suspended); 715 716 Vector<char> frame; 717 ASSERT(!(opCode & ~opCodeMask)); // Checks whether "opCode" fits in the range of opCodes. 718 frame.append(finalBit | opCode); 719 if (dataLength <= maxPayloadLengthWithoutExtendedLengthField) 720 frame.append(maskBit | dataLength); 721 else if (dataLength <= 0xFFFF) { 722 frame.append(maskBit | payloadLengthWithTwoByteExtendedLengthField); 723 frame.append((dataLength & 0xFF00) >> 8); 724 frame.append(dataLength & 0xFF); 725 } else { 726 frame.append(maskBit | payloadLengthWithEightByteExtendedLengthField); 727 char extendedPayloadLength[8]; 728 size_t remaining = dataLength; 729 // Fill the length into extendedPayloadLength in the network byte order. 730 for (int i = 0; i < 8; ++i) { 731 extendedPayloadLength[7 - i] = remaining & 0xFF; 732 remaining >>= 8; 733 } 734 ASSERT(!remaining); 735 frame.append(extendedPayloadLength, 8); 736 } 737 738 // Mask the frame. 739 size_t maskingKeyStart = frame.size(); 740 frame.grow(frame.size() + maskingKeyWidthInBytes); // Add placeholder for masking key. Will be overwritten. 741 size_t payloadStart = frame.size(); 742 frame.append(data, dataLength); 743 744 cryptographicallyRandomValues(frame.data() + maskingKeyStart, maskingKeyWidthInBytes); 745 for (size_t i = 0; i < dataLength; ++i) 746 frame[payloadStart + i] ^= frame[maskingKeyStart + i % maskingKeyWidthInBytes]; 747 748 return m_handle->send(frame.data(), frame.size()); 749 } 750 751 bool WebSocketChannel::sendFrameHixie76(const char* data, size_t dataLength) 752 { 753 ASSERT(m_handle); 754 ASSERT(!m_suspended); 755 756 Vector<char> frame; 757 frame.append('\0'); // Frame type. 758 frame.append(data, dataLength); 759 frame.append('\xff'); // Frame end. 760 return m_handle->send(frame.data(), frame.size()); 466 761 } 467 762 -
trunk/Source/WebCore/websockets/WebSocketChannel.h
r90704 r91243 1 1 /* 2 * Copyright (C) 20 09Google Inc. All rights reserved.2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 89 89 void closingTimerFired(Timer<WebSocketChannel>*); 90 90 91 // Hybi-10 opcodes. 92 typedef unsigned int OpCode; 93 static const OpCode OpCodeContinuation; 94 static const OpCode OpCodeText; 95 static const OpCode OpCodeBinary; 96 static const OpCode OpCodeClose; 97 static const OpCode OpCodePing; 98 static const OpCode OpCodePong; 99 100 static bool isNonControlOpCode(OpCode opCode) { return opCode == OpCodeContinuation || opCode == OpCodeText || opCode == OpCodeBinary; } 101 static bool isControlOpCode(OpCode opCode) { return opCode == OpCodeClose || opCode == OpCodePing || opCode == OpCodePong; } 102 static bool isReservedOpCode(OpCode opCode) { return !isNonControlOpCode(opCode) && !isControlOpCode(opCode); } 103 104 enum ParseFrameResult { 105 FrameOK, 106 FrameIncomplete, 107 FrameError 108 }; 109 110 struct FrameData { 111 OpCode opCode; 112 bool final; 113 bool reserved1; 114 bool reserved2; 115 bool reserved3; 116 bool masked; 117 const char* payload; 118 size_t payloadLength; 119 const char* frameEnd; 120 }; 121 122 ParseFrameResult parseFrame(FrameData&); // May modify part of m_buffer to unmask the frame. 123 124 bool processFrame(); 125 bool processFrameHixie76(); 126 127 bool sendFrame(OpCode, const char* data, size_t dataLength); 128 bool sendFrameHixie76(const char* data, size_t dataLength); 129 91 130 ScriptExecutionContext* m_context; 92 131 WebSocketChannelClient* m_client; … … 108 147 109 148 bool m_useHixie76Protocol; 149 150 // Private members only for hybi-10 protocol. 151 bool m_hasContinuousFrame; 152 OpCode m_continuousFrameOpCode; 153 Vector<char> m_continuousFrameData; 110 154 }; 111 155
Note: See TracChangeset
for help on using the changeset viewer.