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