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