]> git.saurik.com Git - iphone-api.git/blob - WebCore/ResourceHandleCFNet.cpp
Add support for new WinterBoard Settings features.
[iphone-api.git] / WebCore / ResourceHandleCFNet.cpp
1 /*
2 * Copyright (C) 2004, 2005, 2006, 2007 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 "NotImplemented.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 HMODULE findCFNetworkModule()
57 {
58 if (HMODULE module = GetModuleHandleA("CFNetwork"))
59 return module;
60 return GetModuleHandleA("CFNetwork_debug");
61 }
62
63 static DWORD cfNetworkVersion()
64 {
65 HMODULE cfNetworkModule = findCFNetworkModule();
66 WCHAR filename[MAX_PATH];
67 GetModuleFileName(cfNetworkModule, filename, MAX_PATH);
68 DWORD handle;
69 DWORD versionInfoSize = GetFileVersionInfoSize(filename, &handle);
70 Vector<BYTE> versionInfo(versionInfoSize);
71 GetFileVersionInfo(filename, handle, versionInfoSize, versionInfo.data());
72 VS_FIXEDFILEINFO* fixedFileInfo;
73 UINT fixedFileInfoLength;
74 VerQueryValue(versionInfo.data(), TEXT("\\"), reinterpret_cast<LPVOID*>(&fixedFileInfo), &fixedFileInfoLength);
75 return fixedFileInfo->dwProductVersionMS;
76 }
77
78 static CFIndex highestSupportedCFURLConnectionClientVersion()
79 {
80 const DWORD firstCFNetworkVersionWithConnectionClientV2 = 0x000101a8; // 1.424
81 const DWORD firstCFNetworkVersionWithConnectionClientV3 = 0x000101ad; // 1.429
82
83 #ifndef _CFURLConnectionClientV2Present
84 return 1;
85 #else
86
87 DWORD version = cfNetworkVersion();
88 if (version < firstCFNetworkVersionWithConnectionClientV2)
89 return 1;
90 #ifndef _CFURLConnectionClientV3Present
91 return 2;
92 #else
93
94 if (version < firstCFNetworkVersionWithConnectionClientV3)
95 return 2;
96 return 3;
97 #endif // _CFURLConnectionClientV3Present
98 #endif // _CFURLConnectionClientV2Present
99 }
100
101 static CFStringRef WebCoreSynchronousLoaderRunLoopMode = CFSTR("WebCoreSynchronousLoaderRunLoopMode");
102
103 class WebCoreSynchronousLoader {
104 public:
105 static RetainPtr<CFDataRef> load(const ResourceRequest&, ResourceResponse&, ResourceError&);
106
107 private:
108 WebCoreSynchronousLoader(ResourceResponse& response, ResourceError& error)
109 : m_isDone(false)
110 , m_response(response)
111 , m_error(error)
112 {
113 }
114
115 static CFURLRequestRef willSendRequest(CFURLConnectionRef, CFURLRequestRef, CFURLResponseRef, const void* clientInfo);
116 static void didReceiveResponse(CFURLConnectionRef, CFURLResponseRef, const void* clientInfo);
117 static void didReceiveData(CFURLConnectionRef, CFDataRef, CFIndex, const void* clientInfo);
118 static void didFinishLoading(CFURLConnectionRef, const void* clientInfo);
119 static void didFail(CFURLConnectionRef, CFErrorRef, const void* clientInfo);
120 static void didReceiveChallenge(CFURLConnectionRef, CFURLAuthChallengeRef, const void* clientInfo);
121
122 bool m_isDone;
123 RetainPtr<CFURLRef> m_url;
124 ResourceResponse& m_response;
125 RetainPtr<CFMutableDataRef> m_data;
126 ResourceError& m_error;
127 };
128
129 static HashSet<String>& allowsAnyHTTPSCertificateHosts()
130 {
131 static HashSet<String> hosts;
132
133 return hosts;
134 }
135
136 static HashMap<String, RetainPtr<CFDataRef> >& clientCerts()
137 {
138 static HashMap<String, RetainPtr<CFDataRef> > certs;
139 return certs;
140 }
141
142 CFURLRequestRef willSendRequest(CFURLConnectionRef conn, CFURLRequestRef cfRequest, CFURLResponseRef cfRedirectResponse, const void* clientInfo)
143 {
144 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
145
146 if (!cfRedirectResponse) {
147 CFRetain(cfRequest);
148 return cfRequest;
149 }
150
151 LOG(Network, "CFNet - willSendRequest(conn=%p, handle=%p) (%s)", conn, handle, handle->request().url().string().utf8().data());
152
153 ResourceRequest request(cfRequest);
154 if (handle->client())
155 handle->client()->willSendRequest(handle, request, cfRedirectResponse);
156
157 cfRequest = request.cfURLRequest();
158
159 CFRetain(cfRequest);
160 return cfRequest;
161 }
162
163 void didReceiveResponse(CFURLConnectionRef conn, CFURLResponseRef cfResponse, const void* clientInfo)
164 {
165 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
166
167 LOG(Network, "CFNet - didReceiveResponse(conn=%p, handle=%p) (%s)", conn, handle, handle->request().url().string().utf8().data());
168
169 if (handle->client())
170 handle->client()->didReceiveResponse(handle, cfResponse);
171 }
172
173 void didReceiveData(CFURLConnectionRef conn, CFDataRef data, CFIndex originalLength, const void* clientInfo)
174 {
175 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
176 const UInt8* bytes = CFDataGetBytePtr(data);
177 CFIndex length = CFDataGetLength(data);
178
179 LOG(Network, "CFNet - didReceiveData(conn=%p, handle=%p, bytes=%d) (%s)", conn, handle, length, handle->request().url().string().utf8().data());
180
181 if (handle->client())
182 handle->client()->didReceiveData(handle, (const char*)bytes, length, originalLength);
183 }
184
185 #ifdef _CFURLConnectionClientV2Present
186 static void didSendBodyData(CFURLConnectionRef conn, CFIndex bytesWritten, CFIndex totalBytesWritten, CFIndex totalBytesExpectedToWrite, const void *clientInfo)
187 {
188 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
189 if (!handle || !handle->client())
190 return;
191 handle->client()->didSendData(handle, totalBytesWritten, totalBytesExpectedToWrite);
192 }
193 #endif
194
195 #ifdef _CFURLConnectionClientV3Present
196 static Boolean shouldUseCredentialStorageCallback(CFURLConnectionRef conn, const void* clientInfo)
197 {
198 ResourceHandle* handle = const_cast<ResourceHandle*>(static_cast<const ResourceHandle*>(clientInfo));
199
200 LOG(Network, "CFNet - shouldUseCredentialStorage(conn=%p, handle=%p) (%s)", conn, handle, handle->request().url().string().utf8().data());
201
202 if (!handle)
203 return false;
204
205 return handle->shouldUseCredentialStorage();
206 }
207 #endif
208
209 void didFinishLoading(CFURLConnectionRef conn, const void* clientInfo)
210 {
211 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
212
213 LOG(Network, "CFNet - didFinishLoading(conn=%p, handle=%p) (%s)", conn, handle, handle->request().url().string().utf8().data());
214
215 if (handle->client())
216 handle->client()->didFinishLoading(handle);
217 }
218
219 void didFail(CFURLConnectionRef conn, CFErrorRef error, const void* clientInfo)
220 {
221 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
222
223 LOG(Network, "CFNet - didFail(conn=%p, handle=%p, error = %p) (%s)", conn, handle, error, handle->request().url().string().utf8().data());
224
225 if (handle->client())
226 handle->client()->didFail(handle, ResourceError(error));
227 }
228
229 CFCachedURLResponseRef willCacheResponse(CFURLConnectionRef conn, CFCachedURLResponseRef cachedResponse, const void* clientInfo)
230 {
231 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
232
233 CacheStoragePolicy policy = static_cast<CacheStoragePolicy>(CFCachedURLResponseGetStoragePolicy(cachedResponse));
234
235 if (handle->client())
236 handle->client()->willCacheResponse(handle, policy);
237
238 if (static_cast<CFURLCacheStoragePolicy>(policy) != CFCachedURLResponseGetStoragePolicy(cachedResponse))
239 cachedResponse = CFCachedURLResponseCreateWithUserInfo(kCFAllocatorDefault,
240 CFCachedURLResponseGetWrappedResponse(cachedResponse),
241 CFCachedURLResponseGetReceiverData(cachedResponse),
242 CFCachedURLResponseGetUserInfo(cachedResponse),
243 static_cast<CFURLCacheStoragePolicy>(policy));
244 CFRetain(cachedResponse);
245
246 return cachedResponse;
247 }
248
249 void didReceiveChallenge(CFURLConnectionRef conn, CFURLAuthChallengeRef challenge, const void* clientInfo)
250 {
251 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
252 ASSERT(handle);
253 LOG(Network, "CFNet - didReceiveChallenge(conn=%p, handle=%p (%s)", conn, handle, handle->request().url().string().utf8().data());
254
255 handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(challenge, handle));
256 }
257
258 void addHeadersFromHashMap(CFMutableURLRequestRef request, const HTTPHeaderMap& requestHeaders)
259 {
260 if (!requestHeaders.size())
261 return;
262
263 HTTPHeaderMap::const_iterator end = requestHeaders.end();
264 for (HTTPHeaderMap::const_iterator it = requestHeaders.begin(); it != end; ++it) {
265 CFStringRef key = it->first.createCFString();
266 CFStringRef value = it->second.createCFString();
267 CFURLRequestSetHTTPHeaderFieldValue(request, key, value);
268 CFRelease(key);
269 CFRelease(value);
270 }
271 }
272
273 ResourceHandleInternal::~ResourceHandleInternal()
274 {
275 if (m_connection) {
276 LOG(Network, "CFNet - Cancelling connection %p (%s)", m_connection, m_request.url().string().utf8().data());
277 CFURLConnectionCancel(m_connection.get());
278 }
279 }
280
281 ResourceHandle::~ResourceHandle()
282 {
283 LOG(Network, "CFNet - Destroying job %p (%s)", this, d->m_request.url().string().utf8().data());
284 }
285
286 CFArrayRef arrayFromFormData(const FormData& d)
287 {
288 size_t size = d.elements().size();
289 CFMutableArrayRef a = CFArrayCreateMutable(0, d.elements().size(), &kCFTypeArrayCallBacks);
290 for (size_t i = 0; i < size; ++i) {
291 const FormDataElement& e = d.elements()[i];
292 if (e.m_type == FormDataElement::data) {
293 CFDataRef data = CFDataCreate(0, (const UInt8*)e.m_data.data(), e.m_data.size());
294 CFArrayAppendValue(a, data);
295 CFRelease(data);
296 } else {
297 ASSERT(e.m_type == FormDataElement::encodedFile);
298 CFStringRef filename = e.m_filename.createCFString();
299 CFArrayAppendValue(a, filename);
300 CFRelease(filename);
301 }
302 }
303 return a;
304 }
305
306 void emptyPerform(void* unused)
307 {
308 }
309
310 static CFRunLoopRef loaderRL = 0;
311 void* runLoaderThread(void *unused)
312 {
313 loaderRL = CFRunLoopGetCurrent();
314
315 // Must add a source to the run loop to prevent CFRunLoopRun() from exiting
316 CFRunLoopSourceContext ctxt = {0, (void *)1 /*must be non-NULL*/, 0, 0, 0, 0, 0, 0, 0, emptyPerform};
317 CFRunLoopSourceRef bogusSource = CFRunLoopSourceCreate(0, 0, &ctxt);
318 CFRunLoopAddSource(loaderRL, bogusSource,kCFRunLoopDefaultMode);
319
320 CFRunLoopRun();
321
322 return 0;
323 }
324
325 CFRunLoopRef ResourceHandle::loaderRunLoop()
326 {
327 if (!loaderRL) {
328 createThread(runLoaderThread, 0, "CFNetwork::Loader");
329 while (loaderRL == 0) {
330 // FIXME: sleep 10? that can't be right...
331 Sleep(10);
332 }
333 }
334 return loaderRL;
335 }
336
337 static CFURLRequestRef makeFinalRequest(const ResourceRequest& request, bool shouldContentSniff)
338 {
339 CFMutableURLRequestRef newRequest = CFURLRequestCreateMutableCopy(kCFAllocatorDefault, request.cfURLRequest());
340
341 if (!shouldContentSniff)
342 wkSetCFURLRequestShouldContentSniff(newRequest, false);
343
344 RetainPtr<CFMutableDictionaryRef> sslProps;
345
346 if (allowsAnyHTTPSCertificateHosts().contains(request.url().host().lower())) {
347 sslProps.adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
348 CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsAnyRoot, kCFBooleanTrue);
349 CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsExpiredRoots, kCFBooleanTrue);
350 }
351
352 HashMap<String, RetainPtr<CFDataRef> >::iterator clientCert = clientCerts().find(request.url().host().lower());
353 if (clientCert != clientCerts().end()) {
354 if (!sslProps)
355 sslProps.adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
356 wkSetClientCertificateInSSLProperties(sslProps.get(), (clientCert->second).get());
357 }
358
359 if (sslProps)
360 CFURLRequestSetSSLProperties(newRequest, sslProps.get());
361
362 if (CFHTTPCookieStorageRef cookieStorage = currentCookieStorage()) {
363 CFURLRequestSetHTTPCookieStorage(newRequest, cookieStorage);
364 CFURLRequestSetHTTPCookieStorageAcceptPolicy(newRequest, CFHTTPCookieStorageGetCookieAcceptPolicy(cookieStorage));
365 }
366
367 return newRequest;
368 }
369
370 bool ResourceHandle::start(Frame* frame)
371 {
372 // If we are no longer attached to a Page, this must be an attempted load from an
373 // onUnload handler, so let's just block it.
374 if (!frame->page())
375 return false;
376
377 RetainPtr<CFURLRequestRef> request(AdoptCF, makeFinalRequest(d->m_request, d->m_shouldContentSniff));
378
379 static CFIndex clientVersion = highestSupportedCFURLConnectionClientVersion();
380 CFURLConnectionClient* client;
381 #if defined(_CFURLConnectionClientV3Present)
382 CFURLConnectionClient_V3 client_V3 = {clientVersion, this, 0, 0, 0, willSendRequest, didReceiveResponse, didReceiveData, NULL, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge, didSendBodyData, shouldUseCredentialStorageCallback, 0};
383 client = reinterpret_cast<CFURLConnectionClient*>(&client_V3);
384 #elif defined(_CFURLConnectionClientV2Present)
385 CFURLConnectionClient_V2 client_V2 = {clientVersion, this, 0, 0, 0, willSendRequest, didReceiveResponse, didReceiveData, NULL, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge, didSendBodyData};
386 client = reinterpret_cast<CFURLConnectionClient*>(&client_V2);
387 #else
388 CFURLConnectionClient client_V1 = {1, this, 0, 0, 0, willSendRequest, didReceiveResponse, didReceiveData, NULL, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge};
389 client = &client_V1;
390 #endif
391
392 d->m_connection.adoptCF(CFURLConnectionCreate(0, request.get(), client));
393
394 CFURLConnectionScheduleWithCurrentMessageQueue(d->m_connection.get());
395 CFURLConnectionScheduleDownloadWithRunLoop(d->m_connection.get(), loaderRunLoop(), kCFRunLoopDefaultMode);
396 CFURLConnectionStart(d->m_connection.get());
397
398 LOG(Network, "CFNet - Starting URL %s (handle=%p, conn=%p)", d->m_request.url().string().utf8().data(), this, d->m_connection);
399
400 return true;
401 }
402
403 void ResourceHandle::cancel()
404 {
405 if (d->m_connection) {
406 CFURLConnectionCancel(d->m_connection.get());
407 d->m_connection = 0;
408 }
409 }
410
411 PassRefPtr<SharedBuffer> ResourceHandle::bufferedData()
412 {
413 ASSERT_NOT_REACHED();
414 return 0;
415 }
416
417 bool ResourceHandle::supportsBufferedData()
418 {
419 return false;
420 }
421
422 bool ResourceHandle::shouldUseCredentialStorage()
423 {
424 LOG(Network, "CFNet - shouldUseCredentialStorage()");
425 if (client())
426 return client()->shouldUseCredentialStorage(this);
427
428 return false;
429 }
430
431 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
432 {
433 LOG(Network, "CFNet - didReceiveAuthenticationChallenge()");
434 ASSERT(!d->m_currentCFChallenge);
435 ASSERT(d->m_currentWebChallenge.isNull());
436 // Since CFURLConnection networking relies on keeping a reference to the original CFURLAuthChallengeRef,
437 // we make sure that is actually present
438 ASSERT(challenge.cfURLAuthChallengeRef());
439
440 d->m_currentCFChallenge = challenge.cfURLAuthChallengeRef();
441 d->m_currentWebChallenge = AuthenticationChallenge(d->m_currentCFChallenge, this);
442
443 if (client())
444 client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
445 }
446
447 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
448 {
449 LOG(Network, "CFNet - receivedCredential()");
450 ASSERT(!challenge.isNull());
451 ASSERT(challenge.cfURLAuthChallengeRef());
452 if (challenge != d->m_currentWebChallenge)
453 return;
454
455 CFURLCredentialRef cfCredential = createCF(credential);
456 CFURLConnectionUseCredential(d->m_connection.get(), cfCredential, challenge.cfURLAuthChallengeRef());
457 CFRelease(cfCredential);
458
459 clearAuthentication();
460 }
461
462 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
463 {
464 LOG(Network, "CFNet - receivedRequestToContinueWithoutCredential()");
465 ASSERT(!challenge.isNull());
466 ASSERT(challenge.cfURLAuthChallengeRef());
467 if (challenge != d->m_currentWebChallenge)
468 return;
469
470 CFURLConnectionUseCredential(d->m_connection.get(), 0, challenge.cfURLAuthChallengeRef());
471
472 clearAuthentication();
473 }
474
475 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
476 {
477 LOG(Network, "CFNet - receivedCancellation()");
478 if (challenge != d->m_currentWebChallenge)
479 return;
480
481 if (client())
482 client()->receivedCancellation(this, challenge);
483 }
484
485 CFURLConnectionRef ResourceHandle::connection() const
486 {
487 return d->m_connection.get();
488 }
489
490 CFURLConnectionRef ResourceHandle::releaseConnectionForDownload()
491 {
492 LOG(Network, "CFNet - Job %p releasing connection %p for download", this, d->m_connection.get());
493 return d->m_connection.releaseRef();
494 }
495
496 void ResourceHandle::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& vector, Frame*)
497 {
498 ASSERT(!request.isEmpty());
499
500 RetainPtr<CFDataRef> data = WebCoreSynchronousLoader::load(request, response, error);
501
502 if (!error.isNull()) {
503 // FIXME: Return the actual response for failed authentication.
504 response = ResourceResponse(request.url(), String(), 0, String(), String());
505 response.setHTTPStatusCode(404);
506 }
507
508 if (data) {
509 ASSERT(vector.isEmpty());
510 vector.append(CFDataGetBytePtr(data.get()), CFDataGetLength(data.get()));
511 }
512 }
513
514 void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
515 {
516 allowsAnyHTTPSCertificateHosts().add(host.lower());
517 }
518
519 void ResourceHandle::setClientCertificate(const String& host, CFDataRef cert)
520 {
521 clientCerts().set(host.lower(), cert);
522 }
523
524 void ResourceHandle::setDefersLoading(bool defers)
525 {
526 if (!d->m_connection)
527 return;
528
529 if (defers)
530 CFURLConnectionHalt(d->m_connection.get());
531 else
532 CFURLConnectionResume(d->m_connection.get());
533 }
534
535 bool ResourceHandle::loadsBlocked()
536 {
537 return false;
538 }
539
540 bool ResourceHandle::willLoadFromCache(ResourceRequest&)
541 {
542 // Not having this function means that we'll ask the user about re-posting a form
543 // even when we go back to a page that's still in the cache.
544 notImplemented();
545 return false;
546 }
547
548 CFURLRequestRef WebCoreSynchronousLoader::willSendRequest(CFURLConnectionRef, CFURLRequestRef cfRequest, CFURLResponseRef cfRedirectResponse, const void* clientInfo)
549 {
550 WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
551
552 // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests.
553 if (loader->m_url && !protocolHostAndPortAreEqual(loader->m_url.get(), CFURLRequestGetURL(cfRequest))) {
554 RetainPtr<CFErrorRef> cfError(AdoptCF, CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainCFNetwork, kCFURLErrorBadServerResponse, 0));
555 loader->m_error = cfError.get();
556 loader->m_isDone = true;
557 return 0;
558 }
559
560 loader->m_url = CFURLRequestGetURL(cfRequest);
561
562 CFRetain(cfRequest);
563 return cfRequest;
564 }
565
566 void WebCoreSynchronousLoader::didReceiveResponse(CFURLConnectionRef, CFURLResponseRef cfResponse, const void* clientInfo)
567 {
568 WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
569
570 loader->m_response = cfResponse;
571 }
572
573 void WebCoreSynchronousLoader::didReceiveData(CFURLConnectionRef, CFDataRef data, CFIndex originalLength, const void* clientInfo)
574 {
575 WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
576
577 if (!loader->m_data)
578 loader->m_data.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, originalLength));
579
580 const UInt8* bytes = CFDataGetBytePtr(data);
581 CFIndex length = CFDataGetLength(data);
582
583 CFDataAppendBytes(loader->m_data.get(), bytes, length);
584 }
585
586 void WebCoreSynchronousLoader::didFinishLoading(CFURLConnectionRef, const void* clientInfo)
587 {
588 WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
589
590 loader->m_isDone = true;
591 }
592
593 void WebCoreSynchronousLoader::didFail(CFURLConnectionRef, CFErrorRef error, const void* clientInfo)
594 {
595 WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
596
597 loader->m_error = error;
598 loader->m_isDone = true;
599 }
600
601 void WebCoreSynchronousLoader::didReceiveChallenge(CFURLConnectionRef conn, CFURLAuthChallengeRef challenge, const void* clientInfo)
602 {
603 WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
604
605 // FIXME: Mac uses credentials from URL here, should we do the same?
606 CFURLConnectionUseCredential(conn, 0, challenge);
607 }
608
609 RetainPtr<CFDataRef> WebCoreSynchronousLoader::load(const ResourceRequest& request, ResourceResponse& response, ResourceError& error)
610 {
611 ASSERT(response.isNull());
612 ASSERT(error.isNull());
613
614 WebCoreSynchronousLoader loader(response, error);
615
616 RetainPtr<CFURLRequestRef> cfRequest(AdoptCF, makeFinalRequest(request, true));
617
618 CFURLConnectionClient_V3 client = { 3, &loader, 0, 0, 0, willSendRequest, didReceiveResponse, didReceiveData, 0, didFinishLoading, didFail, 0, didReceiveChallenge, 0, 0, 0 };
619
620 RetainPtr<CFURLConnectionRef> connection(AdoptCF, CFURLConnectionCreate(kCFAllocatorDefault, cfRequest.get(), reinterpret_cast<CFURLConnectionClient*>(&client)));
621
622 CFURLConnectionScheduleWithRunLoop(connection.get(), CFRunLoopGetCurrent(), WebCoreSynchronousLoaderRunLoopMode);
623 CFURLConnectionScheduleDownloadWithRunLoop(connection.get(), CFRunLoopGetCurrent(), WebCoreSynchronousLoaderRunLoopMode);
624 CFURLConnectionStart(connection.get());
625
626 while (!loader.m_isDone)
627 CFRunLoopRunInMode(WebCoreSynchronousLoaderRunLoopMode, UINT_MAX, true);
628
629 CFURLConnectionCancel(connection.get());
630
631 return loader.m_data;
632 }
633
634 } // namespace WebCore