Reviewed by Eric Seidel.
[WebKit-https.git] / WebCore / platform / network / cf / SocketStreamHandleCFNet.cpp
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             m_client->didOpen(this);
504             // Fall through.
505         } else if (m_state == Closed)
506             break;
507
508         ASSERT(m_state == Open);
509         ASSERT(m_connectingSubstate == Connected);
510
511         CFIndex length;
512         UInt8 localBuffer[1024]; // Used if CFReadStreamGetBuffer couldn't return anything.
513         const UInt8* ptr = CFReadStreamGetBuffer(m_readStream.get(), 0, &length);
514         if (!ptr) {
515             length = CFReadStreamRead(m_readStream.get(), localBuffer, sizeof(localBuffer));
516             ptr = localBuffer;
517         }
518
519         m_client->didReceiveData(this, reinterpret_cast<const char*>(ptr), length);
520
521         break;
522     }
523     case kCFStreamEventCanAcceptBytes:
524         ASSERT_NOT_REACHED();
525         break;
526     case kCFStreamEventErrorOccurred: {
527         CFStreamError error = CFReadStreamGetError(m_readStream.get());
528         m_client->didFail(this, SocketStreamError(error.error)); // FIXME: Provide a sensible error.
529         break;
530     }
531     case kCFStreamEventEndEncountered:
532         platformClose();
533         break;
534     }
535 }
536
537 void SocketStreamHandle::writeStreamCallback(CFStreamEventType type)
538 {
539     switch(type) {
540     case kCFStreamEventNone:
541         break;
542     case kCFStreamEventOpenCompleted:
543         break;
544     case kCFStreamEventHasBytesAvailable:
545         ASSERT_NOT_REACHED();
546         break;
547     case kCFStreamEventCanAcceptBytes: {
548         // Possibly, a spurious event from CONNECT handshake.
549         if (!CFWriteStreamCanAcceptBytes(m_writeStream.get()))
550             return;
551
552         if (m_connectingSubstate == WaitingForCredentials)
553             break;
554
555         if (m_connectingSubstate == WaitingForConnect) {
556             m_connectingSubstate = Connected;
557             m_state = Open;
558             m_client->didOpen(this);
559             break;
560         }
561
562         ASSERT(m_state = Open);
563         ASSERT(m_connectingSubstate == Connected);
564
565         sendPendingData();
566         break;
567     }
568     case kCFStreamEventErrorOccurred: {
569         CFStreamError error = CFWriteStreamGetError(m_writeStream.get());
570         m_client->didFail(this, SocketStreamError(error.error)); // FIXME: Provide a sensible error.
571         break;
572     }
573     case kCFStreamEventEndEncountered:
574         // FIXME: Currently, we handle closing in read callback, but these can come independently (e.g. a server can stop listening, but keep sending data).
575         break;
576     }
577 }
578
579 SocketStreamHandle::~SocketStreamHandle()
580 {
581     LOG(Network, "SocketStreamHandle %p dtor", this);
582
583 #ifndef BUILDING_ON_TIGER
584     ASSERT(!m_pacRunLoopSource);
585 #endif
586 }
587
588 int SocketStreamHandle::platformSend(const char* data, int length)
589 {
590     if (!CFWriteStreamCanAcceptBytes(m_writeStream.get()))
591         return 0;
592
593     return CFWriteStreamWrite(m_writeStream.get(), reinterpret_cast<const UInt8*>(data), length);
594 }
595
596 void SocketStreamHandle::platformClose()
597 {
598     LOG(Network, "SocketStreamHandle %p platformClose", this);
599
600 #ifndef BUILDING_ON_TIGER
601     if (m_pacRunLoopSource) 
602         removePACRunLoopSource();
603 #endif
604
605     ASSERT(!m_readStream == !m_writeStream);
606     if (!m_readStream)
607         return;
608
609     CFReadStreamUnscheduleFromRunLoop(m_readStream.get(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
610     CFWriteStreamUnscheduleFromRunLoop(m_writeStream.get(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
611
612     CFReadStreamClose(m_readStream.get());
613     CFWriteStreamClose(m_writeStream.get());
614     
615     m_readStream = 0;
616     m_writeStream = 0;
617
618     m_client->didClose(this);
619 }
620
621 void SocketStreamHandle::receivedCredential(const AuthenticationChallenge&, const Credential&)
622 {
623 }
624
625 void SocketStreamHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&)
626 {
627 }
628
629 void SocketStreamHandle::receivedCancellation(const AuthenticationChallenge&)
630 {
631 }
632
633 }  // namespace WebCore