Home | History | Annotate | Download | only in cf
      1 /*
      2  * Copyright (C) 2009 Apple Inc.  All rights reserved.
      3  * Copyright (C) 2009 Google Inc.  All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include "config.h"
     33 #include "SocketStreamHandle.h"
     34 
     35 #include "Credential.h"
     36 #include "CredentialStorage.h"
     37 #include "Logging.h"
     38 #include "ProtectionSpace.h"
     39 #include "SocketStreamError.h"
     40 #include "SocketStreamHandleClient.h"
     41 #include <wtf/MainThread.h>
     42 #include <wtf/text/StringConcatenate.h>
     43 
     44 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
     45 #include <SystemConfiguration/SystemConfiguration.h>
     46 #endif
     47 
     48 #if PLATFORM(WIN)
     49 #include "LoaderRunLoopCF.h"
     50 #include <CFNetwork/CFNetwork.h>
     51 #include <WebKitSystemInterface/WebKitSystemInterface.h>
     52 #else
     53 #include "WebCoreSystemInterface.h"
     54 #endif
     55 
     56 #ifdef BUILDING_ON_TIGER
     57 #define CFN_EXPORT extern
     58 #endif
     59 
     60 namespace WebCore {
     61 
     62 SocketStreamHandle::SocketStreamHandle(const KURL& url, SocketStreamHandleClient* client)
     63     : SocketStreamHandleBase(url, client)
     64     , m_connectingSubstate(New)
     65     , m_connectionType(Unknown)
     66     , m_sentStoredCredentials(false)
     67 {
     68     LOG(Network, "SocketStreamHandle %p new client %p", this, m_client);
     69 
     70     ASSERT(url.protocolIs("ws") || url.protocolIs("wss"));
     71 
     72     KURL httpsURL(KURL(), "https://" + m_url.host());
     73     m_httpsURL.adoptCF(httpsURL.createCFURL());
     74 
     75     createStreams();
     76     ASSERT(!m_readStream == !m_writeStream);
     77     if (!m_readStream) // Doing asynchronous PAC file processing, streams will be created later.
     78         return;
     79 
     80     scheduleStreams();
     81 }
     82 
     83 void SocketStreamHandle::scheduleStreams()
     84 {
     85     ASSERT(m_readStream);
     86     ASSERT(m_writeStream);
     87 
     88     CFStreamClientContext clientContext = { 0, this, 0, 0, copyCFStreamDescription };
     89     // FIXME: Pass specific events we're interested in instead of -1.
     90     CFReadStreamSetClient(m_readStream.get(), static_cast<CFOptionFlags>(-1), readStreamCallback, &clientContext);
     91     CFWriteStreamSetClient(m_writeStream.get(), static_cast<CFOptionFlags>(-1), writeStreamCallback, &clientContext);
     92 
     93 #if PLATFORM(WIN)
     94     CFReadStreamScheduleWithRunLoop(m_readStream.get(), loaderRunLoop(), kCFRunLoopDefaultMode);
     95     CFWriteStreamScheduleWithRunLoop(m_writeStream.get(), loaderRunLoop(), kCFRunLoopDefaultMode);
     96 #else
     97     CFReadStreamScheduleWithRunLoop(m_readStream.get(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
     98     CFWriteStreamScheduleWithRunLoop(m_writeStream.get(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
     99 #endif
    100 
    101     CFReadStreamOpen(m_readStream.get());
    102     CFWriteStreamOpen(m_writeStream.get());
    103 
    104 #ifndef BUILDING_ON_TIGER
    105     if (m_pacRunLoopSource)
    106         removePACRunLoopSource();
    107 #endif
    108 
    109     m_connectingSubstate = WaitingForConnect;
    110 }
    111 
    112 #ifndef BUILDING_ON_TIGER
    113 CFStringRef SocketStreamHandle::copyPACExecutionDescription(void*)
    114 {
    115     return CFSTR("WebSocket proxy PAC file execution");
    116 }
    117 
    118 struct MainThreadPACCallbackInfo {
    119     MainThreadPACCallbackInfo(SocketStreamHandle* handle, CFArrayRef proxyList) : handle(handle), proxyList(proxyList) { }
    120     RefPtr<SocketStreamHandle> handle;
    121     CFArrayRef proxyList;
    122 };
    123 
    124 void SocketStreamHandle::pacExecutionCallback(void* client, CFArrayRef proxyList, CFErrorRef)
    125 {
    126     SocketStreamHandle* handle = static_cast<SocketStreamHandle*>(client);
    127     MainThreadPACCallbackInfo info(handle, proxyList);
    128     // If we're already on main thread (e.g. on Mac), callOnMainThreadAndWait() will be just a function call.
    129     callOnMainThreadAndWait(pacExecutionCallbackMainThread, &info);
    130 }
    131 
    132 void SocketStreamHandle::pacExecutionCallbackMainThread(void* invocation)
    133 {
    134     MainThreadPACCallbackInfo* info = static_cast<MainThreadPACCallbackInfo*>(invocation);
    135     ASSERT(info->handle->m_connectingSubstate == ExecutingPACFile);
    136     // This time, the array won't have PAC as a first entry.
    137     info->handle->chooseProxyFromArray(info->proxyList);
    138     info->handle->createStreams();
    139     info->handle->scheduleStreams();
    140 }
    141 
    142 void SocketStreamHandle::executePACFileURL(CFURLRef pacFileURL)
    143 {
    144     // CFNetwork returns an empty proxy array for WebScoket schemes, so use m_httpsURL.
    145     CFStreamClientContext clientContext = { 0, this, 0, 0, copyPACExecutionDescription };
    146     m_pacRunLoopSource.adoptCF(CFNetworkExecuteProxyAutoConfigurationURL(pacFileURL, m_httpsURL.get(), pacExecutionCallback, &clientContext));
    147 #if PLATFORM(WIN)
    148     CFRunLoopAddSource(loaderRunLoop(), m_pacRunLoopSource.get(), kCFRunLoopDefaultMode);
    149 #else
    150     CFRunLoopAddSource(CFRunLoopGetCurrent(), m_pacRunLoopSource.get(), kCFRunLoopCommonModes);
    151 #endif
    152     m_connectingSubstate = ExecutingPACFile;
    153 }
    154 
    155 void SocketStreamHandle::removePACRunLoopSource()
    156 {
    157     ASSERT(m_pacRunLoopSource);
    158 
    159     CFRunLoopSourceInvalidate(m_pacRunLoopSource.get());
    160 #if PLATFORM(WIN)
    161     CFRunLoopRemoveSource(loaderRunLoop(), m_pacRunLoopSource.get(), kCFRunLoopDefaultMode);
    162 #else
    163     CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m_pacRunLoopSource.get(), kCFRunLoopCommonModes);
    164 #endif
    165     m_pacRunLoopSource = 0;
    166 }
    167 
    168 void SocketStreamHandle::chooseProxy()
    169 {
    170 #ifndef BUILDING_ON_LEOPARD
    171     RetainPtr<CFDictionaryRef> proxyDictionary(AdoptCF, CFNetworkCopySystemProxySettings());
    172 #else
    173     // We don't need proxy information often, so there is no need to set up a permanent dynamic store session.
    174     RetainPtr<CFDictionaryRef> proxyDictionary(AdoptCF, SCDynamicStoreCopyProxies(0));
    175 #endif
    176 
    177     // SOCKS or HTTPS (AKA CONNECT) proxies are supported.
    178     // WebSocket protocol relies on handshake being transferred unchanged, so we need a proxy that will not modify headers.
    179     // Since HTTP proxies must add Via headers, they are highly unlikely to work.
    180     // Many CONNECT proxies limit connectivity to port 443, so we prefer SOCKS, if configured.
    181 
    182     if (!proxyDictionary) {
    183         m_connectionType = Direct;
    184         return;
    185     }
    186 
    187     // CFNetworkCopyProxiesForURL doesn't know about WebSocket schemes, so pretend to use http.
    188     // Always use "https" to get HTTPS proxies in result - we'll try to use those for ws:// even though many are configured to reject connections to ports other than 443.
    189     RetainPtr<CFArrayRef> proxyArray(AdoptCF, CFNetworkCopyProxiesForURL(m_httpsURL.get(), proxyDictionary.get()));
    190 
    191     chooseProxyFromArray(proxyArray.get());
    192 }
    193 
    194 void SocketStreamHandle::chooseProxyFromArray(CFArrayRef proxyArray)
    195 {
    196     if (!proxyArray) {
    197         m_connectionType = Direct;
    198         return;
    199     }
    200 
    201     CFIndex proxyArrayCount = CFArrayGetCount(proxyArray);
    202 
    203     // PAC is always the first entry, if present.
    204     if (proxyArrayCount) {
    205         CFDictionaryRef proxyInfo = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(proxyArray, 0));
    206         CFTypeRef proxyType = CFDictionaryGetValue(proxyInfo, kCFProxyTypeKey);
    207         if (proxyType && CFGetTypeID(proxyType) == CFStringGetTypeID()) {
    208             if (CFEqual(proxyType, kCFProxyTypeAutoConfigurationURL)) {
    209                 CFTypeRef pacFileURL = CFDictionaryGetValue(proxyInfo, kCFProxyAutoConfigurationURLKey);
    210                 if (pacFileURL && CFGetTypeID(pacFileURL) == CFURLGetTypeID()) {
    211                     executePACFileURL(static_cast<CFURLRef>(pacFileURL));
    212                     return;
    213                 }
    214             }
    215         }
    216     }
    217 
    218     CFDictionaryRef chosenProxy = 0;
    219     for (CFIndex i = 0; i < proxyArrayCount; ++i) {
    220         CFDictionaryRef proxyInfo = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(proxyArray, i));
    221         CFTypeRef proxyType = CFDictionaryGetValue(proxyInfo, kCFProxyTypeKey);
    222         if (proxyType && CFGetTypeID(proxyType) == CFStringGetTypeID()) {
    223             if (CFEqual(proxyType, kCFProxyTypeSOCKS)) {
    224                 m_connectionType = SOCKSProxy;
    225                 chosenProxy = proxyInfo;
    226                 break;
    227             }
    228             if (CFEqual(proxyType, kCFProxyTypeHTTPS)) {
    229                 m_connectionType = CONNECTProxy;
    230                 chosenProxy = proxyInfo;
    231                 // Keep looking for proxies, as a SOCKS one is preferable.
    232             }
    233         }
    234     }
    235 
    236     if (chosenProxy) {
    237         ASSERT(m_connectionType != Unknown);
    238         ASSERT(m_connectionType != Direct);
    239 
    240         CFTypeRef proxyHost = CFDictionaryGetValue(chosenProxy, kCFProxyHostNameKey);
    241         CFTypeRef proxyPort = CFDictionaryGetValue(chosenProxy, kCFProxyPortNumberKey);
    242 
    243         if (proxyHost && CFGetTypeID(proxyHost) == CFStringGetTypeID() && proxyPort && CFGetTypeID(proxyPort) == CFNumberGetTypeID()) {
    244             m_proxyHost = static_cast<CFStringRef>(proxyHost);
    245             m_proxyPort = static_cast<CFNumberRef>(proxyPort);
    246             return;
    247         }
    248     }
    249 
    250     m_connectionType = Direct;
    251 }
    252 
    253 #else // BUILDING_ON_TIGER
    254 
    255 void SocketStreamHandle::chooseProxy()
    256 {
    257     // We don't need proxy information often, so there is no need to set up a permanent dynamic store session.
    258     RetainPtr<CFDictionaryRef> proxyDictionary(AdoptCF, SCDynamicStoreCopyProxies(0));
    259 
    260     // SOCKS or HTTPS (AKA CONNECT) proxies are supported.
    261     // WebSocket protocol relies on handshake being transferred unchanged, so we need a proxy that will not modify headers.
    262     // Since HTTP proxies must add Via headers, they are highly unlikely to work.
    263     // Many CONNECT proxies limit connectivity to port 443, so we prefer SOCKS, if configured.
    264 
    265     if (!proxyDictionary) {
    266         m_connectionType = Direct;
    267         return;
    268     }
    269 
    270     // FIXME: check proxy bypass list and ExcludeSimpleHostnames.
    271     // FIXME: Support PAC files.
    272 
    273     CFTypeRef socksEnableCF = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesSOCKSEnable);
    274     int socksEnable;
    275     if (socksEnableCF && CFGetTypeID(socksEnableCF) == CFNumberGetTypeID() && CFNumberGetValue(static_cast<CFNumberRef>(socksEnableCF), kCFNumberIntType, &socksEnable) && socksEnable) {
    276         CFTypeRef proxyHost = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesSOCKSProxy);
    277         CFTypeRef proxyPort = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesSOCKSPort);
    278         if (proxyHost && CFGetTypeID(proxyHost) == CFStringGetTypeID() && proxyPort && CFGetTypeID(proxyPort) == CFNumberGetTypeID()) {
    279             m_proxyHost = static_cast<CFStringRef>(proxyHost);
    280             m_proxyPort = static_cast<CFNumberRef>(proxyPort);
    281             m_connectionType = SOCKSProxy;
    282             return;
    283         }
    284     }
    285 
    286     CFTypeRef httpsEnableCF = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesHTTPSEnable);
    287     int httpsEnable;
    288     if (httpsEnableCF && CFGetTypeID(httpsEnableCF) == CFNumberGetTypeID() && CFNumberGetValue(static_cast<CFNumberRef>(httpsEnableCF), kCFNumberIntType, &httpsEnable) && httpsEnable) {
    289         CFTypeRef proxyHost = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesHTTPSProxy);
    290         CFTypeRef proxyPort = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesHTTPSPort);
    291 
    292         if (proxyHost && CFGetTypeID(proxyHost) == CFStringGetTypeID() && proxyPort && CFGetTypeID(proxyPort) == CFNumberGetTypeID()) {
    293             m_proxyHost = static_cast<CFStringRef>(proxyHost);
    294             m_proxyPort = static_cast<CFNumberRef>(proxyPort);
    295             m_connectionType = CONNECTProxy;
    296             return;
    297         }
    298     }
    299 
    300     m_connectionType = Direct;
    301 }
    302 #endif // BUILDING_ON_TIGER
    303 
    304 void SocketStreamHandle::createStreams()
    305 {
    306     if (m_connectionType == Unknown)
    307         chooseProxy();
    308 
    309     // If it's still unknown, then we're resolving a PAC file asynchronously.
    310     if (m_connectionType == Unknown)
    311         return;
    312 
    313     RetainPtr<CFStringRef> host(AdoptCF, m_url.host().createCFString());
    314 
    315     // Creating streams to final destination, not to proxy.
    316     CFReadStreamRef readStream = 0;
    317     CFWriteStreamRef writeStream = 0;
    318     CFStreamCreatePairWithSocketToHost(0, host.get(), port(), &readStream, &writeStream);
    319 
    320     m_readStream.adoptCF(readStream);
    321     m_writeStream.adoptCF(writeStream);
    322 
    323     switch (m_connectionType) {
    324     case Unknown:
    325         ASSERT_NOT_REACHED();
    326         break;
    327     case Direct:
    328         break;
    329     case SOCKSProxy: {
    330         // FIXME: SOCKS5 doesn't do challenge-response, should we try to apply credentials from Keychain right away?
    331         // But SOCKS5 credentials don't work at the time of this writing anyway, see <rdar://6776698>.
    332         const void* proxyKeys[] = { kCFStreamPropertySOCKSProxyHost, kCFStreamPropertySOCKSProxyPort };
    333         const void* proxyValues[] = { m_proxyHost.get(), m_proxyPort.get() };
    334         RetainPtr<CFDictionaryRef> connectDictionary(AdoptCF, CFDictionaryCreate(0, proxyKeys, proxyValues, WTF_ARRAY_LENGTH(proxyKeys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
    335         CFReadStreamSetProperty(m_readStream.get(), kCFStreamPropertySOCKSProxy, connectDictionary.get());
    336         break;
    337         }
    338     case CONNECTProxy:
    339         wkSetCONNECTProxyForStream(m_readStream.get(), m_proxyHost.get(), m_proxyPort.get());
    340         break;
    341     }
    342 
    343     if (shouldUseSSL()) {
    344         const void* keys[] = { kCFStreamSSLPeerName, kCFStreamSSLLevel };
    345         const void* values[] = { host.get(), kCFStreamSocketSecurityLevelNegotiatedSSL };
    346         RetainPtr<CFDictionaryRef> settings(AdoptCF, CFDictionaryCreate(0, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
    347         CFReadStreamSetProperty(m_readStream.get(), kCFStreamPropertySSLSettings, settings.get());
    348         CFWriteStreamSetProperty(m_writeStream.get(), kCFStreamPropertySSLSettings, settings.get());
    349     }
    350 }
    351 
    352 static bool getStoredCONNECTProxyCredentials(const ProtectionSpace& protectionSpace, String& login, String& password)
    353 {
    354     // Try system credential storage first, matching HTTP behavior (CFNetwork only asks the client for password if it couldn't find it in Keychain).
    355     Credential storedCredential = CredentialStorage::getFromPersistentStorage(protectionSpace);
    356     if (storedCredential.isEmpty())
    357         storedCredential = CredentialStorage::get(protectionSpace);
    358 
    359     if (storedCredential.isEmpty())
    360         return false;
    361 
    362     login = storedCredential.user();
    363     password = storedCredential.password();
    364 
    365     return true;
    366 }
    367 
    368 static ProtectionSpaceAuthenticationScheme authenticationSchemeFromAuthenticationMethod(CFStringRef method)
    369 {
    370     if (CFEqual(method, kCFHTTPAuthenticationSchemeBasic))
    371         return ProtectionSpaceAuthenticationSchemeHTTPBasic;
    372     if (CFEqual(method, kCFHTTPAuthenticationSchemeDigest))
    373         return ProtectionSpaceAuthenticationSchemeHTTPDigest;
    374 #ifndef BUILDING_ON_TIGER
    375     if (CFEqual(method, kCFHTTPAuthenticationSchemeNTLM))
    376         return ProtectionSpaceAuthenticationSchemeNTLM;
    377     if (CFEqual(method, kCFHTTPAuthenticationSchemeNegotiate))
    378         return ProtectionSpaceAuthenticationSchemeNegotiate;
    379 #endif
    380     ASSERT_NOT_REACHED();
    381     return ProtectionSpaceAuthenticationSchemeUnknown;
    382 }
    383 
    384 void SocketStreamHandle::addCONNECTCredentials(CFHTTPMessageRef proxyResponse)
    385 {
    386     RetainPtr<CFHTTPAuthenticationRef> authentication(AdoptCF, CFHTTPAuthenticationCreateFromResponse(0, proxyResponse));
    387 
    388     if (!CFHTTPAuthenticationRequiresUserNameAndPassword(authentication.get())) {
    389         // That's all we can offer...
    390         m_client->didFail(this, SocketStreamError()); // FIXME: Provide a sensible error.
    391         return;
    392     }
    393 
    394     int port = 0;
    395     CFNumberGetValue(m_proxyPort.get(), kCFNumberIntType, &port);
    396     RetainPtr<CFStringRef> methodCF(AdoptCF, CFHTTPAuthenticationCopyMethod(authentication.get()));
    397     RetainPtr<CFStringRef> realmCF(AdoptCF, CFHTTPAuthenticationCopyRealm(authentication.get()));
    398     ProtectionSpace protectionSpace(String(m_proxyHost.get()), port, ProtectionSpaceProxyHTTPS, String(realmCF.get()), authenticationSchemeFromAuthenticationMethod(methodCF.get()));
    399     String login;
    400     String password;
    401     if (!m_sentStoredCredentials && getStoredCONNECTProxyCredentials(protectionSpace, login, password)) {
    402         // Try to apply stored credentials, if we haven't tried those already.
    403         RetainPtr<CFStringRef> loginCF(AdoptCF, login.createCFString());
    404         RetainPtr<CFStringRef> passwordCF(AdoptCF, password.createCFString());
    405         // Creating a temporary request to make CFNetwork apply credentials to it. Unfortunately, this cannot work with NTLM authentication.
    406         RetainPtr<CFHTTPMessageRef> dummyRequest(AdoptCF, CFHTTPMessageCreateRequest(0, CFSTR("GET"), m_httpsURL.get(), kCFHTTPVersion1_1));
    407 
    408         Boolean appliedCredentials = CFHTTPMessageApplyCredentials(dummyRequest.get(), authentication.get(), loginCF.get(), passwordCF.get(), 0);
    409         ASSERT_UNUSED(appliedCredentials, appliedCredentials);
    410 
    411         RetainPtr<CFStringRef> proxyAuthorizationString(AdoptCF, CFHTTPMessageCopyHeaderFieldValue(dummyRequest.get(), CFSTR("Proxy-Authorization")));
    412 
    413         if (!proxyAuthorizationString) {
    414             // Fails e.g. for NTLM auth.
    415             m_client->didFail(this, SocketStreamError()); // FIXME: Provide a sensible error.
    416             return;
    417         }
    418 
    419         // Setting the authorization results in a new connection attempt.
    420         wkSetCONNECTProxyAuthorizationForStream(m_readStream.get(), proxyAuthorizationString.get());
    421         m_sentStoredCredentials = true;
    422         return;
    423     }
    424 
    425     // FIXME: Ask the client if credentials could not be found.
    426 
    427     m_client->didFail(this, SocketStreamError()); // FIXME: Provide a sensible error.
    428 }
    429 
    430 CFStringRef SocketStreamHandle::copyCFStreamDescription(void* info)
    431 {
    432     SocketStreamHandle* handle = static_cast<SocketStreamHandle*>(info);
    433     return ("WebKit socket stream, " + handle->m_url.string()).createCFString();
    434 }
    435 
    436 struct MainThreadEventCallbackInfo {
    437     MainThreadEventCallbackInfo(CFStreamEventType type, SocketStreamHandle* handle) : type(type), handle(handle) { }
    438     CFStreamEventType type;
    439     RefPtr<SocketStreamHandle> handle;
    440 };
    441 
    442 void SocketStreamHandle::readStreamCallback(CFReadStreamRef stream, CFStreamEventType type, void* clientCallBackInfo)
    443 {
    444     SocketStreamHandle* handle = static_cast<SocketStreamHandle*>(clientCallBackInfo);
    445     ASSERT_UNUSED(stream, stream == handle->m_readStream.get());
    446 #if PLATFORM(WIN)
    447     MainThreadEventCallbackInfo info(type, handle);
    448     callOnMainThreadAndWait(readStreamCallbackMainThread, &info);
    449 #else
    450     ASSERT(isMainThread());
    451     handle->readStreamCallback(type);
    452 #endif
    453 }
    454 
    455 void SocketStreamHandle::writeStreamCallback(CFWriteStreamRef stream, CFStreamEventType type, void* clientCallBackInfo)
    456 {
    457     SocketStreamHandle* handle = static_cast<SocketStreamHandle*>(clientCallBackInfo);
    458     ASSERT_UNUSED(stream, stream == handle->m_writeStream.get());
    459 #if PLATFORM(WIN)
    460     MainThreadEventCallbackInfo info(type, handle);
    461     callOnMainThreadAndWait(writeStreamCallbackMainThread, &info);
    462 #else
    463     ASSERT(isMainThread());
    464     handle->writeStreamCallback(type);
    465 #endif
    466 }
    467 
    468 #if PLATFORM(WIN)
    469 void SocketStreamHandle::readStreamCallbackMainThread(void* invocation)
    470 {
    471     MainThreadEventCallbackInfo* info = static_cast<MainThreadEventCallbackInfo*>(invocation);
    472     info->handle->readStreamCallback(info->type);
    473 }
    474 
    475 void SocketStreamHandle::writeStreamCallbackMainThread(void* invocation)
    476 {
    477     MainThreadEventCallbackInfo* info = static_cast<MainThreadEventCallbackInfo*>(invocation);
    478     info->handle->writeStreamCallback(info->type);
    479 }
    480 #endif // PLATFORM(WIN)
    481 
    482 void SocketStreamHandle::readStreamCallback(CFStreamEventType type)
    483 {
    484     switch(type) {
    485     case kCFStreamEventNone:
    486         break;
    487     case kCFStreamEventOpenCompleted:
    488         break;
    489     case kCFStreamEventHasBytesAvailable: {
    490         if (m_connectingSubstate == WaitingForConnect) {
    491             if (m_connectionType == CONNECTProxy) {
    492                 RetainPtr<CFHTTPMessageRef> proxyResponse(AdoptCF, wkCopyCONNECTProxyResponse(m_readStream.get(), m_httpsURL.get()));
    493                 if (proxyResponse && (407 == CFHTTPMessageGetResponseStatusCode(proxyResponse.get()))) {
    494                     addCONNECTCredentials(proxyResponse.get());
    495                     return;
    496                 }
    497             }
    498         } else if (m_connectingSubstate == WaitingForCredentials)
    499             break;
    500 
    501         if (m_connectingSubstate == WaitingForConnect) {
    502             m_connectingSubstate = Connected;
    503             m_state = Open;
    504 
    505             RefPtr<SocketStreamHandle> protect(this); // The client can close the handle, potentially removing the last reference.
    506             m_client->didOpen(this);
    507             if (m_state == Closed)
    508                 break;
    509             // Fall through.
    510         } else if (m_state == Closed)
    511             break;
    512 
    513         ASSERT(m_state == Open);
    514         ASSERT(m_connectingSubstate == Connected);
    515 
    516         CFIndex length;
    517         UInt8 localBuffer[1024]; // Used if CFReadStreamGetBuffer couldn't return anything.
    518         const UInt8* ptr = CFReadStreamGetBuffer(m_readStream.get(), 0, &length);
    519         if (!ptr) {
    520             length = CFReadStreamRead(m_readStream.get(), localBuffer, sizeof(localBuffer));
    521             ptr = localBuffer;
    522         }
    523 
    524         m_client->didReceiveData(this, reinterpret_cast<const char*>(ptr), length);
    525 
    526         break;
    527     }
    528     case kCFStreamEventCanAcceptBytes:
    529         ASSERT_NOT_REACHED();
    530         break;
    531     case kCFStreamEventErrorOccurred: {
    532 #ifndef BUILDING_ON_TIGER
    533         RetainPtr<CFErrorRef> error(AdoptCF, CFReadStreamCopyError(m_readStream.get()));
    534         reportErrorToClient(error.get());
    535 #else
    536         CFStreamError error = CFReadStreamGetError(m_readStream.get());
    537         m_client->didFail(this, SocketStreamError(error.error)); // FIXME: Provide a sensible error.
    538 #endif
    539         break;
    540     }
    541     case kCFStreamEventEndEncountered:
    542         platformClose();
    543         break;
    544     }
    545 }
    546 
    547 void SocketStreamHandle::writeStreamCallback(CFStreamEventType type)
    548 {
    549     switch(type) {
    550     case kCFStreamEventNone:
    551         break;
    552     case kCFStreamEventOpenCompleted:
    553         break;
    554     case kCFStreamEventHasBytesAvailable:
    555         ASSERT_NOT_REACHED();
    556         break;
    557     case kCFStreamEventCanAcceptBytes: {
    558         // Possibly, a spurious event from CONNECT handshake.
    559         if (!CFWriteStreamCanAcceptBytes(m_writeStream.get()))
    560             return;
    561 
    562         if (m_connectingSubstate == WaitingForCredentials)
    563             break;
    564 
    565         if (m_connectingSubstate == WaitingForConnect) {
    566             m_connectingSubstate = Connected;
    567             m_state = Open;
    568 
    569             RefPtr<SocketStreamHandle> protect(this); // The client can close the handle, potentially removing the last reference.
    570             m_client->didOpen(this);
    571             break;
    572         }
    573 
    574         ASSERT(m_state == Open);
    575         ASSERT(m_connectingSubstate == Connected);
    576 
    577         sendPendingData();
    578         break;
    579     }
    580     case kCFStreamEventErrorOccurred: {
    581 #ifndef BUILDING_ON_TIGER
    582         RetainPtr<CFErrorRef> error(AdoptCF, CFWriteStreamCopyError(m_writeStream.get()));
    583         reportErrorToClient(error.get());
    584 #else
    585         CFStreamError error = CFWriteStreamGetError(m_writeStream.get());
    586         m_client->didFail(this, SocketStreamError(error.error)); // FIXME: Provide a sensible error.
    587 #endif
    588         break;
    589     }
    590     case kCFStreamEventEndEncountered:
    591         // FIXME: Currently, we handle closing in read callback, but these can come independently (e.g. a server can stop listening, but keep sending data).
    592         break;
    593     }
    594 }
    595 
    596 #ifndef BUILDING_ON_TIGER
    597 void SocketStreamHandle::reportErrorToClient(CFErrorRef error)
    598 {
    599     CFIndex errorCode = CFErrorGetCode(error);
    600     String description;
    601 
    602 #if PLATFORM(MAC)
    603     if (CFEqual(CFErrorGetDomain(error), kCFErrorDomainOSStatus)) {
    604         const char* descriptionOSStatus = GetMacOSStatusCommentString(static_cast<OSStatus>(errorCode));
    605         if (descriptionOSStatus && descriptionOSStatus[0] != '\0')
    606             description = makeString("OSStatus Error ", String::number(errorCode), ": ", descriptionOSStatus);
    607     }
    608 #endif
    609 
    610     if (description.isNull()) {
    611         RetainPtr<CFStringRef> descriptionCF(AdoptCF, CFErrorCopyDescription(error));
    612         description = String(descriptionCF.get());
    613     }
    614 
    615     m_client->didFail(this, SocketStreamError(static_cast<int>(errorCode), m_url.string(), description));
    616 }
    617 #endif // BUILDING_ON_TIGER
    618 
    619 SocketStreamHandle::~SocketStreamHandle()
    620 {
    621     LOG(Network, "SocketStreamHandle %p dtor", this);
    622 
    623 #ifndef BUILDING_ON_TIGER
    624     ASSERT(!m_pacRunLoopSource);
    625 #endif
    626 }
    627 
    628 int SocketStreamHandle::platformSend(const char* data, int length)
    629 {
    630     if (!CFWriteStreamCanAcceptBytes(m_writeStream.get()))
    631         return 0;
    632 
    633     return CFWriteStreamWrite(m_writeStream.get(), reinterpret_cast<const UInt8*>(data), length);
    634 }
    635 
    636 void SocketStreamHandle::platformClose()
    637 {
    638     LOG(Network, "SocketStreamHandle %p platformClose", this);
    639 
    640 #ifndef BUILDING_ON_TIGER
    641     if (m_pacRunLoopSource)
    642         removePACRunLoopSource();
    643 #endif
    644 
    645     ASSERT(!m_readStream == !m_writeStream);
    646     if (!m_readStream)
    647         return;
    648 
    649     CFReadStreamUnscheduleFromRunLoop(m_readStream.get(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
    650     CFWriteStreamUnscheduleFromRunLoop(m_writeStream.get(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
    651 
    652     CFReadStreamClose(m_readStream.get());
    653     CFWriteStreamClose(m_writeStream.get());
    654 
    655     m_readStream = 0;
    656     m_writeStream = 0;
    657 
    658     m_client->didClose(this);
    659 }
    660 
    661 void SocketStreamHandle::receivedCredential(const AuthenticationChallenge&, const Credential&)
    662 {
    663 }
    664 
    665 void SocketStreamHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&)
    666 {
    667 }
    668 
    669 void SocketStreamHandle::receivedCancellation(const AuthenticationChallenge&)
    670 {
    671 }
    672 
    673 unsigned short SocketStreamHandle::port() const
    674 {
    675     if (unsigned short urlPort = m_url.port())
    676         return urlPort;
    677     if (shouldUseSSL())
    678         return 443;
    679     return 80;
    680 }
    681 
    682 }  // namespace WebCore
    683