Title: [148237] trunk/Source/WebCore
Revision
148237
Author
[email protected]
Date
2013-04-11 14:42:44 -0700 (Thu, 11 Apr 2013)

Log Message

        <rdar://problem/10416316> [Mac] WebSocket doesn't work with authenticating proxies
        https://bugs.webkit.org/show_bug.cgi?id=114464

        Reviewed by Brady Eidson.

        * platform/mac/WebCoreSystemInterface.h:
        * platform/mac/WebCoreSystemInterface.mm:
        Updated for new wkCopyCONNECTProxyResponse signature.

        * platform/network/cf/SocketStreamHandleCFNet.cpp:
        (WebCore::SocketStreamHandle::executePACFileURL): Fixed a typo in a comment.
        (WebCore::SocketStreamHandle::addCONNECTCredentials): Don't crash even if the rest
        of the fix didn't work (which would be the case on some OS versions).
        (WebCore::SocketStreamHandle::readStreamCallback): Replaced most breaks with returns,
        because breaks were confusing in such a huge switch. Changed null proxyResponse
        to not be treated as authentication success, because it's not. Merged two parts
        of WaitingForConnect state handling for clarity.
        (WebCore::SocketStreamHandle::writeStreamCallback): Don't blindly assume that we
        can start WebSocket handshake after kCFStreamEventCanAcceptBytes. Perhaps it's
        nothing but a failed CONNECT, and a read callback still needs to send authentication.
        Without this, establishing connections was flaky. Added a check for Closed state,
        matching read callback.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (148236 => 148237)


--- trunk/Source/WebCore/ChangeLog	2013-04-11 21:41:28 UTC (rev 148236)
+++ trunk/Source/WebCore/ChangeLog	2013-04-11 21:42:44 UTC (rev 148237)
@@ -1,3 +1,28 @@
+2013-04-11  Alexey Proskuryakov  <[email protected]>
+
+        <rdar://problem/10416316> [Mac] WebSocket doesn't work with authenticating proxies
+        https://bugs.webkit.org/show_bug.cgi?id=114464
+
+        Reviewed by Brady Eidson.
+
+        * platform/mac/WebCoreSystemInterface.h:
+        * platform/mac/WebCoreSystemInterface.mm:
+        Updated for new wkCopyCONNECTProxyResponse signature.
+
+        * platform/network/cf/SocketStreamHandleCFNet.cpp:
+        (WebCore::SocketStreamHandle::executePACFileURL): Fixed a typo in a comment.
+        (WebCore::SocketStreamHandle::addCONNECTCredentials): Don't crash even if the rest
+        of the fix didn't work (which would be the case on some OS versions).
+        (WebCore::SocketStreamHandle::readStreamCallback): Replaced most breaks with returns,
+        because breaks were confusing in such a huge switch. Changed null proxyResponse
+        to not be treated as authentication success, because it's not. Merged two parts
+        of WaitingForConnect state handling for clarity.
+        (WebCore::SocketStreamHandle::writeStreamCallback): Don't blindly assume that we
+        can start WebSocket handshake after kCFStreamEventCanAcceptBytes. Perhaps it's
+        nothing but a failed CONNECT, and a read callback still needs to send authentication.
+        Without this, establishing connections was flaky. Added a check for Closed state,
+        matching read callback.
+
 2013-04-11  Sukolsak Sakshuwong  <[email protected]>
 
         MutationRecord is not exposed

Modified: trunk/Source/WebCore/platform/mac/WebCoreSystemInterface.h (148236 => 148237)


--- trunk/Source/WebCore/platform/mac/WebCoreSystemInterface.h	2013-04-11 21:41:28 UTC (rev 148236)
+++ trunk/Source/WebCore/platform/mac/WebCoreSystemInterface.h	2013-04-11 21:42:44 UTC (rev 148237)
@@ -214,7 +214,7 @@
 extern void (*wkSetHTTPPipeliningMinimumFastLanePriority)(int priority);
 extern void (*wkSetCONNECTProxyForStream)(CFReadStreamRef, CFStringRef proxyHost, CFNumberRef proxyPort);
 extern void (*wkSetCONNECTProxyAuthorizationForStream)(CFReadStreamRef, CFStringRef proxyAuthorizationString);
-extern CFHTTPMessageRef (*wkCopyCONNECTProxyResponse)(CFReadStreamRef, CFURLRef responseURL);
+extern CFHTTPMessageRef (*wkCopyCONNECTProxyResponse)(CFReadStreamRef, CFURLRef responseURL, CFStringRef proxyHost, CFNumberRef proxyPort);
 
 extern void (*wkGetGlyphsForCharacters)(CGFontRef, const UniChar[], CGGlyph[], size_t);
 extern bool (*wkGetVerticalGlyphsForCharacters)(CTFontRef, const UniChar[], CGGlyph[], size_t);

