2 * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
30 #include "DocLoader.h"
32 #include "ResourceHandle.h"
33 #include "ResourceHandleInternal.h"
34 #include "ResourceResponse.h"
35 #include "ResourceResponseCFNet.h"
37 #include <WTF/HashMap.h>
39 #include <sys/types.h>
41 #include <process.h> // for _beginthread()
43 #include <CFNetwork/CFNetwork.h>
44 #include <CFNetwork/CFNetworkPriv.h>
46 //#define LOG_RESOURCELOADER_EVENTS 1
50 CFURLRequestRef willSendRequest(CFURLConnectionRef conn, CFURLRequestRef request, CFURLResponseRef redirectionResponse, const void* clientInfo)
52 ResourceHandle* job = (ResourceHandle*)clientInfo;
53 CFURLRef url = CFURLRequestGetURL(request);
54 CFStringRef urlString = CFURLGetString(url);
55 const char *bytes = CFStringGetCStringPtr(urlString, kCFStringEncodingUTF8);
56 bool freeBytes = false;
58 #if defined(LOG_RESOURCELOADER_EVENTS)
59 CFStringRef str = CFStringCreateWithFormat(0, 0, CFSTR("willSendRequest(conn=%p, job = %p)\n"), conn, job);
65 CFIndex numBytes, urlLength = CFStringGetLength(urlString);
67 CFStringGetBytes(urlString, CFRangeMake(0, urlLength), kCFStringEncodingUTF8, 0, FALSE, 0, 0, &numBytes);
68 newBytes = (UInt8*)malloc(numBytes + 1);
69 CFStringGetBytes(urlString, CFRangeMake(0, urlLength), kCFStringEncodingUTF8, 0, FALSE, newBytes, numBytes, &numBytes);
70 newBytes[numBytes] = 0;
72 bytes = (char*)newBytes;
76 if (!(newURL == job->url()))
77 job->client()->receivedRedirect(job, newURL);
83 void didReceiveResponse(CFURLConnectionRef conn, CFURLResponseRef cfResponse, const void* clientInfo)
85 ResourceHandle* handle = (ResourceHandle*)clientInfo;
87 #if defined(LOG_RESOURCELOADER_EVENTS)
88 CFStringRef str = CFStringCreateWithFormat(0, 0, CFSTR("didReceiveResponse(conn=%p, job = %p)\n"), conn, handle);
93 if (ResourceHandleClient* client = handle->client()) {
94 client->receivedResponse(handle, cfResponse);
95 ResourceResponse response;
96 getResourceResponse(response, cfResponse);
97 client->didReceiveResponse(handle, response);
101 void didReceiveData(CFURLConnectionRef conn, CFDataRef data, CFIndex originalLength, const void* clientInfo)
103 ResourceHandle* job = (ResourceHandle*)clientInfo;
104 const UInt8* bytes = CFDataGetBytePtr(data);
105 CFIndex length = CFDataGetLength(data);
107 #if defined(LOG_RESOURCELOADER_EVENTS)
108 CFStringRef str = CFStringCreateWithFormat(0, 0, CFSTR("didReceiveData(conn=%p, job = %p, numBytes = %d)\n"), conn, job, length);
113 job->client()->didReceiveData(job, (const char*)bytes, length);
116 void didFinishLoading(CFURLConnectionRef conn, const void* clientInfo)
118 ResourceHandle* job = (ResourceHandle*)clientInfo;
120 #if defined(LOG_RESOURCELOADER_EVENTS)
121 CFStringRef str = CFStringCreateWithFormat(0, 0, CFSTR("didFinishLoading(conn=%p, job = %p)\n"), conn, job);
126 job->client()->receivedAllData(job, 0);
127 job->client()->didFinishLoading(job);
131 void didFail(CFURLConnectionRef conn, CFStreamError error, const void* clientInfo)
133 ResourceHandle* job = (ResourceHandle*)clientInfo;
135 #if defined(LOG_RESOURCELOADER_EVENTS)
136 CFStringRef str = CFStringCreateWithFormat(0, 0, CFSTR("didFail(conn=%p, job = %p, error = {%d, %d})\n"), conn, job, error.domain, error.error);
142 job->client()->receivedAllData(job, 0);
143 job->client()->didFinishLoading(job);
147 CFCachedURLResponseRef willCacheResponse(CFURLConnectionRef conn, CFCachedURLResponseRef cachedResponse, const void* clientInfo)
149 ResourceHandle* job = (ResourceHandle*)clientInfo;
150 return cachedResponse;
153 void didReceiveChallenge(CFURLConnectionRef conn, CFURLAuthChallengeRef challenge, const void* clientInfo)
155 ResourceHandle* job = (ResourceHandle*)clientInfo;
157 // Do nothing right now
160 void addHeadersFromHashMap(CFMutableURLRequestRef request, const HTTPHeaderMap& requestHeaders)
162 if (!requestHeaders.size())
165 HTTPHeaderMap::const_iterator end = requestHeaders.end();
166 for (HTTPHeaderMap::const_iterator it = requestHeaders.begin(); it != end; ++it) {
167 CFStringRef key = it->first.createCFString();
168 CFStringRef value = it->second.createCFString();
169 CFURLRequestSetHTTPHeaderFieldValue(request, key, value);
175 ResourceHandleInternal::~ResourceHandleInternal()
179 #if defined(LOG_RESOURCELOADER_EVENTS)
180 CFStringRef str = CFStringCreateWithFormat(0, 0, CFSTR("Cancelling connection %p\n"), m_connection);
184 CFURLConnectionCancel(m_connection);
185 CFRelease(m_connection);
190 ResourceHandle::~ResourceHandle()
192 #if defined(LOG_RESOURCELOADER_EVENTS)
193 CFStringRef str = CFStringCreateWithFormat(0, 0, CFSTR("Destroying job %p\n"), this);
200 CFArrayRef arrayFromFormData(const FormData& d)
202 size_t size = d.elements().size();
203 CFMutableArrayRef a = CFArrayCreateMutable(0, d.elements().size(), &kCFTypeArrayCallBacks);
204 for (size_t i = 0; i < size; ++i) {
205 const FormDataElement& e = d.elements()[i];
206 if (e.m_type == FormDataElement::data) {
207 CFDataRef data = CFDataCreate(0, (const UInt8*)e.m_data.data(), e.m_data.size());
208 CFArrayAppendValue(a, data);
211 ASSERT(e.m_type == FormDataElement::encodedFile);
212 CFStringRef filename = e.m_filename.createCFString();
213 CFArrayAppendValue(a, filename);
220 void emptyPerform(void* unused)
224 static CFRunLoopRef loaderRL = 0;
225 void runLoaderThread(void *unused)
227 loaderRL = CFRunLoopGetCurrent();
229 // Must add a source to the run loop to prevent CFRunLoopRun() from exiting
230 CFRunLoopSourceContext ctxt = {0, (void *)1 /*must be non-NULL*/, 0, 0, 0, 0, 0, 0, 0, emptyPerform};
231 CFRunLoopSourceRef bogusSource = CFRunLoopSourceCreate(0, 0, &ctxt);
232 CFRunLoopAddSource(loaderRL, bogusSource,kCFRunLoopDefaultMode);
237 bool ResourceHandle::start(DocLoader* docLoader)
239 CFURLRef url = d->m_request.url().createCFURL();
241 CFStringRef requestMethod = d->m_request.httpMethod().createCFString();
242 CFMutableURLRequestRef request = CFURLRequestCreateMutable(0, url, kCFURLRequestCachePolicyProtocolDefault, 30.0, 0);
243 Boolean isPost = CFStringCompare(requestMethod, CFSTR("POST"), kCFCompareCaseInsensitive);
244 CFRelease(requestMethod);
246 CFStringRef userAgentString = docLoader->frame()->userAgent().createCFString();
247 CFURLRequestSetHTTPHeaderFieldValue(request, CFSTR("User-Agent"), userAgentString);
248 CFRelease(userAgentString);
252 addHeadersFromHashMap(request, d->m_request.httpHeaderFields());
254 String referrer = docLoader->frame()->referrer();
255 if (!referrer.isEmpty() && referrer.find("file:", 0, false) != 0) {
256 CFStringRef str = referrer.createCFString();
257 CFURLRequestSetHTTPHeaderFieldValue(request, CFSTR("Referer"),str);
261 CFReadStreamRef bodyStream = 0;
262 if (postData().elements().size() > 0) {
263 CFArrayRef formArray = arrayFromFormData(postData());
265 CFIndex count = CFArrayGetCount(formArray);
268 // Handle the common special case of one piece of form data, with no files.
269 CFTypeRef d = CFArrayGetValueAtIndex(formArray, 0);
270 if (CFGetTypeID(d) == CFDataGetTypeID()) {
271 CFURLRequestSetHTTPRequestBody(request, (CFDataRef)d);
277 // Precompute the content length so NSURLConnection doesn't use chunked mode.
278 long long length = 0;
281 for (i = 0; success && i < count; ++i) {
282 CFTypeRef data = CFArrayGetValueAtIndex(formArray, i);
283 CFIndex typeID = CFGetTypeID(data);
284 if (typeID == CFDataGetTypeID()) {
285 CFDataRef d = (CFDataRef)data;
286 length += CFDataGetLength(d);
288 // data is a CFStringRef
289 CFStringRef s = (CFStringRef)data;
290 CFIndex bufLen = CFStringGetMaximumSizeOfFileSystemRepresentation(s);
291 char* buf = (char*)malloc(bufLen);
292 if (CFStringGetFileSystemRepresentation(s, buf, bufLen)) {
293 struct _stat64i32 sb;
294 int statResult = _stat(buf, &sb);
295 if (statResult == 0 && (sb.st_mode & S_IFMT) == S_IFREG)
296 length += sb.st_size;
306 CFStringRef lengthStr = CFStringCreateWithFormat(0, 0, CFSTR("%lld"), length);
307 CFURLRequestSetHTTPHeaderFieldValue(request, CFSTR("Content-Length"), lengthStr);
308 CFRelease(lengthStr);
310 bodyStream = CFReadStreamCreateWithFormArray(0, formArray);
312 CFRelease(formArray);
316 CFURLRequestSetHTTPRequestBodyStream(request, bodyStream);
317 CFURLConnectionClient client = {0, this, 0, 0, 0, willSendRequest, didReceiveResponse, didReceiveData, NULL, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge};
318 d->m_connection = CFURLConnectionCreate(0, request, &client);
322 _beginthread(runLoaderThread, 0, 0);
323 while (loaderRL == 0) {
328 CFURLConnectionScheduleWithCurrentMessageQueue(d->m_connection);
329 CFURLConnectionScheduleDownloadWithRunLoop(d->m_connection, loaderRL, kCFRunLoopDefaultMode);
330 CFURLConnectionStart(d->m_connection);
332 #if defined(LOG_RESOURCELOADER_EVENTS)
333 CFStringRef outStr = CFStringCreateWithFormat(0, 0, CFSTR("Starting URL %@ (job = %p, connection = %p)\n"), CFURLGetString(url), this, d->m_connection);
341 void ResourceHandle::cancel()
343 if (d->m_connection) {
344 CFURLConnectionCancel(d->m_connection);
345 CFRelease(d->m_connection);
349 // Copied directly from ResourceHandleWin.cpp
351 d->m_client->receivedAllData(this, 0);
352 d->m_client->didFinishLoading(this);
355 } // namespace WebCore
357 #endif // USE(CFNETWORK)