summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp')
-rw-r--r--WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp587
1 files changed, 569 insertions, 18 deletions
diff --git a/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp b/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp
index 6aa33fc..e7e64da 100644
--- a/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp
+++ b/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
* Copyright (C) 2009 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,58 +32,608 @@
#include "config.h"
#include "SocketStreamHandle.h"
-#include "KURL.h"
+#include "Credential.h"
+#include "CredentialStorage.h"
#include "Logging.h"
-#include "NotImplemented.h"
+#include "ProtectionSpace.h"
+#include "SocketStreamError.h"
#include "SocketStreamHandleClient.h"
+#include <wtf/MainThread.h>
+
+#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
+#include <SystemConfiguration/SystemConfiguration.h>
+#endif
+
+#if PLATFORM(WIN)
+#include "LoaderRunLoopCF.h"
+#include <WebKitSystemInterface/WebKitSystemInterface.h>
+#else
+#include "WebCoreSystemInterface.h"
+#endif
+
+#ifdef BUILDING_ON_TIGER
+#define CFN_EXPORT extern
+#endif
namespace WebCore {
SocketStreamHandle::SocketStreamHandle(const KURL& url, SocketStreamHandleClient* client)
: SocketStreamHandleBase(url, client)
+ , m_connectingSubstate(New)
+ , m_connectionType(Unknown)
+ , m_sentStoredCredentials(false)
{
LOG(Network, "SocketStreamHandle %p new client %p", this, m_client);
- notImplemented();
+
+ ASSERT(url.protocolIs("ws") || url.protocolIs("wss"));
+
+ if (!m_url.port())
+ m_url.setPort(shouldUseSSL() ? 443 : 80);
+
+ KURL httpsURL(KURL(), "https://" + m_url.host());
+ m_httpsURL.adoptCF(httpsURL.createCFURL());
+
+ createStreams();
+ ASSERT(!m_readStream == !m_writeStream);
+ if (!m_readStream) // Doing asynchronous PAC file processing, streams will be created later.
+ return;
+
+ scheduleStreams();
+}
+
+void SocketStreamHandle::scheduleStreams()
+{
+ ASSERT(m_readStream);
+ ASSERT(m_writeStream);
+
+ CFStreamClientContext clientContext = { 0, this, 0, 0, copyCFStreamDescription };
+ // FIXME: Pass specific events we're interested in instead of -1.
+ CFReadStreamSetClient(m_readStream.get(), static_cast<CFOptionFlags>(-1), readStreamCallback, &clientContext);
+ CFWriteStreamSetClient(m_writeStream.get(), static_cast<CFOptionFlags>(-1), writeStreamCallback, &clientContext);
+
+#if PLATFORM(WIN)
+ CFReadStreamScheduleWithRunLoop(m_readStream.get(), loaderRunLoop(), kCFRunLoopDefaultMode);
+ CFWriteStreamScheduleWithRunLoop(m_writeStream.get(), loaderRunLoop(), kCFRunLoopDefaultMode);
+#else
+ CFReadStreamScheduleWithRunLoop(m_readStream.get(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
+ CFWriteStreamScheduleWithRunLoop(m_writeStream.get(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
+#endif
+
+ CFReadStreamOpen(m_readStream.get());
+ CFWriteStreamOpen(m_writeStream.get());
+
+#ifndef BUILDING_ON_TIGER
+ if (m_pacRunLoopSource)
+ removePACRunLoopSource();
+#endif
+
+ m_connectingSubstate = WaitingForConnect;
+}
+
+#ifndef BUILDING_ON_TIGER
+CFStringRef SocketStreamHandle::copyPACExecutionDescription(void*)
+{
+ return CFSTR("WebSocket proxy PAC file execution");
+}
+
+struct MainThreadPACCallbackInfo {
+ MainThreadPACCallbackInfo(SocketStreamHandle* handle, CFArrayRef proxyList) : handle(handle), proxyList(proxyList) { }
+ SocketStreamHandle* handle;
+ CFArrayRef proxyList;
+};
+
+void SocketStreamHandle::pacExecutionCallback(void* client, CFArrayRef proxyList, CFErrorRef)
+{
+ SocketStreamHandle* handle = static_cast<SocketStreamHandle*>(client);
+ MainThreadPACCallbackInfo info(handle, proxyList);
+ // If we're already on main thread (e.g. on Mac), callOnMainThreadAndWait() will be just a function call.
+ callOnMainThreadAndWait(pacExecutionCallbackMainThread, &info);
+}
+
+void SocketStreamHandle::pacExecutionCallbackMainThread(void* invocation)
+{
+ MainThreadPACCallbackInfo* info = static_cast<MainThreadPACCallbackInfo*>(invocation);
+ ASSERT(info->handle->m_connectingSubstate == ExecutingPACFile);
+ // This time, the array won't have PAC as a first entry.
+ info->handle->chooseProxyFromArray(info->proxyList);
+ info->handle->createStreams();
+ info->handle->scheduleStreams();
+}
+
+void SocketStreamHandle::executePACFileURL(CFURLRef pacFileURL)
+{
+ // CFNetwork returns an empty proxy array for WebScoket schemes, so use m_httpsURL.
+ CFStreamClientContext clientContext = { 0, this, 0, 0, copyPACExecutionDescription };
+ m_pacRunLoopSource.adoptCF(CFNetworkExecuteProxyAutoConfigurationURL(pacFileURL, m_httpsURL.get(), pacExecutionCallback, &clientContext));
+#if PLATFORM(WIN)
+ CFRunLoopAddSource(loaderRunLoop(), m_pacRunLoopSource.get(), kCFRunLoopDefaultMode);
+#else
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), m_pacRunLoopSource.get(), kCFRunLoopCommonModes);
+#endif
+ m_connectingSubstate = ExecutingPACFile;
+}
+
+void SocketStreamHandle::removePACRunLoopSource()
+{
+ ASSERT(m_pacRunLoopSource);
+
+ CFRunLoopSourceInvalidate(m_pacRunLoopSource.get());
+#if PLATFORM(WIN)
+ CFRunLoopRemoveSource(loaderRunLoop(), m_pacRunLoopSource.get(), kCFRunLoopDefaultMode);
+#else
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m_pacRunLoopSource.get(), kCFRunLoopCommonModes);
+#endif
+ m_pacRunLoopSource = 0;
+}
+
+void SocketStreamHandle::chooseProxy()
+{
+#ifndef BUILDING_ON_LEOPARD
+ RetainPtr<CFDictionaryRef> proxyDictionary(AdoptCF, CFNetworkCopySystemProxySettings());
+#else
+ // We don't need proxy information often, so there is no need to set up a permanent dynamic store session.
+ RetainPtr<CFDictionaryRef> proxyDictionary(AdoptCF, SCDynamicStoreCopyProxies(0));
+#endif
+
+ // SOCKS or HTTPS (AKA CONNECT) proxies are supported.
+ // WebSocket protocol relies on handshake being transferred unchanged, so we need a proxy that will not modify headers.
+ // Since HTTP proxies must add Via headers, they are highly unlikely to work.
+ // Many CONNECT proxies limit connectivity to port 443, so we prefer SOCKS, if configured.
+
+ if (!proxyDictionary) {
+ m_connectionType = Direct;
+ return;
+ }
+
+ // CFNetworkCopyProxiesForURL doesn't know about WebSocket schemes, so pretend to use http.
+ // 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.
+ RetainPtr<CFArrayRef> proxyArray(AdoptCF, CFNetworkCopyProxiesForURL(m_httpsURL.get(), proxyDictionary.get()));
+
+ chooseProxyFromArray(proxyArray.get());
+}
+
+void SocketStreamHandle::chooseProxyFromArray(CFArrayRef proxyArray)
+{
+ if (!proxyArray)
+ m_connectionType = Direct;
+
+ CFIndex proxyArrayCount = CFArrayGetCount(proxyArray);
+
+ // PAC is always the first entry, if present.
+ if (proxyArrayCount) {
+ CFDictionaryRef proxyInfo = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(proxyArray, 0));
+ CFTypeRef proxyType = CFDictionaryGetValue(proxyInfo, kCFProxyTypeKey);
+ if (proxyType && CFGetTypeID(proxyType) == CFStringGetTypeID()) {
+ if (CFEqual(proxyType, kCFProxyTypeAutoConfigurationURL)) {
+ CFTypeRef pacFileURL = CFDictionaryGetValue(proxyInfo, kCFProxyAutoConfigurationURLKey);
+ if (pacFileURL && CFGetTypeID(pacFileURL) == CFURLGetTypeID()) {
+ executePACFileURL(static_cast<CFURLRef>(pacFileURL));
+ return;
+ }
+ }
+ }
+ }
+
+ CFDictionaryRef chosenProxy = 0;
+ for (CFIndex i = 0; i < proxyArrayCount; ++i) {
+ CFDictionaryRef proxyInfo = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(proxyArray, i));
+ CFTypeRef proxyType = CFDictionaryGetValue(proxyInfo, kCFProxyTypeKey);
+ if (proxyType && CFGetTypeID(proxyType) == CFStringGetTypeID()) {
+ if (CFEqual(proxyType, kCFProxyTypeSOCKS)) {
+ m_connectionType = SOCKSProxy;
+ chosenProxy = proxyInfo;
+ break;
+ }
+ if (CFEqual(proxyType, kCFProxyTypeHTTPS)) {
+ m_connectionType = CONNECTProxy;
+ chosenProxy = proxyInfo;
+ // Keep looking for proxies, as a SOCKS one is preferable.
+ }
+ }
+ }
+
+ if (chosenProxy) {
+ ASSERT(m_connectionType != Unknown);
+ ASSERT(m_connectionType != Direct);
+
+ CFTypeRef proxyHost = CFDictionaryGetValue(chosenProxy, kCFProxyHostNameKey);
+ CFTypeRef proxyPort = CFDictionaryGetValue(chosenProxy, kCFProxyPortNumberKey);
+
+ if (proxyHost && CFGetTypeID(proxyHost) == CFStringGetTypeID() && proxyPort && CFGetTypeID(proxyPort) == CFNumberGetTypeID()) {
+ m_proxyHost = static_cast<CFStringRef>(proxyHost);
+ m_proxyPort = static_cast<CFNumberRef>(proxyPort);
+ return;
+ }
+ }
+
+ m_connectionType = Direct;
+}
+
+#else // BUILDING_ON_TIGER
+
+void SocketStreamHandle::chooseProxy()
+{
+ // We don't need proxy information often, so there is no need to set up a permanent dynamic store session.
+ RetainPtr<CFDictionaryRef> proxyDictionary(AdoptCF, SCDynamicStoreCopyProxies(0));
+
+ // SOCKS or HTTPS (AKA CONNECT) proxies are supported.
+ // WebSocket protocol relies on handshake being transferred unchanged, so we need a proxy that will not modify headers.
+ // Since HTTP proxies must add Via headers, they are highly unlikely to work.
+ // Many CONNECT proxies limit connectivity to port 443, so we prefer SOCKS, if configured.
+
+ if (!proxyDictionary) {
+ m_connectionType = Direct;
+ return;
+ }
+
+ // FIXME: check proxy bypass list and ExcludeSimpleHostnames.
+ // FIXME: Support PAC files.
+
+ CFTypeRef socksEnableCF = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesSOCKSEnable);
+ int socksEnable;
+ if (socksEnableCF && CFGetTypeID(socksEnableCF) == CFNumberGetTypeID() && CFNumberGetValue(static_cast<CFNumberRef>(socksEnableCF), kCFNumberIntType, &socksEnable) && socksEnable) {
+ CFTypeRef proxyHost = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesSOCKSProxy);
+ CFTypeRef proxyPort = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesSOCKSPort);
+ if (proxyHost && CFGetTypeID(proxyHost) == CFStringGetTypeID() && proxyPort && CFGetTypeID(proxyPort) == CFNumberGetTypeID()) {
+ m_proxyHost = static_cast<CFStringRef>(proxyHost);
+ m_proxyPort = static_cast<CFNumberRef>(proxyPort);
+ m_connectionType = SOCKSProxy;
+ return;
+ }
+ }
+
+ CFTypeRef httpsEnableCF = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesHTTPSEnable);
+ int httpsEnable;
+ if (httpsEnableCF && CFGetTypeID(httpsEnableCF) == CFNumberGetTypeID() && CFNumberGetValue(static_cast<CFNumberRef>(httpsEnableCF), kCFNumberIntType, &httpsEnable) && httpsEnable) {
+ CFTypeRef proxyHost = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesHTTPSProxy);
+ CFTypeRef proxyPort = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesHTTPSPort);
+
+ if (proxyHost && CFGetTypeID(proxyHost) == CFStringGetTypeID() && proxyPort && CFGetTypeID(proxyPort) == CFNumberGetTypeID()) {
+ m_proxyHost = static_cast<CFStringRef>(proxyHost);
+ m_proxyPort = static_cast<CFNumberRef>(proxyPort);
+ m_connectionType = CONNECTProxy;
+ return;
+ }
+ }
+
+ m_connectionType = Direct;
+}
+#endif // BUILDING_ON_TIGER
+
+void SocketStreamHandle::createStreams()
+{
+ if (m_connectionType == Unknown)
+ chooseProxy();
+
+ // If it's still unknown, then we're resolving a PAC file asynchronously.
+ if (m_connectionType == Unknown)
+ return;
+
+ RetainPtr<CFStringRef> host(AdoptCF, m_url.host().createCFString());
+
+ // Creating streams to final destination, not to proxy.
+ CFReadStreamRef readStream = 0;
+ CFWriteStreamRef writeStream = 0;
+ CFStreamCreatePairWithSocketToHost(0, host.get(), m_url.port(), &readStream, &writeStream);
+
+ m_readStream.adoptCF(readStream);
+ m_writeStream.adoptCF(writeStream);
+
+ switch (m_connectionType) {
+ case Unknown:
+ ASSERT_NOT_REACHED();
+ break;
+ case Direct:
+ break;
+ case SOCKSProxy: {
+ // FIXME: SOCKS5 doesn't do challenge-response, should we try to apply credentials from Keychain right away?
+ // But SOCKS5 credentials don't work at the time of this writing anyway, see <rdar://6776698>.
+ const void* proxyKeys[] = { kCFStreamPropertySOCKSProxyHost, kCFStreamPropertySOCKSProxyPort };
+ const void* proxyValues[] = { m_proxyHost.get(), m_proxyPort.get() };
+ RetainPtr<CFDictionaryRef> connectDictionary(AdoptCF, CFDictionaryCreate(0, proxyKeys, proxyValues, sizeof(proxyKeys) / sizeof(*proxyKeys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+ CFReadStreamSetProperty(m_readStream.get(), kCFStreamPropertySOCKSProxy, connectDictionary.get());
+ break;
+ }
+ case CONNECTProxy:
+ wkSetCONNECTProxyForStream(m_readStream.get(), m_proxyHost.get(), m_proxyPort.get());
+ break;
+ }
+
+ if (shouldUseSSL()) {
+ const void* keys[] = { kCFStreamSSLPeerName, kCFStreamSSLLevel };
+ const void* values[] = { host.get(), kCFStreamSocketSecurityLevelNegotiatedSSL };
+ RetainPtr<CFDictionaryRef> settings(AdoptCF, CFDictionaryCreate(0, keys, values, sizeof(keys) / sizeof(*keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+ CFReadStreamSetProperty(m_readStream.get(), kCFStreamPropertySSLSettings, settings.get());
+ CFWriteStreamSetProperty(m_writeStream.get(), kCFStreamPropertySSLSettings, settings.get());
+ }
+}
+
+static bool getStoredCONNECTProxyCredentials(const ProtectionSpace& protectionSpace, String& login, String& password)
+{
+ // Try system credential storage first, matching HTTP behavior (CFNetwork only asks the client for password if it couldn't find it in Keychain).
+ Credential storedCredential = CredentialStorage::getFromPersistentStorage(protectionSpace);
+ if (storedCredential.isEmpty())
+ storedCredential = CredentialStorage::get(protectionSpace);
+
+ if (storedCredential.isEmpty())
+ return false;
+
+ login = storedCredential.user();
+ password = storedCredential.password();
+
+ return true;
+}
+
+static ProtectionSpaceAuthenticationScheme authenticationSchemeFromAuthenticationMethod(CFStringRef method)
+{
+ if (CFEqual(method, kCFHTTPAuthenticationSchemeBasic))
+ return ProtectionSpaceAuthenticationSchemeHTTPBasic;
+ if (CFEqual(method, kCFHTTPAuthenticationSchemeDigest))
+ return ProtectionSpaceAuthenticationSchemeHTTPDigest;
+#ifndef BUILDING_ON_TIGER
+ if (CFEqual(method, kCFHTTPAuthenticationSchemeNTLM))
+ return ProtectionSpaceAuthenticationSchemeNTLM;
+ if (CFEqual(method, kCFHTTPAuthenticationSchemeNegotiate))
+ return ProtectionSpaceAuthenticationSchemeNegotiate;
+#endif
+ ASSERT_NOT_REACHED();
+ return ProtectionSpaceAuthenticationSchemeDefault;
+}
+
+void SocketStreamHandle::addCONNECTCredentials(CFHTTPMessageRef proxyResponse)
+{
+ RetainPtr<CFHTTPAuthenticationRef> authentication(AdoptCF, CFHTTPAuthenticationCreateFromResponse(0, proxyResponse));
+
+ if (!CFHTTPAuthenticationRequiresUserNameAndPassword(authentication.get())) {
+ // That's all we can offer...
+ m_client->didFail(this, SocketStreamError()); // FIXME: Provide a sensible error.
+ return;
+ }
+
+ int port = 0;
+ CFNumberGetValue(m_proxyPort.get(), kCFNumberIntType, &port);
+ RetainPtr<CFStringRef> methodCF(AdoptCF, CFHTTPAuthenticationCopyMethod(authentication.get()));
+ RetainPtr<CFStringRef> realmCF(AdoptCF, CFHTTPAuthenticationCopyRealm(authentication.get()));
+ ProtectionSpace protectionSpace(String(m_proxyHost.get()), port, ProtectionSpaceProxyHTTPS, String(realmCF.get()), authenticationSchemeFromAuthenticationMethod(methodCF.get()));
+ String login;
+ String password;
+ if (!m_sentStoredCredentials && getStoredCONNECTProxyCredentials(protectionSpace, login, password)) {
+ // Try to apply stored credentials, if we haven't tried those already.
+ RetainPtr<CFStringRef> loginCF(AdoptCF, login.createCFString());
+ RetainPtr<CFStringRef> passwordCF(AdoptCF, password.createCFString());
+ // Creating a temporary request to make CFNetwork apply credentials to it. Unfortunately, this cannot work with NTLM authentication.
+ RetainPtr<CFHTTPMessageRef> dummyRequest(AdoptCF, CFHTTPMessageCreateRequest(0, CFSTR("GET"), m_httpsURL.get(), kCFHTTPVersion1_1));
+
+ Boolean appliedCredentials = CFHTTPMessageApplyCredentials(dummyRequest.get(), authentication.get(), loginCF.get(), passwordCF.get(), 0);
+ ASSERT_UNUSED(appliedCredentials, appliedCredentials);
+
+ RetainPtr<CFStringRef> proxyAuthorizationString(AdoptCF, CFHTTPMessageCopyHeaderFieldValue(dummyRequest.get(), CFSTR("Proxy-Authorization")));
+
+ if (!proxyAuthorizationString) {
+ // Fails e.g. for NTLM auth.
+ m_client->didFail(this, SocketStreamError()); // FIXME: Provide a sensible error.
+ return;
+ }
+
+ // Setting the authorization results in a new connection attempt.
+ wkSetCONNECTProxyAuthorizationForStream(m_readStream.get(), proxyAuthorizationString.get());
+ m_sentStoredCredentials = true;
+ return;
+ }
+
+ // FIXME: Ask the client if credentials could not be found.
+
+ m_client->didFail(this, SocketStreamError()); // FIXME: Provide a sensible error.
+}
+
+CFStringRef SocketStreamHandle::copyCFStreamDescription(void* info)
+{
+ SocketStreamHandle* handle = static_cast<SocketStreamHandle*>(info);
+ return ("WebKit socket stream, " + handle->m_url.string()).createCFString();
+}
+
+struct MainThreadEventCallbackInfo {
+ MainThreadEventCallbackInfo(CFStreamEventType type, SocketStreamHandle* handle) : type(type), handle(handle) { }
+ CFStreamEventType type;
+ SocketStreamHandle* handle;
+};
+
+void SocketStreamHandle::readStreamCallback(CFReadStreamRef stream, CFStreamEventType type, void* clientCallBackInfo)
+{
+ SocketStreamHandle* handle = static_cast<SocketStreamHandle*>(clientCallBackInfo);
+ ASSERT_UNUSED(stream, stream == handle->m_readStream.get());
+#if PLATFORM(WIN)
+ MainThreadEventCallbackInfo info(type, handle);
+ callOnMainThreadAndWait(readStreamCallbackMainThread, &info);
+#else
+ ASSERT(isMainThread());
+ handle->readStreamCallback(type);
+#endif
+}
+
+void SocketStreamHandle::writeStreamCallback(CFWriteStreamRef stream, CFStreamEventType type, void* clientCallBackInfo)
+{
+ SocketStreamHandle* handle = static_cast<SocketStreamHandle*>(clientCallBackInfo);
+ ASSERT_UNUSED(stream, stream == handle->m_writeStream.get());
+#if PLATFORM(WIN)
+ MainThreadEventCallbackInfo info(type, handle);
+ callOnMainThreadAndWait(writeStreamCallbackMainThread, &info);
+#else
+ ASSERT(isMainThread());
+ handle->writeStreamCallback(type);
+#endif
+}
+
+#if PLATFORM(WIN)
+void SocketStreamHandle::readStreamCallbackMainThread(void* invocation)
+{
+ MainThreadEventCallbackInfo* info = static_cast<MainThreadEventCallbackInfo*>(invocation);
+ info->handle->readStreamCallback(info->type);
+}
+
+void SocketStreamHandle::writeStreamCallbackMainThread(void* invocation)
+{
+ MainThreadEventCallbackInfo* info = static_cast<MainThreadEventCallbackInfo*>(invocation);
+ info->handle->writeStreamCallback(info->type);
+}
+#endif // PLATFORM(WIN)
+
+void SocketStreamHandle::readStreamCallback(CFStreamEventType type)
+{
+ switch(type) {
+ case kCFStreamEventNone:
+ break;
+ case kCFStreamEventOpenCompleted:
+ break;
+ case kCFStreamEventHasBytesAvailable: {
+ if (m_connectingSubstate == WaitingForConnect) {
+ if (m_connectionType == CONNECTProxy) {
+ RetainPtr<CFHTTPMessageRef> proxyResponse(AdoptCF, wkCopyCONNECTProxyResponse(m_readStream.get(), m_httpsURL.get()));
+ if (proxyResponse && (407 == CFHTTPMessageGetResponseStatusCode(proxyResponse.get()))) {
+ addCONNECTCredentials(proxyResponse.get());
+ return;
+ }
+ }
+ } else if (m_connectingSubstate == WaitingForCredentials)
+ break;
+
+ if (m_connectingSubstate == WaitingForConnect) {
+ m_connectingSubstate = Connected;
+ m_state = Open;
+
+ RefPtr<SocketStreamHandle> protect(this); // The client can close the handle, potentially removing the last reference.
+ m_client->didOpen(this);
+ if (m_state == Closed)
+ break;
+ // Fall through.
+ } else if (m_state == Closed)
+ break;
+
+ ASSERT(m_state == Open);
+ ASSERT(m_connectingSubstate == Connected);
+
+ CFIndex length;
+ UInt8 localBuffer[1024]; // Used if CFReadStreamGetBuffer couldn't return anything.
+ const UInt8* ptr = CFReadStreamGetBuffer(m_readStream.get(), 0, &length);
+ if (!ptr) {
+ length = CFReadStreamRead(m_readStream.get(), localBuffer, sizeof(localBuffer));
+ ptr = localBuffer;
+ }
+
+ m_client->didReceiveData(this, reinterpret_cast<const char*>(ptr), length);
+
+ break;
+ }
+ case kCFStreamEventCanAcceptBytes:
+ ASSERT_NOT_REACHED();
+ break;
+ case kCFStreamEventErrorOccurred: {
+ CFStreamError error = CFReadStreamGetError(m_readStream.get());
+ m_client->didFail(this, SocketStreamError(error.error)); // FIXME: Provide a sensible error.
+ break;
+ }
+ case kCFStreamEventEndEncountered:
+ platformClose();
+ break;
+ }
+}
+
+void SocketStreamHandle::writeStreamCallback(CFStreamEventType type)
+{
+ switch(type) {
+ case kCFStreamEventNone:
+ break;
+ case kCFStreamEventOpenCompleted:
+ break;
+ case kCFStreamEventHasBytesAvailable:
+ ASSERT_NOT_REACHED();
+ break;
+ case kCFStreamEventCanAcceptBytes: {
+ // Possibly, a spurious event from CONNECT handshake.
+ if (!CFWriteStreamCanAcceptBytes(m_writeStream.get()))
+ return;
+
+ if (m_connectingSubstate == WaitingForCredentials)
+ break;
+
+ if (m_connectingSubstate == WaitingForConnect) {
+ m_connectingSubstate = Connected;
+ m_state = Open;
+
+ RefPtr<SocketStreamHandle> protect(this); // The client can close the handle, potentially removing the last reference.
+ m_client->didOpen(this);
+ break;
+ }
+
+ ASSERT(m_state = Open);
+ ASSERT(m_connectingSubstate == Connected);
+
+ sendPendingData();
+ break;
+ }
+ case kCFStreamEventErrorOccurred: {
+ CFStreamError error = CFWriteStreamGetError(m_writeStream.get());
+ m_client->didFail(this, SocketStreamError(error.error)); // FIXME: Provide a sensible error.
+ break;
+ }
+ case kCFStreamEventEndEncountered:
+ // FIXME: Currently, we handle closing in read callback, but these can come independently (e.g. a server can stop listening, but keep sending data).
+ break;
+ }
}
SocketStreamHandle::~SocketStreamHandle()
{
- LOG(Network, "SocketStreamHandle %p delete", this);
- setClient(0);
- notImplemented();
+ LOG(Network, "SocketStreamHandle %p dtor", this);
+
+#ifndef BUILDING_ON_TIGER
+ ASSERT(!m_pacRunLoopSource);
+#endif
}
-int SocketStreamHandle::platformSend(const char*, int)
+int SocketStreamHandle::platformSend(const char* data, int length)
{
- LOG(Network, "SocketStreamHandle %p platformSend", this);
- notImplemented();
- return 0;
+ if (!CFWriteStreamCanAcceptBytes(m_writeStream.get()))
+ return 0;
+
+ return CFWriteStreamWrite(m_writeStream.get(), reinterpret_cast<const UInt8*>(data), length);
}
void SocketStreamHandle::platformClose()
{
LOG(Network, "SocketStreamHandle %p platformClose", this);
- notImplemented();
-}
-void SocketStreamHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge&)
-{
- notImplemented();
+#ifndef BUILDING_ON_TIGER
+ if (m_pacRunLoopSource)
+ removePACRunLoopSource();
+#endif
+
+ ASSERT(!m_readStream == !m_writeStream);
+ if (!m_readStream)
+ return;
+
+ CFReadStreamUnscheduleFromRunLoop(m_readStream.get(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
+ CFWriteStreamUnscheduleFromRunLoop(m_writeStream.get(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
+
+ CFReadStreamClose(m_readStream.get());
+ CFWriteStreamClose(m_writeStream.get());
+
+ m_readStream = 0;
+ m_writeStream = 0;
+
+ m_client->didClose(this);
}
void SocketStreamHandle::receivedCredential(const AuthenticationChallenge&, const Credential&)
{
- notImplemented();
}
void SocketStreamHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&)
{
- notImplemented();
}
void SocketStreamHandle::receivedCancellation(const AuthenticationChallenge&)
{
- notImplemented();
}
} // namespace WebCore