Modified: trunk/Source/WebCore/platform/mac/WebCoreSystemInterface.mm (148236 => 148237)


--- trunk/Source/WebCore/platform/mac/WebCoreSystemInterface.mm	2013-04-11 21:41:28 UTC (rev 148236)
+++ trunk/Source/WebCore/platform/mac/WebCoreSystemInterface.mm	2013-04-11 21:42:44 UTC (rev 148237)
@@ -117,7 +117,7 @@
 void (*wkSetHTTPPipeliningMinimumFastLanePriority)(int priority);
 void (*wkSetCONNECTProxyForStream)(CFReadStreamRef, CFStringRef proxyHost, CFNumberRef proxyPort);
 void (*wkSetCONNECTProxyAuthorizationForStream)(CFReadStreamRef, CFStringRef proxyAuthorizationString);
-CFHTTPMessageRef (*wkCopyCONNECTProxyResponse)(CFReadStreamRef, CFURLRef responseURL);
+CFHTTPMessageRef (*wkCopyCONNECTProxyResponse)(CFReadStreamRef, CFURLRef responseURL, CFStringRef proxyHost, CFNumberRef proxyPort);
 
 #if USE(CFNETWORK)
 CFHTTPCookieStorageRef (*wkGetDefaultHTTPCookieStorage)();

Modified: trunk/Source/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp (148236 => 148237)


