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
SocketStreamHandle(const KURL & url,SocketStreamHandleClient * client)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
scheduleStreams()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
copyPACExecutionDescription(void *)114 CFStringRef SocketStreamHandle::copyPACExecutionDescription(void*)
115 {
116 return CFSTR("WebSocket proxy PAC file execution");
117 }
118
119 struct MainThreadPACCallbackInfo {
MainThreadPACCallbackInfoWebCore::MainThreadPACCallbackInfo120 MainThreadPACCallbackInfo(SocketStreamHandle* handle, CFArrayRef proxyList) : handle(handle), proxyList(proxyList) { }
121 SocketStreamHandle* handle;
122 CFArrayRef proxyList;
123 };
124
pacExecutionCallback(void * client,CFArrayRef proxyList,CFErrorRef)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
pacExecutionCallbackMainThread(void * invocation)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
executePACFileURL(CFURLRef pacFileURL)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
removePACRunLoopSource()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
chooseProxy()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
chooseProxyFromArray(CFArrayRef proxyArray)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
chooseProxy()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
createStreams()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
getStoredCONNECTProxyCredentials(const ProtectionSpace & protectionSpace,String & login,String & password)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
authenticationSchemeFromAuthenticationMethod(CFStringRef method)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
addCONNECTCredentials(CFHTTPMessageRef proxyResponse)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
copyCFStreamDescription(void * info)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 {
MainThreadEventCallbackInfoWebCore::MainThreadEventCallbackInfo436 MainThreadEventCallbackInfo(CFStreamEventType type, SocketStreamHandle* handle) : type(type), handle(handle) { }
437 CFStreamEventType type;
438 SocketStreamHandle* handle;
439 };
440
readStreamCallback(CFReadStreamRef stream,CFStreamEventType type,void * clientCallBackInfo)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
writeStreamCallback(CFWriteStreamRef stream,CFStreamEventType type,void * clientCallBackInfo)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)
readStreamCallbackMainThread(void * invocation)468 void SocketStreamHandle::readStreamCallbackMainThread(void* invocation)
469 {
470 MainThreadEventCallbackInfo* info = static_cast<MainThreadEventCallbackInfo*>(invocation);
471 info->handle->readStreamCallback(info->type);
472 }
473
writeStreamCallbackMainThread(void * invocation)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
readStreamCallback(CFStreamEventType type)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
writeStreamCallback(CFStreamEventType type)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
~SocketStreamHandle()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
platformSend(const char * data,int length)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
platformClose()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
receivedCredential(const AuthenticationChallenge &,const Credential &)627 void SocketStreamHandle::receivedCredential(const AuthenticationChallenge&, const Credential&)
628 {
629 }
630
receivedRequestToContinueWithoutCredential(const AuthenticationChallenge &)631 void SocketStreamHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&)
632 {
633 }
634
receivedCancellation(const AuthenticationChallenge &)635 void SocketStreamHandle::receivedCancellation(const AuthenticationChallenge&)
636 {
637 }
638
639 } // namespace WebCore
640