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