--- trunk/Source/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp	2013-04-11 21:41:28 UTC (rev 148236)
+++ trunk/Source/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp	2013-04-11 21:42:44 UTC (rev 148237)
@@ -149,7 +149,7 @@
 
 void SocketStreamHandle::executePACFileURL(CFURLRef pacFileURL)
 {
-    // CFNetwork returns an empty proxy array for WebScoket schemes, so use m_httpsURL.
+    // CFNetwork returns an empty proxy array for WebSocket schemes, so use m_httpsURL.
     CFStreamClientContext clientContext = { 0, this, retainSocketStreamHandle, releaseSocketStreamHandle, copyPACExecutionDescription };
     m_pacRunLoopSource.adoptCF(CFNetworkExecuteProxyAutoConfigurationURL(pacFileURL, m_httpsURL.get(), pacExecutionCallback, &clientContext));
 #if PLATFORM(WIN)
@@ -352,6 +352,13 @@
     CFNumberGetValue(m_proxyPort.get(), kCFNumberIntType, &port);
     RetainPtr<CFStringRef> methodCF(AdoptCF, CFHTTPAuthenticationCopyMethod(authentication.get()));
     RetainPtr<CFStringRef> realmCF(AdoptCF, CFHTTPAuthenticationCopyRealm(authentication.get()));
+
+    if (!methodCF || !realmCF) {
+        // This shouldn't happen, but on some OS versions we get incomplete authentication data, see <rdar://problem/10416316>.
+        m_client->didFailSocketStream(this, SocketStreamError(0, m_url.string(), "WebSocket proxy authentication couldn't be handled"));
+        return;
+    }
+
     ProtectionSpace protectionSpace(String(m_proxyHost.get()), port, ProtectionSpaceProxyHTTPS, String(realmCF.get()), authenticationSchemeFromAuthenticationMethod(methodCF.get()));
     String login;
     String password;
@@ -438,42 +445,42 @@
 {
     switch(type) {
     case kCFStreamEventNone:
-        break;
+        return;
     case kCFStreamEventOpenCompleted:
-        break;
+        return;
     case kCFStreamEventHasBytesAvailable: {
+        if (m_connectingSubstate == WaitingForCredentials)
+            return;
+
         if (m_connectingSubstate == WaitingForConnect) {
             if (m_connectionType == CONNECTProxy) {
-                RetainPtr<CFHTTPMessageRef> proxyResponse(AdoptCF, wkCopyCONNECTProxyResponse(m_readStream.get(), m_httpsURL.get()));
-                if (proxyResponse) {
-                    CFIndex proxyResponseCode = CFHTTPMessageGetResponseStatusCode(proxyResponse.get());
-                    switch (proxyResponseCode) {
-                    case 200:
-                        // Successful connection.
-                        break;
-                    case 407:
-                        addCONNECTCredentials(proxyResponse.get());
-                        return;
-                    default:
-                        m_client->didFailSocketStream(this, SocketStreamError(static_cast<int>(proxyResponseCode), m_url.string(), "Proxy connection could not be established"));
-                        platformClose();
-                        return;
-                    }
+                RetainPtr<CFHTTPMessageRef> proxyResponse(AdoptCF, wkCopyCONNECTProxyResponse(m_readStream.get(), m_httpsURL.get(), m_proxyHost.get(), m_proxyPort.get()));
+                if (!proxyResponse)
+                    return;
+
+                CFIndex proxyResponseCode = CFHTTPMessageGetResponseStatusCode(proxyResponse.get());
+                switch (proxyResponseCode) {
+                case 200:
+                    // Successful connection.
+                    break;
+                case 407:
+                    addCONNECTCredentials(proxyResponse.get());
+                    return;
+                default:
+                    m_client->didFailSocketStream(this, SocketStreamError(static_cast<int>(proxyResponseCode), m_url.string(), "Proxy connection could not be established, unexpected response code"));
+                    platformClose();
+                    return;
                 }
             }
-        } else if (m_connectingSubstate == WaitingForCredentials)
-            break;
-
-        if (m_connectingSubstate == WaitingForConnect) {
             m_connectingSubstate = Connected;
             m_state = Open;
             m_client->didOpenSocketStream(this);
-            if (m_state == Closed)
-                break;
-            // Fall through.
-        } else if (m_state == Closed)
-            break;
+        }
 
+        // Not an "else if", we could have made a client call above, and it could close the connection.
+        if (m_state == Closed)
+            return;
+
         ASSERT(m_state == Open);
         ASSERT(m_connectingSubstate == Connected);
 
@@ -487,19 +494,19 @@
 
         m_client->didReceiveSocketStreamData(this, reinterpret_cast<const char*>(ptr), length);
 
-        break;
+        return;
     }
     case kCFStreamEventCanAcceptBytes:
         ASSERT_NOT_REACHED();
-        break;
+        return;
     case kCFStreamEventErrorOccurred: {
         RetainPtr<CFErrorRef> error(AdoptCF, CFReadStreamCopyError(m_readStream.get()));
         reportErrorToClient(error.get());
-        break;
+        return;
     }
     case kCFStreamEventEndEncountered:
         platformClose();
-        break;
+        return;
     }
 }
 
@@ -507,41 +514,55 @@
 {
     switch(type) {
     case kCFStreamEventNone:
-        break;
+        return;
     case kCFStreamEventOpenCompleted:
-        break;
+        return;
     case kCFStreamEventHasBytesAvailable:
         ASSERT_NOT_REACHED();
-        break;
+        return;
     case kCFStreamEventCanAcceptBytes: {
-        // Possibly, a spurious event from CONNECT handshake.
+        // Can be false if read stream callback just decided to retry a CONNECT with credentials.
         if (!CFWriteStreamCanAcceptBytes(m_writeStream.get()))
             return;
 
         if (m_connectingSubstate == WaitingForCredentials)
-            break;
+            return;
 
         if (m_connectingSubstate == WaitingForConnect) {
+            if (m_connectionType == CONNECTProxy) {
+                RetainPtr<CFHTTPMessageRef> proxyResponse(AdoptCF, wkCopyCONNECTProxyResponse(m_readStream.get(), m_httpsURL.get(), m_proxyHost.get(), m_proxyPort.get()));
+                if (!proxyResponse)
+                    return;
+
+                // Don't write anything until read stream callback has dealt with CONNECT credentials.
+                // The order of callbacks is not defined, so this can be called before readStreamCallback's kCFStreamEventHasBytesAvailable.
+                CFIndex proxyResponseCode = CFHTTPMessageGetResponseStatusCode(proxyResponse.get());
+                if (proxyResponseCode != 200)
+                    return;
+            }
             m_connectingSubstate = Connected;
             m_state = Open;
             m_client->didOpenSocketStream(this);
-            break;
         }
 
+        // Not an "else if", we could have made a client call above, and it could close the connection.
+        if (m_state == Closed)
+            return;
+
         ASSERT(m_state == Open);
         ASSERT(m_connectingSubstate == Connected);
 
         sendPendingData();
-        break;
+        return;
     }
     case kCFStreamEventErrorOccurred: {
         RetainPtr<CFErrorRef> error(AdoptCF, CFWriteStreamCopyError(m_writeStream.get()));
         reportErrorToClient(error.get());
-        break;
+        return;
     }
     case kCFStreamEventEndEncountered:
         // FIXME: Currently, we handle closing in read callback, but these can come independently (e.g. a server can stop listening, but keep sending data).
-        break;
+        return;
     }
 }
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to