Title: [280385] trunk
Revision
280385
Author
[email protected]
Date
2021-07-28 08:57:49 -0700 (Wed, 28 Jul 2021)

Log Message

WebSocket: Safari on iOS 15 beta 3 is sending invalid close frame
https://bugs.webkit.org/show_bug.cgi?id=228329

Patch by Alex Christensen <[email protected]> on 2021-07-28
Reviewed by Youenn Fablet.

Source/WebKit:

Our NSURLSession WebSocket implementation had two bugs:
1. It was sending 1005 as the close code if none was specified.
   It now sends no close code, which matches Chrome and Firefox.
2. It was not sending a close code before the close reason when a WebSocket is closed due to navigation.
   It now sends the close code in 2 bytes before the reason, which matches Chrome and Firefox.
   Side note: our CFReadStream/CFWriteStream implementation sent neither the code nor the reason
   in this case, which matches no other browser.

Covered by an API test.

* NetworkProcess/cocoa/WebSocketTaskCocoa.mm:
(WebKit::WebSocketTask::close):
* WebProcess/Network/WebSocketChannel.cpp:
(WebKit::WebSocketChannel::fail):
(WebKit::WebSocketChannel::disconnect):

Tools:

* TestWebKitAPI/Tests/WebKitCocoa/WebSocket.mm:
(TestWebKitAPI::TEST):

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (280384 => 280385)


--- trunk/Source/WebKit/ChangeLog	2021-07-28 15:55:25 UTC (rev 280384)
+++ trunk/Source/WebKit/ChangeLog	2021-07-28 15:57:49 UTC (rev 280385)
@@ -1,3 +1,26 @@
+2021-07-28  Alex Christensen  <[email protected]>
+
+        WebSocket: Safari on iOS 15 beta 3 is sending invalid close frame
+        https://bugs.webkit.org/show_bug.cgi?id=228329
+
+        Reviewed by Youenn Fablet.
+
+        Our NSURLSession WebSocket implementation had two bugs:
+        1. It was sending 1005 as the close code if none was specified.
+           It now sends no close code, which matches Chrome and Firefox.
+        2. It was not sending a close code before the close reason when a WebSocket is closed due to navigation.
+           It now sends the close code in 2 bytes before the reason, which matches Chrome and Firefox.
+           Side note: our CFReadStream/CFWriteStream implementation sent neither the code nor the reason
+           in this case, which matches no other browser.
+
+        Covered by an API test.
+
+        * NetworkProcess/cocoa/WebSocketTaskCocoa.mm:
+        (WebKit::WebSocketTask::close):
+        * WebProcess/Network/WebSocketChannel.cpp:
+        (WebKit::WebSocketChannel::fail):
+        (WebKit::WebSocketChannel::disconnect):
+
 2021-07-28  Philippe Normand  <[email protected]>
 
         [WPE][GTK] SVN_REVISION drifting away if bots don't re-run cmake

Modified: trunk/Source/WebKit/NetworkProcess/cocoa/WebSocketTaskCocoa.mm (280384 => 280385)


--- trunk/Source/WebKit/NetworkProcess/cocoa/WebSocketTaskCocoa.mm	2021-07-28 15:55:25 UTC (rev 280384)
+++ trunk/Source/WebKit/NetworkProcess/cocoa/WebSocketTaskCocoa.mm	2021-07-28 15:57:49 UTC (rev 280385)
@@ -140,9 +140,8 @@
 
 void WebSocketTask::close(int32_t code, const String& reason)
 {
-    // FIXME: Should NSURLSession provide a way to call cancelWithCloseCode without any specific code.
     if (code == WebCore::WebSocketChannel::CloseEventCodeNotSpecified)
-        code = 1005;
+        code = NSURLSessionWebSocketCloseCodeInvalid;
     auto utf8 = reason.utf8();
     auto nsData = adoptNS([[NSData alloc] initWithBytes:utf8.data() length:utf8.length()]);
     if ([m_task respondsToSelector:@selector(_sendCloseCode:reason:)]) {

Modified: trunk/Source/WebKit/WebProcess/Network/WebSocketChannel.cpp (280384 => 280385)


--- trunk/Source/WebKit/WebProcess/Network/WebSocketChannel.cpp	2021-07-28 15:55:25 UTC (rev 280384)
+++ trunk/Source/WebKit/WebProcess/Network/WebSocketChannel.cpp	2021-07-28 15:57:49 UTC (rev 280385)
@@ -224,7 +224,7 @@
     if (m_isClosing)
         return;
 
-    MessageSender::send(Messages::NetworkSocketChannel::Close { 0, reason });
+    MessageSender::send(Messages::NetworkSocketChannel::Close { WebCore::WebSocketChannel::CloseEventCodeGoingAway, reason });
     didClose(WebCore::WebSocketChannel::CloseEventCodeAbnormalClosure, { });
 }
 
@@ -235,10 +235,9 @@
     m_pendingTasks.clear();
     m_messageQueue.clear();
 
-
     m_inspector.didCloseWebSocket(m_document.get());
 
-    MessageSender::send(Messages::NetworkSocketChannel::Close { 0, { } });
+    MessageSender::send(Messages::NetworkSocketChannel::Close { WebCore::WebSocketChannel::CloseEventCodeGoingAway, { } });
 }
 
 void WebSocketChannel::didConnect(String&& subprotocol, String&& extensions)

