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