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