Modified: trunk/Tools/ChangeLog (280384 => 280385)


--- trunk/Tools/ChangeLog	2021-07-28 15:55:25 UTC (rev 280384)
+++ trunk/Tools/ChangeLog	2021-07-28 15:57:49 UTC (rev 280385)
@@ -1,3 +1,13 @@
+2021-07-28  Alex Christensen  <[email protected]>
+
+        WebSocket: Safari on iOS 15 beta 3 is sending invalid close frame
+        https://bugs.webkit.org/show_bug.cgi?id=228329
+
+        Reviewed by Youenn Fablet.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/WebSocket.mm:
+        (TestWebKitAPI::TEST):
+
 2021-07-28  Philippe Normand  <[email protected]>
 
         [WPE][GTK] SVN_REVISION drifting away if bots don't re-run cmake

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebSocket.mm (280384 => 280385)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebSocket.mm	2021-07-28 15:55:25 UTC (rev 280384)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebSocket.mm	2021-07-28 15:57:49 UTC (rev 280385)
@@ -127,4 +127,89 @@
     EXPECT_EQ(originalNetworkProcessPID, configuration.get().websiteDataStore._networkProcessIdentifier);
 }
 
+#if HAVE(NSURLSESSION_WEBSOCKET)
+TEST(WebSocket, CloseCode)
+{
+    bool receivedWebSocketClose { false };
+    Vector<uint8_t> closeData;
+    HTTPServer webSocketServer([&](Connection connection) {
+        connection.webSocketHandshake([&, connection] {
+            connection.receiveBytes([&, connection](Vector<uint8_t>&& v) {
+                // https://datatracker.ietf.org/doc/html/rfc6455#section-5.2
+                constexpr uint8_t connectionCloseOpcode { 0x08 };
+                constexpr uint8_t opcodeMask { 0x08 };
+                constexpr uint8_t maskMask { 0x80 };
+                constexpr uint8_t payloadLengthMask { 0x7F };
+                constexpr uint8_t headerSize { 6 };
+                ASSERT_GT(v.size(), 1u);
+                EXPECT_EQ(v[0] & opcodeMask, connectionCloseOpcode);
+                EXPECT_TRUE(v[1] & maskMask);
+                size_t length = v[1] & payloadLengthMask;
+                ASSERT_EQ(length, v.size() - headerSize);
+                std::array<uint8_t, 4> mask { v[2], v[3], v[4], v[5] };
+                for (size_t i = headerSize; i < v.size(); i++)
+                    closeData.append(v[i] ^ mask[(i - headerSize) % 4]);
+                receivedWebSocketClose = true;
+                connection.receiveBytes([](Vector<uint8_t>&& v) {
+                    EXPECT_EQ(v.size(), 0u);
+                });
+            });
+        });
+    });
+
+    auto htmlWithOnOpen = [&] (const char* onopen) {
+        return [NSString stringWithFormat:@""
+            "<script>"
+            "    var ws = new WebSocket('ws://127.0.0.1:%d/');"
+            "    ws._onopen_ = function() { %s };"
+                "</script>", webSocketServer.port(), onopen];
+    };
+
+    HTTPServer httpServer({
+        { "/navigateAway", { htmlWithOnOpen("window.location = '/navigationTarget'") }},
+        { "/navigationTarget", { "hi" } },
+        { "/closeCustomCode", { htmlWithOnOpen("ws.close(3000)") } },
+        { "/closeNoArguments", { htmlWithOnOpen("ws.close()") } },
+        { "/closeBothParameters", { htmlWithOnOpen("ws.close(3001, 'custom reason')") } },
+    });
+
+    auto appendString = [] (Vector<uint8_t>& vector, const char* string) {
+        auto length = strlen(string);
+        for (size_t i = 0; i < length; i++)
+            vector.append(string[i]);
+    };
+
+    auto webView = adoptNS([WKWebView new]);
+    [webView loadRequest:httpServer.request("/navigateAway")];
+    [webView _test_waitForDidFinishNavigation];
+    [webView _test_waitForDidFinishNavigation];
+    Util::run(&receivedWebSocketClose);
+    Vector<uint8_t> expected { 0x3, 0xe9 }; // NSURLSessionWebSocketCloseCodeGoingAway
+    appendString(expected, "WebSocket is closed due to suspension.");
+    EXPECT_EQ(closeData, expected);
+
+    receivedWebSocketClose = false;
+    closeData = { };
+    expected = { 0xb, 0xb8 }; // 3000
+    [webView loadRequest:httpServer.request("/closeCustomCode")];
+    Util::run(&receivedWebSocketClose);
+    EXPECT_EQ(closeData, expected);
+
+    receivedWebSocketClose = false;
+    closeData = { };
+    expected = { };
+    [webView loadRequest:httpServer.request("/closeNoArguments")];
+    Util::run(&receivedWebSocketClose);
+    EXPECT_EQ(closeData, expected);
+
+    receivedWebSocketClose = false;
+    closeData = { };
+    expected = { 0xb, 0xb9 }; // 3001
+    appendString(expected, "custom reason");
+    [webView loadRequest:httpServer.request("/closeBothParameters")];
+    Util::run(&receivedWebSocketClose);
+    EXPECT_EQ(closeData, expected);
 }
+#endif // HAVE(NSURLSESSION_WEBSOCKET)
+
+}
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to