initial import
[vuplus_webkit] / Source / WebCore / platform / network / cf / ResourceHandleCFNet.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27
28 #include "ResourceHandleInternal.h"
29
30 #include "AuthenticationCF.h"
31 #include "AuthenticationChallenge.h"
32 #include "Base64.h"
33 #include "CookieStorageCFNet.h"
34 #include "CredentialStorage.h"
35 #include "CachedResourceLoader.h"
36 #include "FormDataStreamCFNet.h"
37 #include "Frame.h"
38 #include "FrameLoader.h"
39 #include "LoaderRunLoopCF.h"
40 #include "Logging.h"
41 #include "MIMETypeRegistry.h"
42 #include "ResourceError.h"
43 #include "ResourceHandleClient.h"
44 #include "ResourceResponse.h"
45 #include "SharedBuffer.h"
46 #include <CFNetwork/CFNetwork.h>
47 #include <sys/stat.h>
48 #include <sys/types.h>
49 #include <wtf/HashMap.h>
50 #include <wtf/Threading.h>
51 #include <wtf/text/CString.h>
52
53 #if PLATFORM(MAC)
54 #include "WebCoreSystemInterface.h"
55 #if USE(CFNETWORK)
56 #include "WebCoreURLResponse.h"
57 #include <CFNetwork/CFURLConnectionPriv.h>
58 #include <CFNetwork/CFURLRequestPriv.h>
59 #endif
60 #endif
61
62 #if PLATFORM(WIN)
63 #include <WebKitSystemInterface/WebKitSystemInterface.h>
64 #include <process.h>
65
66 // FIXME: Remove this declaration once it's in WebKitSupportLibrary.
67 extern "C" {
68 __declspec(dllimport) CFURLConnectionRef CFURLConnectionCreateWithProperties(
69   CFAllocatorRef           alloc,
70   CFURLRequestRef          request,
71   CFURLConnectionClient *  client,
72   CFDictionaryRef properties);
73 }
74 #endif
75
76 namespace WebCore {
77
78 #if USE(CFNETWORK)
79
80 class WebCoreSynchronousLoaderClient : public ResourceHandleClient {
81 public:
82     static PassOwnPtr<WebCoreSynchronousLoaderClient> create(ResourceResponse& response, ResourceError& error)
83     {
84         return adoptPtr(new WebCoreSynchronousLoaderClient(response, error));
85     }
86
87     void setAllowStoredCredentials(bool allow) { m_allowStoredCredentials = allow; }
88     bool isDone() { return m_isDone; }
89
90     CFMutableDataRef data() { return m_data.get(); }
91
92 private:
93     WebCoreSynchronousLoaderClient(ResourceResponse& response, ResourceError& error)
94         : m_allowStoredCredentials(false)
95         , m_response(response)
96         , m_error(error)
97         , m_isDone(false)
98     {
99     }
100
101     virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse& /*redirectResponse*/);
102     virtual bool shouldUseCredentialStorage(ResourceHandle*);
103     virtual void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge&);
104     virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&);
105     virtual void didReceiveData(ResourceHandle*, const char*, int, int /*encodedDataLength*/);
106     virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/);
107     virtual void didFail(ResourceHandle*, const ResourceError&);
108 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
109     virtual bool canAuthenticateAgainstProtectionSpace(ResourceHandle*, const ProtectionSpace&);
110 #endif
111
112     bool m_allowStoredCredentials;
113     ResourceResponse& m_response;
114     RetainPtr<CFMutableDataRef> m_data;
115     ResourceError& m_error;
116     bool m_isDone;
117 };
118
119 static HashSet<String>& allowsAnyHTTPSCertificateHosts()
120 {
121     DEFINE_STATIC_LOCAL(HashSet<String>, hosts, ());
122     return hosts;
123 }
124
125 static HashMap<String, RetainPtr<CFDataRef> >& clientCerts()
126 {
127     typedef HashMap<String, RetainPtr<CFDataRef> > CertsMap;
128     DEFINE_STATIC_LOCAL(CertsMap, certs, ());
129     return certs;
130 }
131
132 static void setDefaultMIMEType(CFURLResponseRef response)
133 {
134     static CFStringRef defaultMIMETypeString = defaultMIMEType().createCFString();
135     
136     CFURLResponseSetMIMEType(response, defaultMIMETypeString);
137 }
138
139 static void applyBasicAuthorizationHeader(ResourceRequest& request, const Credential& credential)
140 {
141     String authenticationHeader = "Basic " + base64Encode(String(credential.user() + ":" + credential.password()).utf8());
142     request.clearHTTPAuthorization(); // FIXME: Should addHTTPHeaderField be smart enough to not build comma-separated lists in headers like Authorization?
143     request.addHTTPHeaderField("Authorization", authenticationHeader);
144 }
145
146 static CFURLRequestRef willSendRequest(CFURLConnectionRef conn, CFURLRequestRef cfRequest, CFURLResponseRef cfRedirectResponse, const void* clientInfo)
147 {
148 #if LOG_DISABLED
149     UNUSED_PARAM(conn);
150 #endif
151     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
152
153     if (!cfRedirectResponse) {
154         CFRetain(cfRequest);
155         return cfRequest;
156     }
157
158     LOG(Network, "CFNet - willSendRequest(conn=%p, handle=%p) (%s)", conn, handle, handle->firstRequest().url().string().utf8().data());
159
160     ResourceRequest request;
161     if (cfRedirectResponse) {
162         CFHTTPMessageRef httpMessage = CFURLResponseGetHTTPResponse(cfRedirectResponse);
163         if (httpMessage && CFHTTPMessageGetResponseStatusCode(httpMessage) == 307) {
164             RetainPtr<CFStringRef> lastHTTPMethod(AdoptCF, handle->lastHTTPMethod().createCFString());
165             RetainPtr<CFStringRef> newMethod(AdoptCF, CFURLRequestCopyHTTPRequestMethod(cfRequest));
166             if (CFStringCompareWithOptions(lastHTTPMethod.get(), newMethod.get(), CFRangeMake(0, CFStringGetLength(lastHTTPMethod.get())), kCFCompareCaseInsensitive)) {
167                 RetainPtr<CFMutableURLRequestRef> mutableRequest(AdoptCF, CFURLRequestCreateMutableCopy(0, cfRequest));
168 #if USE(CFURLSTORAGESESSIONS)
169                 wkSetRequestStorageSession(ResourceHandle::currentStorageSession(), mutableRequest.get());
170 #endif
171                 CFURLRequestSetHTTPRequestMethod(mutableRequest.get(), lastHTTPMethod.get());
172
173                 FormData* body = handle->firstRequest().httpBody();
174                 if (!equalIgnoringCase(handle->firstRequest().httpMethod(), "GET") && body && !body->isEmpty())
175                     WebCore::setHTTPBody(mutableRequest.get(), body);
176
177                 String originalContentType = handle->firstRequest().httpContentType();
178                 RetainPtr<CFStringRef> originalContentTypeCF(AdoptCF, originalContentType.createCFString());
179                 if (!originalContentType.isEmpty())
180                     CFURLRequestSetHTTPHeaderFieldValue(mutableRequest.get(), CFSTR("Content-Type"), originalContentTypeCF.get());
181
182                 request = mutableRequest.get();
183             }
184         }
185     }
186     if (request.isNull())
187         request = cfRequest;
188
189     // Should not set Referer after a redirect from a secure resource to non-secure one.
190     if (!request.url().protocolIs("https") && protocolIs(request.httpReferrer(), "https"))
191         request.clearHTTPReferrer();
192
193     handle->willSendRequest(request, cfRedirectResponse);
194
195     if (request.isNull())
196         return 0;
197
198     cfRequest = request.cfURLRequest();
199
200     CFRetain(cfRequest);
201     return cfRequest;
202 }
203
204 static void didReceiveResponse(CFURLConnectionRef conn, CFURLResponseRef cfResponse, const void* clientInfo)
205 {
206 #if LOG_DISABLED
207     UNUSED_PARAM(conn);
208 #endif
209     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
210
211     LOG(Network, "CFNet - didReceiveResponse(conn=%p, handle=%p) (%s)", conn, handle, handle->firstRequest().url().string().utf8().data());
212
213     if (!handle->client())
214         return;
215
216 #if PLATFORM(MAC)
217     // Avoid MIME type sniffing if the response comes back as 304 Not Modified.
218     CFHTTPMessageRef msg = wkGetCFURLResponseHTTPResponse(cfResponse);
219     int statusCode = msg ? CFHTTPMessageGetResponseStatusCode(msg) : 0;
220
221     if (statusCode != 304)
222         adjustMIMETypeIfNecessary(cfResponse);
223
224     if (_CFURLRequestCopyProtocolPropertyForKey(handle->firstRequest().cfURLRequest(), CFSTR("ForceHTMLMIMEType")))
225         wkSetCFURLResponseMIMEType(cfResponse, CFSTR("text/html"));
226 #else
227     if (!CFURLResponseGetMIMEType(cfResponse)) {
228         // We should never be applying the default MIMEType if we told the networking layer to do content sniffing for handle.
229         ASSERT(!handle->shouldContentSniff());
230         setDefaultMIMEType(cfResponse);
231     }
232 #endif
233     
234     handle->client()->didReceiveResponse(handle, cfResponse);
235 }
236
237 #if HAVE(NETWORK_CFDATA_ARRAY_CALLBACK)
238 static void didReceiveDataArray(CFURLConnectionRef conn, CFArrayRef dataArray, const void* clientInfo)
239 {
240 #if LOG_DISABLED
241     UNUSED_PARAM(conn);
242 #endif
243     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
244     if (!handle->client())
245         return;
246
247     LOG(Network, "CFNet - didReceiveDataArray(conn=%p, handle=%p, arrayLength=%ld) (%s)", conn, handle, CFArrayGetCount(dataArray), handle->firstRequest().url().string().utf8().data());
248
249     handle->handleDataArray(dataArray);
250 }
251 #endif
252
253 static void didReceiveData(CFURLConnectionRef conn, CFDataRef data, CFIndex originalLength, const void* clientInfo)
254 {
255 #if LOG_DISABLED
256     UNUSED_PARAM(conn);
257 #endif
258     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
259     const UInt8* bytes = CFDataGetBytePtr(data);
260     CFIndex length = CFDataGetLength(data);
261
262     LOG(Network, "CFNet - didReceiveData(conn=%p, handle=%p, bytes=%ld) (%s)", conn, handle, length, handle->firstRequest().url().string().utf8().data());
263
264     if (handle->client())
265         handle->client()->didReceiveData(handle, (const char*)bytes, length, originalLength);
266 }
267
268 static void didSendBodyData(CFURLConnectionRef, CFIndex, CFIndex totalBytesWritten, CFIndex totalBytesExpectedToWrite, const void *clientInfo)
269 {
270     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
271     if (!handle || !handle->client())
272         return;
273     handle->client()->didSendData(handle, totalBytesWritten, totalBytesExpectedToWrite);
274 }
275
276 static Boolean shouldUseCredentialStorageCallback(CFURLConnectionRef conn, const void* clientInfo)
277 {
278 #if LOG_DISABLED
279     UNUSED_PARAM(conn);
280 #endif
281     ResourceHandle* handle = const_cast<ResourceHandle*>(static_cast<const ResourceHandle*>(clientInfo));
282
283     LOG(Network, "CFNet - shouldUseCredentialStorage(conn=%p, handle=%p) (%s)", conn, handle, handle->firstRequest().url().string().utf8().data());
284
285     if (!handle)
286         return false;
287
288     return handle->shouldUseCredentialStorage();
289 }
290
291 static void didFinishLoading(CFURLConnectionRef conn, const void* clientInfo)
292 {
293 #if LOG_DISABLED
294     UNUSED_PARAM(conn);
295 #endif
296     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
297
298     LOG(Network, "CFNet - didFinishLoading(conn=%p, handle=%p) (%s)", conn, handle, handle->firstRequest().url().string().utf8().data());
299
300     if (handle->client())
301         handle->client()->didFinishLoading(handle, 0);
302 }
303
304 static void didFail(CFURLConnectionRef conn, CFErrorRef error, const void* clientInfo)
305 {
306 #if LOG_DISABLED
307     UNUSED_PARAM(conn);
308 #endif
309     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
310
311     LOG(Network, "CFNet - didFail(conn=%p, handle=%p, error = %p) (%s)", conn, handle, error, handle->firstRequest().url().string().utf8().data());
312
313     if (handle->client())
314         handle->client()->didFail(handle, ResourceError(error));
315 }
316
317 static CFCachedURLResponseRef willCacheResponse(CFURLConnectionRef, CFCachedURLResponseRef cachedResponse, const void* clientInfo)
318 {
319     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
320
321 #if PLATFORM(WIN)
322     if (handle->client() && !handle->client()->shouldCacheResponse(handle, cachedResponse))
323         return 0;
324 #else
325     CFCachedURLResponseRef newResponse = handle->client()->willCacheResponse(handle, cachedResponse);
326     if (newResponse != cachedResponse)
327         return newResponse;
328 #endif
329
330     CacheStoragePolicy policy = static_cast<CacheStoragePolicy>(CFCachedURLResponseGetStoragePolicy(cachedResponse));
331
332     if (handle->client())
333         handle->client()->willCacheResponse(handle, policy);
334
335     if (static_cast<CFURLCacheStoragePolicy>(policy) != CFCachedURLResponseGetStoragePolicy(cachedResponse)) {
336         cachedResponse = CFCachedURLResponseCreateWithUserInfo(kCFAllocatorDefault, 
337                                                                CFCachedURLResponseGetWrappedResponse(cachedResponse),
338                                                                CFCachedURLResponseGetReceiverData(cachedResponse),
339                                                                CFCachedURLResponseGetUserInfo(cachedResponse), 
340                                                                static_cast<CFURLCacheStoragePolicy>(policy));
341     } else
342         CFRetain(cachedResponse);
343
344     return cachedResponse;
345 }
346
347 static void didReceiveChallenge(CFURLConnectionRef conn, CFURLAuthChallengeRef challenge, const void* clientInfo)
348 {
349 #if LOG_DISABLED
350     UNUSED_PARAM(conn);
351 #endif
352     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
353     ASSERT(handle);
354     LOG(Network, "CFNet - didReceiveChallenge(conn=%p, handle=%p (%s)", conn, handle, handle->firstRequest().url().string().utf8().data());
355
356     handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(challenge, handle));
357 }
358
359 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
360 static Boolean canRespondToProtectionSpace(CFURLConnectionRef conn, CFURLProtectionSpaceRef protectionSpace, const void* clientInfo)
361 {
362 #if LOG_DISABLED
363     UNUSED_PARAM(conn);
364 #endif
365     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
366     ASSERT(handle);
367
368     LOG(Network, "CFNet - canRespondToProtectionSpace(conn=%p, handle=%p (%s)", conn, handle, handle->firstRequest().url().string().utf8().data());
369
370     return handle->canAuthenticateAgainstProtectionSpace(core(protectionSpace));
371 }
372 #endif
373
374 ResourceHandleInternal::~ResourceHandleInternal()
375 {
376     if (m_connection) {
377         LOG(Network, "CFNet - Cancelling connection %p (%s)", m_connection.get(), m_firstRequest.url().string().utf8().data());
378         CFURLConnectionCancel(m_connection.get());
379     }
380 }
381
382 ResourceHandle::~ResourceHandle()
383 {
384     LOG(Network, "CFNet - Destroying job %p (%s)", this, d->m_firstRequest.url().string().utf8().data());
385 }
386
387 static CFURLRequestRef makeFinalRequest(const ResourceRequest& request, bool shouldContentSniff)
388 {
389     CFMutableURLRequestRef newRequest = CFURLRequestCreateMutableCopy(kCFAllocatorDefault, request.cfURLRequest());
390 #if USE(CFURLSTORAGESESSIONS)
391     wkSetRequestStorageSession(ResourceHandle::currentStorageSession(), newRequest);
392 #endif
393     
394     if (!shouldContentSniff)
395         wkSetCFURLRequestShouldContentSniff(newRequest, false);
396
397     RetainPtr<CFMutableDictionaryRef> sslProps;
398
399     if (allowsAnyHTTPSCertificateHosts().contains(request.url().host().lower())) {
400         sslProps.adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
401         CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsAnyRoot, kCFBooleanTrue);
402         CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsExpiredRoots, kCFBooleanTrue);
403         CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsExpiredCertificates, kCFBooleanTrue);
404         CFDictionaryAddValue(sslProps.get(), kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse);
405     }
406
407     HashMap<String, RetainPtr<CFDataRef> >::iterator clientCert = clientCerts().find(request.url().host().lower());
408     if (clientCert != clientCerts().end()) {
409         if (!sslProps)
410             sslProps.adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
411 #if PLATFORM(WIN)
412         wkSetClientCertificateInSSLProperties(sslProps.get(), (clientCert->second).get());
413 #endif
414     }
415
416     if (sslProps)
417         CFURLRequestSetSSLProperties(newRequest, sslProps.get());
418
419     if (RetainPtr<CFHTTPCookieStorageRef> cookieStorage = currentCFHTTPCookieStorage()) {
420         CFURLRequestSetHTTPCookieStorage(newRequest, cookieStorage.get());
421         CFHTTPCookieStorageAcceptPolicy policy = CFHTTPCookieStorageGetCookieAcceptPolicy(cookieStorage.get());
422         CFURLRequestSetHTTPCookieStorageAcceptPolicy(newRequest, policy);
423
424         // If a URL already has cookies, then we'll relax the 3rd party cookie policy and accept new cookies.
425         if (policy == CFHTTPCookieStorageAcceptPolicyOnlyFromMainDocumentDomain) {
426             CFURLRef url = CFURLRequestGetURL(newRequest);
427             RetainPtr<CFArrayRef> cookies(AdoptCF, CFHTTPCookieStorageCopyCookiesForURL(cookieStorage.get(), url, false));
428             if (CFArrayGetCount(cookies.get()))
429                 CFURLRequestSetMainDocumentURL(newRequest, url);
430         }
431     }
432
433     return newRequest;
434 }
435
436 static CFDictionaryRef createConnectionProperties(bool shouldUseCredentialStorage)
437 {
438     static const CFStringRef webKitPrivateSessionCF = CFSTR("WebKitPrivateSession");
439     static const CFStringRef _kCFURLConnectionSessionID = CFSTR("_kCFURLConnectionSessionID");
440     static const CFStringRef kCFURLConnectionSocketStreamProperties = CFSTR("kCFURLConnectionSocketStreamProperties");
441
442     CFDictionaryRef sessionID = shouldUseCredentialStorage ?
443         CFDictionaryCreate(0, 0, 0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) :
444         CFDictionaryCreate(0, (const void**)&_kCFURLConnectionSessionID, (const void**)&webKitPrivateSessionCF, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
445
446     CFDictionaryRef propertiesDictionary = CFDictionaryCreate(0, (const void**)&kCFURLConnectionSocketStreamProperties, (const void**)&sessionID, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
447
448     CFRelease(sessionID);
449     return propertiesDictionary;
450 }
451
452 void ResourceHandle::createCFURLConnection(bool shouldUseCredentialStorage, bool shouldContentSniff)
453 {
454     if ((!d->m_user.isEmpty() || !d->m_pass.isEmpty()) && !firstRequest().url().protocolInHTTPFamily()) {
455         // Credentials for ftp can only be passed in URL, the didReceiveAuthenticationChallenge delegate call won't be made.
456         KURL urlWithCredentials(firstRequest().url());
457         urlWithCredentials.setUser(d->m_user);
458         urlWithCredentials.setPass(d->m_pass);
459         firstRequest().setURL(urlWithCredentials);
460     }
461
462     // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, 
463     // try and reuse the credential preemptively, as allowed by RFC 2617.
464     if (shouldUseCredentialStorage && firstRequest().url().protocolInHTTPFamily()) {
465         if (d->m_user.isEmpty() && d->m_pass.isEmpty()) {
466             // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, 
467             // try and reuse the credential preemptively, as allowed by RFC 2617.
468             d->m_initialCredential = CredentialStorage::get(firstRequest().url());
469         } else {
470             // If there is already a protection space known for the URL, update stored credentials before sending a request.
471             // This makes it possible to implement logout by sending an XMLHttpRequest with known incorrect credentials, and aborting it immediately
472             // (so that an authentication dialog doesn't pop up).
473             CredentialStorage::set(Credential(d->m_user, d->m_pass, CredentialPersistenceNone), firstRequest().url());
474         }
475     }
476         
477     if (!d->m_initialCredential.isEmpty()) {
478         // FIXME: Support Digest authentication, and Proxy-Authorization.
479         applyBasicAuthorizationHeader(firstRequest(), d->m_initialCredential);
480     }
481
482     RetainPtr<CFURLRequestRef> request(AdoptCF, makeFinalRequest(firstRequest(), shouldContentSniff));
483
484 #if HAVE(NETWORK_CFDATA_ARRAY_CALLBACK) && USE(PROTECTION_SPACE_AUTH_CALLBACK)
485     CFURLConnectionClient_V6 client = { 6, this, 0, 0, 0, WebCore::willSendRequest, didReceiveResponse, didReceiveData, 0, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge, didSendBodyData, shouldUseCredentialStorageCallback, 0, canRespondToProtectionSpace, 0, didReceiveDataArray};
486 #else
487     CFURLConnectionClient_V3 client = { 3, this, 0, 0, 0, WebCore::willSendRequest, didReceiveResponse, didReceiveData, 0, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge, didSendBodyData, shouldUseCredentialStorageCallback, 0};
488 #endif
489     RetainPtr<CFDictionaryRef> connectionProperties(AdoptCF, createConnectionProperties(shouldUseCredentialStorage));
490
491     d->m_connection.adoptCF(CFURLConnectionCreateWithProperties(0, request.get(), reinterpret_cast<CFURLConnectionClient*>(&client), connectionProperties.get()));
492 }
493
494 bool ResourceHandle::start(NetworkingContext* context)
495 {
496     if (!context)
497         return false;
498
499     // If NetworkingContext is invalid then we are no longer attached to a Page,
500     // this must be an attempted load from an unload handler, so let's just block it.
501     if (!context->isValid())
502         return false;
503
504     bool shouldUseCredentialStorage = !client() || client()->shouldUseCredentialStorage(this);
505
506     createCFURLConnection(shouldUseCredentialStorage, d->m_shouldContentSniff);
507
508 #if PLATFORM(WIN)
509     CFURLConnectionScheduleWithCurrentMessageQueue(d->m_connection.get());
510 #else
511     CFURLConnectionScheduleWithRunLoop(d->m_connection.get(), CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
512 #endif
513     CFURLConnectionScheduleDownloadWithRunLoop(d->m_connection.get(), loaderRunLoop(), kCFRunLoopDefaultMode);
514     CFURLConnectionStart(d->m_connection.get());
515
516     LOG(Network, "CFNet - Starting URL %s (handle=%p, conn=%p)", firstRequest().url().string().utf8().data(), this, d->m_connection.get());
517
518     return true;
519 }
520
521 void ResourceHandle::cancel()
522 {
523     if (d->m_connection) {
524         CFURLConnectionCancel(d->m_connection.get());
525         d->m_connection = 0;
526     }
527 }
528
529 void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
530 {
531     const KURL& url = request.url();
532     d->m_user = url.user();
533     d->m_pass = url.pass();
534     d->m_lastHTTPMethod = request.httpMethod();
535     request.removeCredentials();
536
537     if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) {
538         // If the network layer carries over authentication headers from the original request
539         // in a cross-origin redirect, we want to clear those headers here.
540         request.clearHTTPAuthorization();
541     } else {
542         // Only consider applying authentication credentials if this is actually a redirect and the redirect
543         // URL didn't include credentials of its own.
544         if (d->m_user.isEmpty() && d->m_pass.isEmpty() && !redirectResponse.isNull()) {
545             Credential credential = CredentialStorage::get(request.url());
546             if (!credential.isEmpty()) {
547                 d->m_initialCredential = credential;
548                 
549                 // FIXME: Support Digest authentication, and Proxy-Authorization.
550                 applyBasicAuthorizationHeader(request, d->m_initialCredential);
551             }
552         }
553     }
554
555 #if USE(CFURLSTORAGESESSIONS)
556      request.setStorageSession(ResourceHandle::currentStorageSession());
557 #endif
558
559     client()->willSendRequest(this, request, redirectResponse);
560 }
561
562 bool ResourceHandle::shouldUseCredentialStorage()
563 {
564     LOG(Network, "CFNet - shouldUseCredentialStorage()");
565     if (client())
566         return client()->shouldUseCredentialStorage(this);
567
568     return false;
569 }
570
571 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
572 {
573     LOG(Network, "CFNet - didReceiveAuthenticationChallenge()");
574     ASSERT(d->m_currentWebChallenge.isNull());
575     // Since CFURLConnection networking relies on keeping a reference to the original CFURLAuthChallengeRef,
576     // we make sure that is actually present
577     ASSERT(challenge.cfURLAuthChallengeRef());
578     ASSERT(challenge.authenticationClient() == this); // Should be already set.
579
580     if (!d->m_user.isNull() && !d->m_pass.isNull()) {
581         RetainPtr<CFStringRef> user(AdoptCF, d->m_user.createCFString());
582         RetainPtr<CFStringRef> pass(AdoptCF, d->m_pass.createCFString());
583         RetainPtr<CFURLCredentialRef> credential(AdoptCF,
584             CFURLCredentialCreate(kCFAllocatorDefault, user.get(), pass.get(), 0, kCFURLCredentialPersistenceNone));
585         
586         KURL urlToStore;
587         if (challenge.failureResponse().httpStatusCode() == 401)
588             urlToStore = firstRequest().url();
589         CredentialStorage::set(core(credential.get()), challenge.protectionSpace(), urlToStore);
590         
591         CFURLConnectionUseCredential(d->m_connection.get(), credential.get(), challenge.cfURLAuthChallengeRef());
592         d->m_user = String();
593         d->m_pass = String();
594         // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
595         return;
596     }
597
598     if (!client() || client()->shouldUseCredentialStorage(this)) {
599         if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
600             // The stored credential wasn't accepted, stop using it.
601             // There is a race condition here, since a different credential might have already been stored by another ResourceHandle,
602             // but the observable effect should be very minor, if any.
603             CredentialStorage::remove(challenge.protectionSpace());
604         }
605
606         if (!challenge.previousFailureCount()) {
607             Credential credential = CredentialStorage::get(challenge.protectionSpace());
608             if (!credential.isEmpty() && credential != d->m_initialCredential) {
609                 ASSERT(credential.persistence() == CredentialPersistenceNone);
610                 if (challenge.failureResponse().httpStatusCode() == 401) {
611                     // Store the credential back, possibly adding it as a default for this directory.
612                     CredentialStorage::set(credential, challenge.protectionSpace(), firstRequest().url());
613                 }
614                 RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(credential));
615                 CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
616                 return;
617             }
618         }
619     }
620
621     d->m_currentWebChallenge = challenge;
622     
623     if (client())
624         client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
625 }
626
627 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
628 bool ResourceHandle::canAuthenticateAgainstProtectionSpace(const ProtectionSpace& protectionSpace)
629 {
630     if (client())
631         return client()->canAuthenticateAgainstProtectionSpace(this, protectionSpace);
632
633     return false;
634 }
635 #endif
636
637 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
638 {
639     LOG(Network, "CFNet - receivedCredential()");
640     ASSERT(!challenge.isNull());
641     ASSERT(challenge.cfURLAuthChallengeRef());
642     if (challenge != d->m_currentWebChallenge)
643         return;
644
645     // FIXME: Support empty credentials. Currently, an empty credential cannot be stored in WebCore credential storage, as that's empty value for its map.
646     if (credential.isEmpty()) {
647         receivedRequestToContinueWithoutCredential(challenge);
648         return;
649     }
650
651     if (credential.persistence() == CredentialPersistenceForSession) {
652         // Manage per-session credentials internally, because once NSURLCredentialPersistencePerSession is used, there is no way
653         // to ignore it for a particular request (short of removing it altogether).
654         Credential webCredential(credential.user(), credential.password(), CredentialPersistenceNone);
655         RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(webCredential));
656         
657         KURL urlToStore;
658         if (challenge.failureResponse().httpStatusCode() == 401)
659             urlToStore = firstRequest().url();      
660         CredentialStorage::set(webCredential, challenge.protectionSpace(), urlToStore);
661
662         CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
663     } else {
664         RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(credential));
665         CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
666     }
667
668     clearAuthentication();
669 }
670
671 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
672 {
673     LOG(Network, "CFNet - receivedRequestToContinueWithoutCredential()");
674     ASSERT(!challenge.isNull());
675     ASSERT(challenge.cfURLAuthChallengeRef());
676     if (challenge != d->m_currentWebChallenge)
677         return;
678
679     CFURLConnectionUseCredential(d->m_connection.get(), 0, challenge.cfURLAuthChallengeRef());
680
681     clearAuthentication();
682 }
683
684 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
685 {
686     LOG(Network, "CFNet - receivedCancellation()");
687     if (challenge != d->m_currentWebChallenge)
688         return;
689
690     if (client())
691         client()->receivedCancellation(this, challenge);
692 }
693
694 CFURLConnectionRef ResourceHandle::connection() const
695 {
696     return d->m_connection.get();
697 }
698
699 CFURLConnectionRef ResourceHandle::releaseConnectionForDownload()
700 {
701     LOG(Network, "CFNet - Job %p releasing connection %p for download", this, d->m_connection.get());
702     return d->m_connection.releaseRef();
703 }
704
705 CFStringRef ResourceHandle::synchronousLoadRunLoopMode()
706 {
707     return CFSTR("WebCoreSynchronousLoaderRunLoopMode");
708 }
709
710 void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& vector)
711 {
712     LOG(Network, "ResourceHandle::loadResourceSynchronously:%s allowStoredCredentials:%u", request.url().string().utf8().data(), storedCredentials);
713
714     ASSERT(!request.isEmpty());
715
716     ASSERT(response.isNull());
717     ASSERT(error.isNull());
718
719     OwnPtr<WebCoreSynchronousLoaderClient> client = WebCoreSynchronousLoaderClient::create(response, error);
720     client->setAllowStoredCredentials(storedCredentials == AllowStoredCredentials);
721
722     RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(request, client.get(), false /*defersLoading*/, true /*shouldContentSniff*/));
723
724     if (context && handle->d->m_scheduledFailureType != NoFailure) {
725         error = context->blockedError(request);
726         return;
727     }
728
729     handle->createCFURLConnection(storedCredentials == AllowStoredCredentials, ResourceHandle::shouldContentSniffURL(request.url()));
730
731     CFURLConnectionScheduleWithRunLoop(handle->connection(), CFRunLoopGetCurrent(), synchronousLoadRunLoopMode());
732     CFURLConnectionScheduleDownloadWithRunLoop(handle->connection(), CFRunLoopGetCurrent(), synchronousLoadRunLoopMode());
733     CFURLConnectionStart(handle->connection());
734
735     while (!client->isDone())
736         CFRunLoopRunInMode(synchronousLoadRunLoopMode(), UINT_MAX, true);
737
738     CFURLConnectionCancel(handle->connection());
739     
740     if (error.isNull() && response.mimeType().isNull())
741         setDefaultMIMEType(response.cfURLResponse());
742
743     RetainPtr<CFDataRef> data = client->data();
744     
745     if (!error.isNull()) {
746         response = ResourceResponse(request.url(), String(), 0, String(), String());
747
748         CFErrorRef cfError = error;
749         CFStringRef domain = CFErrorGetDomain(cfError);
750         // FIXME: Return the actual response for failed authentication.
751         if (domain == kCFErrorDomainCFNetwork)
752             response.setHTTPStatusCode(CFErrorGetCode(cfError));
753         else
754             response.setHTTPStatusCode(404);
755     }
756
757     if (data) {
758         ASSERT(vector.isEmpty());
759         vector.append(CFDataGetBytePtr(data.get()), CFDataGetLength(data.get()));
760     }
761 }
762
763 void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
764 {
765     allowsAnyHTTPSCertificateHosts().add(host.lower());
766 }
767
768 void ResourceHandle::setClientCertificate(const String& host, CFDataRef cert)
769 {
770     clientCerts().set(host.lower(), cert);
771 }
772
773 void ResourceHandle::platformSetDefersLoading(bool defers)
774 {
775     if (!d->m_connection)
776         return;
777
778     if (defers)
779         CFURLConnectionHalt(d->m_connection.get());
780     else
781         CFURLConnectionResume(d->m_connection.get());
782 }
783
784 bool ResourceHandle::loadsBlocked()
785 {
786     return false;
787 }
788
789 bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame*)
790 {
791     request.setCachePolicy(ReturnCacheDataDontLoad);
792     
793     CFURLResponseRef cfResponse = 0;
794     CFErrorRef cfError = 0;
795     RetainPtr<CFURLRequestRef> cfRequest(AdoptCF, makeFinalRequest(request, true));
796     RetainPtr<CFDataRef> data(AdoptCF, CFURLConnectionSendSynchronousRequest(cfRequest.get(), &cfResponse, &cfError, request.timeoutInterval()));
797     bool cached = cfResponse && !cfError;
798
799     if (cfError)
800         CFRelease(cfError);
801     if (cfResponse)
802         CFRelease(cfResponse);
803
804     return cached;
805 }
806
807 #if PLATFORM(MAC)
808 void ResourceHandle::schedule(SchedulePair* pair)
809 {
810     CFRunLoopRef runLoop = pair->runLoop();
811     if (!runLoop)
812         return;
813
814     CFURLConnectionScheduleWithRunLoop(d->m_connection.get(), runLoop, pair->mode());
815     if (d->m_startWhenScheduled) {
816         CFURLConnectionStart(d->m_connection.get());
817         d->m_startWhenScheduled = false;
818     }
819 }
820
821 void ResourceHandle::unschedule(SchedulePair* pair)
822 {
823     CFRunLoopRef runLoop = pair->runLoop();
824     if (!runLoop)
825         return;
826
827     CFURLConnectionUnscheduleFromRunLoop(d->m_connection.get(), runLoop, pair->mode());
828 }
829 #endif
830
831 void WebCoreSynchronousLoaderClient::willSendRequest(ResourceHandle* handle, ResourceRequest& request, const ResourceResponse& /*redirectResponse*/)
832 {
833     // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests.
834     if (!protocolHostAndPortAreEqual(handle->firstRequest().url(), request.url())) {
835         ASSERT(!m_error.cfError());
836         RetainPtr<CFErrorRef> cfError(AdoptCF, CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainCFNetwork, kCFURLErrorBadServerResponse, 0));
837         m_error = cfError.get();
838         m_isDone = true;
839         CFURLRequestRef nullRequest = 0;
840         request = nullRequest;
841         return;
842     }
843 }
844 void WebCoreSynchronousLoaderClient::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
845 {
846     m_response = response;
847 }
848
849 void WebCoreSynchronousLoaderClient::didReceiveData(ResourceHandle*, const char* data, int length, int /*encodedDataLength*/)
850 {
851     if (!m_data)
852         m_data.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0));
853     CFDataAppendBytes(m_data.get(), reinterpret_cast<const UInt8*>(data), length);
854 }
855
856 void WebCoreSynchronousLoaderClient::didFinishLoading(ResourceHandle*, double)
857 {
858     m_isDone = true;
859 }
860
861 void WebCoreSynchronousLoaderClient::didFail(ResourceHandle*, const ResourceError& error)
862 {
863     m_error = error;
864     m_isDone = true;
865 }
866
867 void WebCoreSynchronousLoaderClient::didReceiveAuthenticationChallenge(ResourceHandle* handle, const AuthenticationChallenge& challenge)
868 {
869     // FIXME: The user should be asked for credentials, as in async case.
870     CFURLConnectionUseCredential(handle->connection(), 0, challenge.cfURLAuthChallengeRef());
871 }
872
873 bool WebCoreSynchronousLoaderClient::shouldUseCredentialStorage(ResourceHandle*)
874 {
875     // FIXME: We should ask FrameLoaderClient whether using credential storage is globally forbidden.
876     return m_allowStoredCredentials;
877 }
878
879 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
880 bool WebCoreSynchronousLoaderClient::canAuthenticateAgainstProtectionSpace(ResourceHandle*, const ProtectionSpace&)
881 {
882     // FIXME: We should ask FrameLoaderClient. <http://webkit.org/b/65196>
883     return true;
884 }
885 #endif
886
887 #endif // USE(CFNETWORK)
888
889 #if HAVE(NETWORK_CFDATA_ARRAY_CALLBACK)
890 void ResourceHandle::handleDataArray(CFArrayRef dataArray)
891 {
892     ASSERT(client());
893     if (client()->supportsDataArray()) {
894         client()->didReceiveDataArray(this, dataArray);
895         return;
896     }
897
898     CFIndex count = CFArrayGetCount(dataArray);
899     ASSERT(count);
900     if (count == 1) {
901         CFDataRef data = static_cast<CFDataRef>(CFArrayGetValueAtIndex(dataArray, 0));
902         CFIndex length = CFDataGetLength(data);
903         client()->didReceiveData(this, reinterpret_cast<const char*>(CFDataGetBytePtr(data)), length, static_cast<int>(length));
904         return;
905     }
906
907     CFIndex totalSize = 0;
908     CFIndex index;
909     for (index = 0; index < count; index++)
910         totalSize += CFDataGetLength(static_cast<CFDataRef>(CFArrayGetValueAtIndex(dataArray, index)));
911
912     RetainPtr<CFMutableDataRef> mergedData(AdoptCF, CFDataCreateMutable(kCFAllocatorDefault, totalSize));
913     for (index = 0; index < count; index++) {
914         CFDataRef data = static_cast<CFDataRef>(CFArrayGetValueAtIndex(dataArray, index));
915         CFDataAppendBytes(mergedData.get(), CFDataGetBytePtr(data), CFDataGetLength(data));
916     }
917
918     client()->didReceiveData(this, reinterpret_cast<const char*>(CFDataGetBytePtr(mergedData.get())), totalSize, static_cast<int>(totalSize));
919 }
920 #endif
921
922 #if USE(CFURLSTORAGESESSIONS)
923
924 RetainPtr<CFURLStorageSessionRef> ResourceHandle::createPrivateBrowsingStorageSession(CFStringRef identifier)
925 {
926 #if PLATFORM(WIN)
927     return RetainPtr<CFURLStorageSessionRef>(AdoptCF, wkCreatePrivateStorageSession(identifier, defaultStorageSession()));
928 #else
929     return RetainPtr<CFURLStorageSessionRef>(AdoptCF, wkCreatePrivateStorageSession(identifier));
930 #endif
931 }
932
933 CFURLStorageSessionRef ResourceHandle::currentStorageSession()
934 {
935     if (CFURLStorageSessionRef privateStorageSession = privateBrowsingStorageSession())
936         return privateStorageSession;
937     return defaultStorageSession();
938 }
939
940 static RetainPtr<CFURLStorageSessionRef>& defaultCFURLStorageSession()
941 {
942     DEFINE_STATIC_LOCAL(RetainPtr<CFURLStorageSessionRef>, storageSession, ());
943     return storageSession;
944 }
945
946 void ResourceHandle::setDefaultStorageSession(CFURLStorageSessionRef storageSession)
947 {
948     defaultCFURLStorageSession() = storageSession;
949 }
950
951 CFURLStorageSessionRef ResourceHandle::defaultStorageSession()
952 {
953     return defaultCFURLStorageSession().get();
954 }
955
956 static RetainPtr<CFURLStorageSessionRef>& privateStorageSession()
957 {
958     DEFINE_STATIC_LOCAL(RetainPtr<CFURLStorageSessionRef>, storageSession, ());
959     return storageSession;
960 }
961
962 static String& privateBrowsingStorageSessionIdentifierBase()
963 {
964     DEFINE_STATIC_LOCAL(String, base, ());
965     return base;
966 }
967
968 void ResourceHandle::setPrivateBrowsingEnabled(bool enabled)
969 {
970     if (!enabled) {
971         privateStorageSession() = nullptr;
972         return;
973     }
974
975     if (privateStorageSession())
976         return;
977
978     String base = privateBrowsingStorageSessionIdentifierBase().isNull() ? privateBrowsingStorageSessionIdentifierDefaultBase() : privateBrowsingStorageSessionIdentifierBase();
979     RetainPtr<CFStringRef> cfIdentifier(AdoptCF, String(base + ".PrivateBrowsing").createCFString());
980
981     privateStorageSession() = createPrivateBrowsingStorageSession(cfIdentifier.get());
982 }
983
984 CFURLStorageSessionRef ResourceHandle::privateBrowsingStorageSession()
985 {
986     return privateStorageSession().get();
987 }
988
989 void ResourceHandle::setPrivateBrowsingStorageSessionIdentifierBase(const String& identifier)
990 {
991     privateBrowsingStorageSessionIdentifierBase() = identifier;
992 }
993
994 #if PLATFORM(WIN)
995
996 String ResourceHandle::privateBrowsingStorageSessionIdentifierDefaultBase()
997 {
998     return String(reinterpret_cast<CFStringRef>(CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), kCFBundleIdentifierKey)));
999 }
1000
1001 #endif // PLATFORM(WIN)
1002
1003 #endif // USE(CFURLSTORAGESESSIONS)
1004
1005 } // namespace WebCore
1006