• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007, 2009 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 "ResourceHandle.h"
29 #include "ResourceHandleClient.h"
30 #include "ResourceHandleInternal.h"
31 
32 #include "AuthenticationCF.h"
33 #include "AuthenticationChallenge.h"
34 #include "CookieStorageWin.h"
35 #include "CString.h"
36 #include "DocLoader.h"
37 #include "Frame.h"
38 #include "FrameLoader.h"
39 #include "Logging.h"
40 #include "MIMETypeRegistry.h"
41 #include "ResourceError.h"
42 #include "ResourceResponse.h"
43 
44 #include <wtf/HashMap.h>
45 #include <wtf/Threading.h>
46 
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <process.h> // for _beginthread()
50 
51 #include <CFNetwork/CFNetwork.h>
52 #include <WebKitSystemInterface/WebKitSystemInterface.h>
53 
54 namespace WebCore {
55 
56 static CFStringRef WebCoreSynchronousLoaderRunLoopMode = CFSTR("WebCoreSynchronousLoaderRunLoopMode");
57 
58 class WebCoreSynchronousLoader {
59 public:
60     static RetainPtr<CFDataRef> load(const ResourceRequest&, StoredCredentials, ResourceResponse&, ResourceError&);
61 
62 private:
WebCoreSynchronousLoader(ResourceResponse & response,ResourceError & error)63     WebCoreSynchronousLoader(ResourceResponse& response, ResourceError& error)
64         : m_isDone(false)
65         , m_response(response)
66         , m_error(error)
67     {
68     }
69 
70     static CFURLRequestRef willSendRequest(CFURLConnectionRef, CFURLRequestRef, CFURLResponseRef, const void* clientInfo);
71     static void didReceiveResponse(CFURLConnectionRef, CFURLResponseRef, const void* clientInfo);
72     static void didReceiveData(CFURLConnectionRef, CFDataRef, CFIndex, const void* clientInfo);
73     static void didFinishLoading(CFURLConnectionRef, const void* clientInfo);
74     static void didFail(CFURLConnectionRef, CFErrorRef, const void* clientInfo);
75     static void didReceiveChallenge(CFURLConnectionRef, CFURLAuthChallengeRef, const void* clientInfo);
76     static Boolean shouldUseCredentialStorage(CFURLConnectionRef, const void* clientInfo);
77 
78     bool m_isDone;
79     RetainPtr<CFURLRef> m_url;
80     RetainPtr<CFStringRef> m_user;
81     RetainPtr<CFStringRef> m_pass;
82     bool m_allowStoredCredentials;
83     ResourceResponse& m_response;
84     RetainPtr<CFMutableDataRef> m_data;
85     ResourceError& m_error;
86 };
87 
allowsAnyHTTPSCertificateHosts()88 static HashSet<String>& allowsAnyHTTPSCertificateHosts()
89 {
90     static HashSet<String> hosts;
91 
92     return hosts;
93 }
94 
clientCerts()95 static HashMap<String, RetainPtr<CFDataRef> >& clientCerts()
96 {
97     static HashMap<String, RetainPtr<CFDataRef> > certs;
98     return certs;
99 }
100 
setDefaultMIMEType(CFURLResponseRef response)101 static void setDefaultMIMEType(CFURLResponseRef response)
102 {
103     static CFStringRef defaultMIMETypeString = defaultMIMEType().createCFString();
104 
105     CFURLResponseSetMIMEType(response, defaultMIMETypeString);
106 }
107 
willSendRequest(CFURLConnectionRef conn,CFURLRequestRef cfRequest,CFURLResponseRef cfRedirectResponse,const void * clientInfo)108 CFURLRequestRef willSendRequest(CFURLConnectionRef conn, CFURLRequestRef cfRequest, CFURLResponseRef cfRedirectResponse, const void* clientInfo)
109 {
110     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
111 
112     if (!cfRedirectResponse) {
113         CFRetain(cfRequest);
114         return cfRequest;
115     }
116 
117     LOG(Network, "CFNet - willSendRequest(conn=%p, handle=%p) (%s)", conn, handle, handle->request().url().string().utf8().data());
118 
119     ResourceRequest request;
120     if (cfRedirectResponse) {
121         CFHTTPMessageRef httpMessage = CFURLResponseGetHTTPResponse(cfRedirectResponse);
122         if (httpMessage && CFHTTPMessageGetResponseStatusCode(httpMessage) == 307) {
123             RetainPtr<CFStringRef> originalMethod(AdoptCF, handle->request().httpMethod().createCFString());
124             RetainPtr<CFStringRef> newMethod(AdoptCF, CFURLRequestCopyHTTPRequestMethod(cfRequest));
125             if (CFStringCompareWithOptions(originalMethod.get(), newMethod.get(), CFRangeMake(0, CFStringGetLength(originalMethod.get())), kCFCompareCaseInsensitive)) {
126                 RetainPtr<CFMutableURLRequestRef> mutableRequest(AdoptCF, CFURLRequestCreateMutableCopy(0, cfRequest));
127                 CFURLRequestSetHTTPRequestMethod(mutableRequest.get(), originalMethod.get());
128                 request = mutableRequest.get();
129             }
130         }
131     }
132     if (request.isNull())
133         request = cfRequest;
134 
135     handle->willSendRequest(request, cfRedirectResponse);
136 
137     if (request.isNull())
138         return 0;
139 
140     cfRequest = request.cfURLRequest();
141 
142     CFRetain(cfRequest);
143     return cfRequest;
144 }
145 
didReceiveResponse(CFURLConnectionRef conn,CFURLResponseRef cfResponse,const void * clientInfo)146 void didReceiveResponse(CFURLConnectionRef conn, CFURLResponseRef cfResponse, const void* clientInfo)
147 {
148     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
149 
150     LOG(Network, "CFNet - didReceiveResponse(conn=%p, handle=%p) (%s)", conn, handle, handle->request().url().string().utf8().data());
151 
152     if (!handle->client())
153         return;
154 
155     if (!CFURLResponseGetMIMEType(cfResponse)) {
156         // We should never be applying the default MIMEType if we told the networking layer to do content sniffing for handle.
157         ASSERT(!handle->shouldContentSniff());
158         setDefaultMIMEType(cfResponse);
159     }
160 
161     handle->client()->didReceiveResponse(handle, cfResponse);
162 }
163 
didReceiveData(CFURLConnectionRef conn,CFDataRef data,CFIndex originalLength,const void * clientInfo)164 void didReceiveData(CFURLConnectionRef conn, CFDataRef data, CFIndex originalLength, const void* clientInfo)
165 {
166     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
167     const UInt8* bytes = CFDataGetBytePtr(data);
168     CFIndex length = CFDataGetLength(data);
169 
170     LOG(Network, "CFNet - didReceiveData(conn=%p, handle=%p, bytes=%d) (%s)", conn, handle, length, handle->request().url().string().utf8().data());
171 
172     if (handle->client())
173         handle->client()->didReceiveData(handle, (const char*)bytes, length, originalLength);
174 }
175 
didSendBodyData(CFURLConnectionRef conn,CFIndex bytesWritten,CFIndex totalBytesWritten,CFIndex totalBytesExpectedToWrite,const void * clientInfo)176 static void didSendBodyData(CFURLConnectionRef conn, CFIndex bytesWritten, CFIndex totalBytesWritten, CFIndex totalBytesExpectedToWrite, const void *clientInfo)
177 {
178     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
179     if (!handle || !handle->client())
180         return;
181     handle->client()->didSendData(handle, totalBytesWritten, totalBytesExpectedToWrite);
182 }
183 
shouldUseCredentialStorageCallback(CFURLConnectionRef conn,const void * clientInfo)184 static Boolean shouldUseCredentialStorageCallback(CFURLConnectionRef conn, const void* clientInfo)
185 {
186     ResourceHandle* handle = const_cast<ResourceHandle*>(static_cast<const ResourceHandle*>(clientInfo));
187 
188     LOG(Network, "CFNet - shouldUseCredentialStorage(conn=%p, handle=%p) (%s)", conn, handle, handle->request().url().string().utf8().data());
189 
190     if (!handle)
191         return false;
192 
193     return handle->shouldUseCredentialStorage();
194 }
195 
didFinishLoading(CFURLConnectionRef conn,const void * clientInfo)196 void didFinishLoading(CFURLConnectionRef conn, const void* clientInfo)
197 {
198     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
199 
200     LOG(Network, "CFNet - didFinishLoading(conn=%p, handle=%p) (%s)", conn, handle, handle->request().url().string().utf8().data());
201 
202     if (handle->client())
203         handle->client()->didFinishLoading(handle);
204 }
205 
didFail(CFURLConnectionRef conn,CFErrorRef error,const void * clientInfo)206 void didFail(CFURLConnectionRef conn, CFErrorRef error, const void* clientInfo)
207 {
208     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
209 
210     LOG(Network, "CFNet - didFail(conn=%p, handle=%p, error = %p) (%s)", conn, handle, error, handle->request().url().string().utf8().data());
211 
212     if (handle->client())
213         handle->client()->didFail(handle, ResourceError(error));
214 }
215 
willCacheResponse(CFURLConnectionRef conn,CFCachedURLResponseRef cachedResponse,const void * clientInfo)216 CFCachedURLResponseRef willCacheResponse(CFURLConnectionRef conn, CFCachedURLResponseRef cachedResponse, const void* clientInfo)
217 {
218     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
219 
220     if (handle->client() && !handle->client()->shouldCacheResponse(handle, cachedResponse))
221         return 0;
222 
223     CacheStoragePolicy policy = static_cast<CacheStoragePolicy>(CFCachedURLResponseGetStoragePolicy(cachedResponse));
224 
225     if (handle->client())
226         handle->client()->willCacheResponse(handle, policy);
227 
228     if (static_cast<CFURLCacheStoragePolicy>(policy) != CFCachedURLResponseGetStoragePolicy(cachedResponse))
229         cachedResponse = CFCachedURLResponseCreateWithUserInfo(kCFAllocatorDefault,
230                                                                CFCachedURLResponseGetWrappedResponse(cachedResponse),
231                                                                CFCachedURLResponseGetReceiverData(cachedResponse),
232                                                                CFCachedURLResponseGetUserInfo(cachedResponse),
233                                                                static_cast<CFURLCacheStoragePolicy>(policy));
234     CFRetain(cachedResponse);
235 
236     return cachedResponse;
237 }
238 
didReceiveChallenge(CFURLConnectionRef conn,CFURLAuthChallengeRef challenge,const void * clientInfo)239 void didReceiveChallenge(CFURLConnectionRef conn, CFURLAuthChallengeRef challenge, const void* clientInfo)
240 {
241     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
242     ASSERT(handle);
243     LOG(Network, "CFNet - didReceiveChallenge(conn=%p, handle=%p (%s)", conn, handle, handle->request().url().string().utf8().data());
244 
245     handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(challenge, handle));
246 }
247 
addHeadersFromHashMap(CFMutableURLRequestRef request,const HTTPHeaderMap & requestHeaders)248 void addHeadersFromHashMap(CFMutableURLRequestRef request, const HTTPHeaderMap& requestHeaders)
249 {
250     if (!requestHeaders.size())
251         return;
252 
253     HTTPHeaderMap::const_iterator end = requestHeaders.end();
254     for (HTTPHeaderMap::const_iterator it = requestHeaders.begin(); it != end; ++it) {
255         CFStringRef key = it->first.createCFString();
256         CFStringRef value = it->second.createCFString();
257         CFURLRequestSetHTTPHeaderFieldValue(request, key, value);
258         CFRelease(key);
259         CFRelease(value);
260     }
261 }
262 
~ResourceHandleInternal()263 ResourceHandleInternal::~ResourceHandleInternal()
264 {
265     if (m_connection) {
266         LOG(Network, "CFNet - Cancelling connection %p (%s)", m_connection, m_request.url().string().utf8().data());
267         CFURLConnectionCancel(m_connection.get());
268     }
269 }
270 
~ResourceHandle()271 ResourceHandle::~ResourceHandle()
272 {
273     LOG(Network, "CFNet - Destroying job %p (%s)", this, d->m_request.url().string().utf8().data());
274 }
275 
arrayFromFormData(const FormData & d)276 CFArrayRef arrayFromFormData(const FormData& d)
277 {
278     size_t size = d.elements().size();
279     CFMutableArrayRef a = CFArrayCreateMutable(0, d.elements().size(), &kCFTypeArrayCallBacks);
280     for (size_t i = 0; i < size; ++i) {
281         const FormDataElement& e = d.elements()[i];
282         if (e.m_type == FormDataElement::data) {
283             CFDataRef data = CFDataCreate(0, (const UInt8*)e.m_data.data(), e.m_data.size());
284             CFArrayAppendValue(a, data);
285             CFRelease(data);
286         } else {
287             ASSERT(e.m_type == FormDataElement::encodedFile);
288             CFStringRef filename = e.m_filename.createCFString();
289             CFArrayAppendValue(a, filename);
290             CFRelease(filename);
291         }
292     }
293     return a;
294 }
295 
emptyPerform(void * unused)296 void emptyPerform(void* unused)
297 {
298 }
299 
300 static CFRunLoopRef loaderRL = 0;
runLoaderThread(void * unused)301 void* runLoaderThread(void *unused)
302 {
303     loaderRL = CFRunLoopGetCurrent();
304 
305     // Must add a source to the run loop to prevent CFRunLoopRun() from exiting
306     CFRunLoopSourceContext ctxt = {0, (void *)1 /*must be non-NULL*/, 0, 0, 0, 0, 0, 0, 0, emptyPerform};
307     CFRunLoopSourceRef bogusSource = CFRunLoopSourceCreate(0, 0, &ctxt);
308     CFRunLoopAddSource(loaderRL, bogusSource,kCFRunLoopDefaultMode);
309 
310     CFRunLoopRun();
311 
312     return 0;
313 }
314 
loaderRunLoop()315 CFRunLoopRef ResourceHandle::loaderRunLoop()
316 {
317     if (!loaderRL) {
318         createThread(runLoaderThread, 0, "WebCore: CFNetwork Loader");
319         while (loaderRL == 0) {
320             // FIXME: sleep 10? that can't be right...
321             Sleep(10);
322         }
323     }
324     return loaderRL;
325 }
326 
makeFinalRequest(const ResourceRequest & request,bool shouldContentSniff)327 static CFURLRequestRef makeFinalRequest(const ResourceRequest& request, bool shouldContentSniff)
328 {
329     CFMutableURLRequestRef newRequest = CFURLRequestCreateMutableCopy(kCFAllocatorDefault, request.cfURLRequest());
330 
331     if (!shouldContentSniff)
332         wkSetCFURLRequestShouldContentSniff(newRequest, false);
333 
334     RetainPtr<CFMutableDictionaryRef> sslProps;
335 
336     if (allowsAnyHTTPSCertificateHosts().contains(request.url().host().lower())) {
337         sslProps.adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
338         CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsAnyRoot, kCFBooleanTrue);
339         CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsExpiredRoots, kCFBooleanTrue);
340         CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsExpiredCertificates, kCFBooleanTrue);
341         CFDictionaryAddValue(sslProps.get(), kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse);
342     }
343 
344     HashMap<String, RetainPtr<CFDataRef> >::iterator clientCert = clientCerts().find(request.url().host().lower());
345     if (clientCert != clientCerts().end()) {
346         if (!sslProps)
347             sslProps.adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
348         wkSetClientCertificateInSSLProperties(sslProps.get(), (clientCert->second).get());
349     }
350 
351     if (sslProps)
352         CFURLRequestSetSSLProperties(newRequest, sslProps.get());
353 
354     if (CFHTTPCookieStorageRef cookieStorage = currentCookieStorage()) {
355         CFURLRequestSetHTTPCookieStorage(newRequest, cookieStorage);
356         CFURLRequestSetHTTPCookieStorageAcceptPolicy(newRequest, CFHTTPCookieStorageGetCookieAcceptPolicy(cookieStorage));
357     }
358 
359     return newRequest;
360 }
361 
start(Frame * frame)362 bool ResourceHandle::start(Frame* frame)
363 {
364     // If we are no longer attached to a Page, this must be an attempted load from an
365     // onUnload handler, so let's just block it.
366     if (!frame->page())
367         return false;
368 
369     if ((!d->m_user.isEmpty() || !d->m_pass.isEmpty()) && !d->m_request.url().protocolInHTTPFamily()) {
370         // Credentials for ftp can only be passed in URL, the didReceiveAuthenticationChallenge delegate call won't be made.
371         KURL urlWithCredentials(d->m_request.url());
372         urlWithCredentials.setUser(d->m_user);
373         urlWithCredentials.setPass(d->m_pass);
374         d->m_request.setURL(urlWithCredentials);
375     }
376 
377     RetainPtr<CFURLRequestRef> request(AdoptCF, makeFinalRequest(d->m_request, d->m_shouldContentSniff));
378 
379     CFURLConnectionClient_V3 client = { 3, this, 0, 0, 0, WebCore::willSendRequest, didReceiveResponse, didReceiveData, NULL, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge, didSendBodyData, shouldUseCredentialStorageCallback, 0};
380 
381     d->m_connection.adoptCF(CFURLConnectionCreate(0, request.get(), reinterpret_cast<CFURLConnectionClient*>(&client)));
382 
383     CFURLConnectionScheduleWithCurrentMessageQueue(d->m_connection.get());
384     CFURLConnectionScheduleDownloadWithRunLoop(d->m_connection.get(), loaderRunLoop(), kCFRunLoopDefaultMode);
385     CFURLConnectionStart(d->m_connection.get());
386 
387     LOG(Network, "CFNet - Starting URL %s (handle=%p, conn=%p)", d->m_request.url().string().utf8().data(), this, d->m_connection);
388 
389     return true;
390 }
391 
cancel()392 void ResourceHandle::cancel()
393 {
394     if (d->m_connection) {
395         CFURLConnectionCancel(d->m_connection.get());
396         d->m_connection = 0;
397     }
398 }
399 
bufferedData()400 PassRefPtr<SharedBuffer> ResourceHandle::bufferedData()
401 {
402     ASSERT_NOT_REACHED();
403     return 0;
404 }
405 
supportsBufferedData()406 bool ResourceHandle::supportsBufferedData()
407 {
408     return false;
409 }
410 
willSendRequest(ResourceRequest & request,const ResourceResponse & redirectResponse)411 void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
412 {
413     const KURL& url = request.url();
414     d->m_user = url.user();
415     d->m_pass = url.pass();
416     request.removeCredentials();
417 
418     client()->willSendRequest(this, request, redirectResponse);
419 }
420 
shouldUseCredentialStorage()421 bool ResourceHandle::shouldUseCredentialStorage()
422 {
423     LOG(Network, "CFNet - shouldUseCredentialStorage()");
424     if (client())
425         return client()->shouldUseCredentialStorage(this);
426 
427     return false;
428 }
429 
didReceiveAuthenticationChallenge(const AuthenticationChallenge & challenge)430 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
431 {
432     LOG(Network, "CFNet - didReceiveAuthenticationChallenge()");
433     ASSERT(!d->m_currentCFChallenge);
434     ASSERT(d->m_currentWebChallenge.isNull());
435     // Since CFURLConnection networking relies on keeping a reference to the original CFURLAuthChallengeRef,
436     // we make sure that is actually present
437     ASSERT(challenge.cfURLAuthChallengeRef());
438 
439     if (!d->m_user.isNull() && !d->m_pass.isNull()) {
440         RetainPtr<CFStringRef> user(AdoptCF, d->m_user.createCFString());
441         RetainPtr<CFStringRef> pass(AdoptCF, d->m_pass.createCFString());
442         RetainPtr<CFURLCredentialRef> credential(AdoptCF,
443             CFURLCredentialCreate(kCFAllocatorDefault, user.get(), pass.get(), 0, kCFURLCredentialPersistenceNone));
444         WebCoreCredentialStorage::set(CFURLAuthChallengeGetProtectionSpace(challenge.cfURLAuthChallengeRef()), credential.get());
445         CFURLConnectionUseCredential(d->m_connection.get(), credential.get(), challenge.cfURLAuthChallengeRef());
446         d->m_user = String();
447         d->m_pass = String();
448         // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
449         return;
450     }
451 
452     if (!challenge.previousFailureCount() && (!client() || client()->shouldUseCredentialStorage(this))) {
453         CFURLCredentialRef credential = WebCoreCredentialStorage::get(CFURLAuthChallengeGetProtectionSpace(challenge.cfURLAuthChallengeRef()));
454         if (credential) {
455             ASSERT(CFURLCredentialGetPersistence(credential) == kCFURLCredentialPersistenceNone);
456             CFURLConnectionUseCredential(d->m_connection.get(), credential, challenge.cfURLAuthChallengeRef());
457             return;
458         }
459     }
460 
461     d->m_currentCFChallenge = challenge.cfURLAuthChallengeRef();
462     d->m_currentWebChallenge = AuthenticationChallenge(d->m_currentCFChallenge, this);
463 
464     if (client())
465         client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
466 }
467 
receivedCredential(const AuthenticationChallenge & challenge,const Credential & credential)468 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
469 {
470     LOG(Network, "CFNet - receivedCredential()");
471     ASSERT(!challenge.isNull());
472     ASSERT(challenge.cfURLAuthChallengeRef());
473     if (challenge != d->m_currentWebChallenge)
474         return;
475 
476     if (credential.persistence() == CredentialPersistenceForSession) {
477         // Manage per-session credentials internally, because once NSURLCredentialPersistencePerSession is used, there is no way
478         // to ignore it for a particular request (short of removing it altogether).
479         Credential webCredential(credential.user(), credential.password(), CredentialPersistenceNone);
480         RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(webCredential));
481         WebCoreCredentialStorage::set(CFURLAuthChallengeGetProtectionSpace(challenge.cfURLAuthChallengeRef()), cfCredential.get());
482         CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
483     } else {
484         RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(credential));
485         CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
486     }
487 
488     clearAuthentication();
489 }
490 
receivedRequestToContinueWithoutCredential(const AuthenticationChallenge & challenge)491 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
492 {
493     LOG(Network, "CFNet - receivedRequestToContinueWithoutCredential()");
494     ASSERT(!challenge.isNull());
495     ASSERT(challenge.cfURLAuthChallengeRef());
496     if (challenge != d->m_currentWebChallenge)
497         return;
498 
499     CFURLConnectionUseCredential(d->m_connection.get(), 0, challenge.cfURLAuthChallengeRef());
500 
501     clearAuthentication();
502 }
503 
receivedCancellation(const AuthenticationChallenge & challenge)504 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
505 {
506     LOG(Network, "CFNet - receivedCancellation()");
507     if (challenge != d->m_currentWebChallenge)
508         return;
509 
510     if (client())
511         client()->receivedCancellation(this, challenge);
512 }
513 
connection() const514 CFURLConnectionRef ResourceHandle::connection() const
515 {
516     return d->m_connection.get();
517 }
518 
releaseConnectionForDownload()519 CFURLConnectionRef ResourceHandle::releaseConnectionForDownload()
520 {
521     LOG(Network, "CFNet - Job %p releasing connection %p for download", this, d->m_connection.get());
522     return d->m_connection.releaseRef();
523 }
524 
loadResourceSynchronously(const ResourceRequest & request,StoredCredentials storedCredentials,ResourceError & error,ResourceResponse & response,Vector<char> & vector,Frame *)525 void ResourceHandle::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& vector, Frame*)
526 {
527     ASSERT(!request.isEmpty());
528 
529     RetainPtr<CFDataRef> data = WebCoreSynchronousLoader::load(request, storedCredentials, response, error);
530 
531     if (!error.isNull()) {
532         response = ResourceResponse(request.url(), String(), 0, String(), String());
533 
534         CFErrorRef cfError = error;
535         CFStringRef domain = CFErrorGetDomain(cfError);
536         // FIXME: Return the actual response for failed authentication.
537         if (domain == kCFErrorDomainCFNetwork)
538             response.setHTTPStatusCode(CFErrorGetCode(cfError));
539         else
540             response.setHTTPStatusCode(404);
541     }
542 
543     if (data) {
544         ASSERT(vector.isEmpty());
545         vector.append(CFDataGetBytePtr(data.get()), CFDataGetLength(data.get()));
546     }
547 }
548 
setHostAllowsAnyHTTPSCertificate(const String & host)549 void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
550 {
551     allowsAnyHTTPSCertificateHosts().add(host.lower());
552 }
553 
setClientCertificate(const String & host,CFDataRef cert)554 void ResourceHandle::setClientCertificate(const String& host, CFDataRef cert)
555 {
556     clientCerts().set(host.lower(), cert);
557 }
558 
setDefersLoading(bool defers)559 void ResourceHandle::setDefersLoading(bool defers)
560 {
561     if (!d->m_connection)
562         return;
563 
564     if (defers)
565         CFURLConnectionHalt(d->m_connection.get());
566     else
567         CFURLConnectionResume(d->m_connection.get());
568 }
569 
loadsBlocked()570 bool ResourceHandle::loadsBlocked()
571 {
572     return false;
573 }
574 
willLoadFromCache(ResourceRequest & request,Frame * frame)575 bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame* frame)
576 {
577     request.setCachePolicy(ReturnCacheDataDontLoad);
578 
579     CFURLResponseRef cfResponse = 0;
580     CFErrorRef cfError = 0;
581     RetainPtr<CFURLRequestRef> cfRequest(AdoptCF, makeFinalRequest(request, true));
582     RetainPtr<CFDataRef> data(AdoptCF, CFURLConnectionSendSynchronousRequest(cfRequest.get(), &cfResponse, &cfError, request.timeoutInterval()));
583     bool cached = cfResponse && !cfError;
584 
585     if (cfError)
586         CFRelease(cfError);
587     if (cfResponse)
588         CFRelease(cfResponse);
589 
590     return cached;
591 }
592 
willSendRequest(CFURLConnectionRef,CFURLRequestRef cfRequest,CFURLResponseRef cfRedirectResponse,const void * clientInfo)593 CFURLRequestRef WebCoreSynchronousLoader::willSendRequest(CFURLConnectionRef, CFURLRequestRef cfRequest, CFURLResponseRef cfRedirectResponse, const void* clientInfo)
594 {
595     WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
596 
597     // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests.
598     if (loader->m_url && !protocolHostAndPortAreEqual(loader->m_url.get(), CFURLRequestGetURL(cfRequest))) {
599         RetainPtr<CFErrorRef> cfError(AdoptCF, CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainCFNetwork, kCFURLErrorBadServerResponse, 0));
600         loader->m_error = cfError.get();
601         loader->m_isDone = true;
602         return 0;
603     }
604 
605     loader->m_url = CFURLRequestGetURL(cfRequest);
606 
607     if (cfRedirectResponse) {
608         // Take user/pass out of the URL.
609         loader->m_user.adoptCF(CFURLCopyUserName(loader->m_url.get()));
610         loader->m_pass.adoptCF(CFURLCopyPassword(loader->m_url.get()));
611         if (loader->m_user || loader->m_pass) {
612             ResourceRequest requestWithoutCredentials = cfRequest;
613             requestWithoutCredentials.removeCredentials();
614             cfRequest = requestWithoutCredentials.cfURLRequest();
615         }
616     }
617 
618     CFRetain(cfRequest);
619     return cfRequest;
620 }
621 
didReceiveResponse(CFURLConnectionRef,CFURLResponseRef cfResponse,const void * clientInfo)622 void WebCoreSynchronousLoader::didReceiveResponse(CFURLConnectionRef, CFURLResponseRef cfResponse, const void* clientInfo)
623 {
624     WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
625 
626     loader->m_response = cfResponse;
627 }
628 
didReceiveData(CFURLConnectionRef,CFDataRef data,CFIndex originalLength,const void * clientInfo)629 void WebCoreSynchronousLoader::didReceiveData(CFURLConnectionRef, CFDataRef data, CFIndex originalLength, const void* clientInfo)
630 {
631     WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
632 
633     if (!loader->m_data)
634         loader->m_data.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0));
635 
636     const UInt8* bytes = CFDataGetBytePtr(data);
637     CFIndex length = CFDataGetLength(data);
638 
639     CFDataAppendBytes(loader->m_data.get(), bytes, length);
640 }
641 
didFinishLoading(CFURLConnectionRef,const void * clientInfo)642 void WebCoreSynchronousLoader::didFinishLoading(CFURLConnectionRef, const void* clientInfo)
643 {
644     WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
645 
646     loader->m_isDone = true;
647 }
648 
didFail(CFURLConnectionRef,CFErrorRef error,const void * clientInfo)649 void WebCoreSynchronousLoader::didFail(CFURLConnectionRef, CFErrorRef error, const void* clientInfo)
650 {
651     WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
652 
653     loader->m_error = error;
654     loader->m_isDone = true;
655 }
656 
didReceiveChallenge(CFURLConnectionRef conn,CFURLAuthChallengeRef challenge,const void * clientInfo)657 void WebCoreSynchronousLoader::didReceiveChallenge(CFURLConnectionRef conn, CFURLAuthChallengeRef challenge, const void* clientInfo)
658 {
659     WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
660 
661     if (loader->m_user && loader->m_pass) {
662         RetainPtr<CFURLCredentialRef> credential(AdoptCF,
663             CFURLCredentialCreate(kCFAllocatorDefault, loader->m_user.get(), loader->m_pass.get(), 0, kCFURLCredentialPersistenceNone));
664         WebCoreCredentialStorage::set(CFURLAuthChallengeGetProtectionSpace(challenge), credential.get());
665         CFURLConnectionUseCredential(conn, credential.get(), challenge);
666         loader->m_user = 0;
667         loader->m_pass = 0;
668         return;
669     }
670     if (!CFURLAuthChallengeGetPreviousFailureCount(challenge) && loader->m_allowStoredCredentials) {
671         CFURLCredentialRef credential = WebCoreCredentialStorage::get(CFURLAuthChallengeGetProtectionSpace(challenge));
672         if (credential) {
673             ASSERT(CFURLCredentialGetPersistence(credential) == kCFURLCredentialPersistenceNone);
674             CFURLConnectionUseCredential(conn, credential, challenge);
675             return;
676         }
677     }
678     // FIXME: The user should be asked for credentials, as in async case.
679     CFURLConnectionUseCredential(conn, 0, challenge);
680 }
681 
shouldUseCredentialStorage(CFURLConnectionRef,const void * clientInfo)682 Boolean WebCoreSynchronousLoader::shouldUseCredentialStorage(CFURLConnectionRef, const void* clientInfo)
683 {
684     WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
685 
686     // FIXME: We should ask FrameLoaderClient whether using credential storage is globally forbidden.
687     return loader->m_allowStoredCredentials;
688 }
689 
load(const ResourceRequest & request,StoredCredentials storedCredentials,ResourceResponse & response,ResourceError & error)690 RetainPtr<CFDataRef> WebCoreSynchronousLoader::load(const ResourceRequest& request, StoredCredentials storedCredentials, ResourceResponse& response, ResourceError& error)
691 {
692     ASSERT(response.isNull());
693     ASSERT(error.isNull());
694 
695     WebCoreSynchronousLoader loader(response, error);
696 
697     KURL url = request.url();
698 
699     loader.m_user.adoptCF(url.user().createCFString());
700     loader.m_pass.adoptCF(url.pass().createCFString());
701     loader.m_allowStoredCredentials = (storedCredentials == AllowStoredCredentials);
702 
703     // Take user/pass out of the URL.
704     // Credentials for ftp can only be passed in URL, the didReceiveAuthenticationChallenge delegate call won't be made.
705     RetainPtr<CFURLRequestRef> cfRequest;
706     if ((loader.m_user || loader.m_pass) && url.protocolInHTTPFamily()) {
707         ResourceRequest requestWithoutCredentials(request);
708         requestWithoutCredentials.removeCredentials();
709         cfRequest.adoptCF(makeFinalRequest(requestWithoutCredentials, ResourceHandle::shouldContentSniffURL(requestWithoutCredentials.url())));
710     } else
711         cfRequest.adoptCF(makeFinalRequest(request, ResourceHandle::shouldContentSniffURL(request.url())));
712 
713     CFURLConnectionClient_V3 client = { 3, &loader, 0, 0, 0, willSendRequest, didReceiveResponse, didReceiveData, 0, didFinishLoading, didFail, 0, didReceiveChallenge, 0, shouldUseCredentialStorage, 0 };
714     RetainPtr<CFURLConnectionRef> connection(AdoptCF, CFURLConnectionCreate(kCFAllocatorDefault, cfRequest.get(), reinterpret_cast<CFURLConnectionClient*>(&client)));
715 
716     CFURLConnectionScheduleWithRunLoop(connection.get(), CFRunLoopGetCurrent(), WebCoreSynchronousLoaderRunLoopMode);
717     CFURLConnectionScheduleDownloadWithRunLoop(connection.get(), CFRunLoopGetCurrent(), WebCoreSynchronousLoaderRunLoopMode);
718     CFURLConnectionStart(connection.get());
719 
720     while (!loader.m_isDone)
721         CFRunLoopRunInMode(WebCoreSynchronousLoaderRunLoopMode, UINT_MAX, true);
722 
723     CFURLConnectionCancel(connection.get());
724 
725     if (error.isNull() && loader.m_response.mimeType().isNull())
726         setDefaultMIMEType(loader.m_response.cfURLResponse());
727 
728     return loader.m_data;
729 }
730 
731 } // namespace WebCore
732