Build fix.
[WebKit-https.git] / WebCore / platform / network / cf / ResourceHandleCFNet.cpp
1 /*
2  * Copyright (C) 2004, 2006 Apple Computer, 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 #if USE(CFNETWORK)
29
30 #include "DocLoader.h"
31 #include "Frame.h"
32 #include "ResourceHandle.h"
33 #include "ResourceHandleInternal.h"
34 #include "ResourceResponse.h"
35 #include "ResourceResponseCFNet.h"
36
37 #include <WTF/HashMap.h>
38
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <process.h> // for _beginthread()
42
43 #include <CFNetwork/CFNetwork.h>
44 #include <CFNetwork/CFNetworkPriv.h>
45
46 //#define LOG_RESOURCELOADER_EVENTS 1
47
48 namespace WebCore {
49
50 CFURLRequestRef willSendRequest(CFURLConnectionRef conn, CFURLRequestRef request, CFURLResponseRef redirectionResponse, const void* clientInfo) 
51 {
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;
57
58 #if defined(LOG_RESOURCELOADER_EVENTS)
59     CFStringRef str = CFStringCreateWithFormat(0, 0, CFSTR("willSendRequest(conn=%p, job = %p)\n"), conn, job);
60     CFShow(str);
61     CFRelease(str);
62 #endif
63
64     if (!bytes) {
65         CFIndex numBytes, urlLength = CFStringGetLength(urlString);
66         UInt8* newBytes;
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;
71         freeBytes = true;
72         bytes = (char*)newBytes;
73     }
74     ASSERT(bytes);
75     KURL newURL(bytes);
76     if (!(newURL == job->url()))
77         job->client()->receivedRedirect(job, newURL);
78     if (freeBytes) 
79         free((void*)bytes);
80     return request;
81 }
82
83 void didReceiveResponse(CFURLConnectionRef conn, CFURLResponseRef cfResponse, const void* clientInfo) 
84 {
85     ResourceHandle* handle = (ResourceHandle*)clientInfo;
86
87 #if defined(LOG_RESOURCELOADER_EVENTS)
88     CFStringRef str = CFStringCreateWithFormat(0, 0, CFSTR("didReceiveResponse(conn=%p, job = %p)\n"), conn, handle);
89     CFShow(str);
90     CFRelease(str);
91 #endif
92
93     if (ResourceHandleClient* client = handle->client()) {
94       client->receivedResponse(handle, cfResponse);
95       ResourceResponse response;
96       getResourceResponse(response, cfResponse);
97       client->didReceiveResponse(handle, response);
98     }
99 }
100
101 void didReceiveData(CFURLConnectionRef conn, CFDataRef data, CFIndex originalLength, const void* clientInfo) 
102 {
103     ResourceHandle* job = (ResourceHandle*)clientInfo;
104     const UInt8* bytes = CFDataGetBytePtr(data);
105     CFIndex length = CFDataGetLength(data);
106
107 #if defined(LOG_RESOURCELOADER_EVENTS)
108     CFStringRef str = CFStringCreateWithFormat(0, 0, CFSTR("didReceiveData(conn=%p, job = %p, numBytes = %d)\n"), conn, job, length);
109     CFShow(str);
110     CFRelease(str);
111 #endif
112
113     job->client()->didReceiveData(job, (const char*)bytes, length);
114 }
115
116 void didFinishLoading(CFURLConnectionRef conn, const void* clientInfo) 
117 {
118     ResourceHandle* job = (ResourceHandle*)clientInfo;
119
120 #if defined(LOG_RESOURCELOADER_EVENTS)
121     CFStringRef str = CFStringCreateWithFormat(0, 0, CFSTR("didFinishLoading(conn=%p, job = %p)\n"), conn, job);
122     CFShow(str);
123     CFRelease(str);
124 #endif
125
126     job->client()->receivedAllData(job, 0);
127     job->client()->didFinishLoading(job);
128     job->kill();
129 }
130
131 void didFail(CFURLConnectionRef conn, CFStreamError error, const void* clientInfo) 
132 {
133     ResourceHandle* job = (ResourceHandle*)clientInfo;
134
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);
137     CFShow(str);
138     CFRelease(str);
139 #endif
140
141     job->setError(1);
142     job->client()->receivedAllData(job, 0);
143     job->client()->didFinishLoading(job);
144     job->kill();
145 }
146
147 CFCachedURLResponseRef willCacheResponse(CFURLConnectionRef conn, CFCachedURLResponseRef cachedResponse, const void* clientInfo) 
148 {
149     ResourceHandle* job = (ResourceHandle*)clientInfo;
150     return cachedResponse;
151 }
152
153 void didReceiveChallenge(CFURLConnectionRef conn, CFURLAuthChallengeRef challenge, const void* clientInfo)
154 {
155     ResourceHandle* job = (ResourceHandle*)clientInfo;
156
157     // Do nothing right now
158 }
159
160 void addHeadersFromHashMap(CFHTTPMessageRef request, const HTTPHeaderMap& requestHeaders) 
161 {
162     if (!requestHeaders.size())
163         return;
164
165     CFMutableDictionaryRef allHeaders = CFDictionaryCreateMutable(0, requestHeaders.size(), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
166     HTTPHeaderMap::const_iterator end = requestHeaders.end();
167     for (HTTPHeaderMap::const_iterator it = requestHeaders.begin(); it != end; ++it) {
168         CFStringRef key = it->first.createCFString();
169         CFStringRef value = it->second.createCFString();
170         CFDictionaryAddValue(allHeaders, key, value);
171         CFRelease(key);
172         CFRelease(value);
173     }
174     _CFHTTPMessageSetMultipleHeaderFields(request, allHeaders);
175     CFRelease(allHeaders);
176 }
177
178 ResourceHandleInternal::~ResourceHandleInternal()
179 {
180     if (m_connection) {
181
182 #if defined(LOG_RESOURCELOADER_EVENTS)
183         CFStringRef str = CFStringCreateWithFormat(0, 0, CFSTR("Cancelling connection %p\n"), m_connection);
184         CFShow(str);
185         CFRelease(str);
186 #endif
187         CFURLConnectionCancel(m_connection);
188         CFRelease(m_connection);
189         m_connection = 0;
190     }
191 }
192
193 ResourceHandle::~ResourceHandle()
194 {
195 #if defined(LOG_RESOURCELOADER_EVENTS)
196     CFStringRef str = CFStringCreateWithFormat(0, 0, CFSTR("Destroying job %p\n"), this);
197     CFShow(str);
198     CFRelease(str);
199 #endif
200     delete d;
201 }
202
203 CFArrayRef arrayFromFormData(const FormData& d)
204 {
205     size_t size = d.elements().size();
206     CFMutableArrayRef a = CFArrayCreateMutable(0, d.elements().size(), &kCFTypeArrayCallBacks);
207     for (size_t i = 0; i < size; ++i) {
208         const FormDataElement& e = d.elements()[i];
209         if (e.m_type == FormDataElement::data) {
210             CFDataRef data = CFDataCreate(0, (const UInt8*)e.m_data.data(), e.m_data.size());
211             CFArrayAppendValue(a, data);
212             CFRelease(data);
213         } else {
214             ASSERT(e.m_type == FormDataElement::encodedFile);
215             CFStringRef filename = e.m_filename.createCFString();
216             CFArrayAppendValue(a, filename);
217             CFRelease(filename);
218         }
219     }
220     return a;
221 }
222
223 void emptyPerform(void* unused) 
224 {
225 }
226
227 static CFRunLoopRef loaderRL = 0;
228 void runLoaderThread(void *unused)
229 {
230     loaderRL = CFRunLoopGetCurrent();
231
232     // Must add a source to the run loop to prevent CFRunLoopRun() from exiting
233     CFRunLoopSourceContext ctxt = {0, (void *)1 /*must be non-NULL*/, 0, 0, 0, 0, 0, 0, 0, emptyPerform};
234     CFRunLoopSourceRef bogusSource = CFRunLoopSourceCreate(0, 0, &ctxt);
235     CFRunLoopAddSource(loaderRL, bogusSource,kCFRunLoopDefaultMode);
236
237     CFRunLoopRun();
238 }
239
240 bool ResourceHandle::start(DocLoader* docLoader)
241 {
242     CFURLRef url = d->m_request.url().createCFURL();
243     CFStringRef requestMethod = d->m_request.httpMethod().createCFString();
244     Boolean isPost = CFStringCompare(requestMethod, CFSTR("POST"), kCFCompareCaseInsensitive);
245     CFHTTPMessageRef httpRequest = CFHTTPMessageCreateRequest(0, requestMethod, url, kCFHTTPVersion1_1);
246     CFStringRef userAgentString = docLoader->frame()->userAgent().createCFString();
247     CFHTTPMessageSetHeaderFieldValue(httpRequest, CFSTR("User-Agent"), userAgentString);
248     CFRelease(requestMethod);
249     CFRelease(userAgentString);
250
251     ref();
252     d->m_loading = true;
253     addHeadersFromHashMap(httpRequest, d->m_request.httpHeaderFields());
254
255     String referrer = docLoader->frame()->referrer();
256     if (!referrer.isEmpty()) {
257         CFStringRef str = referrer.createCFString();
258         CFHTTPMessageSetHeaderFieldValue(httpRequest, CFSTR("Referer"),str);
259         CFRelease(str);
260     }
261     
262     CFReadStreamRef bodyStream = 0;
263     if (postData().elements().size() > 0) {
264         CFArrayRef formArray = arrayFromFormData(postData());
265         bool done = false;
266         CFIndex count = CFArrayGetCount(formArray);
267
268         if (count == 1) {
269             // Handle the common special case of one piece of form data, with no files.
270             CFTypeRef d = CFArrayGetValueAtIndex(formArray, 0);
271             if (CFGetTypeID(d) == CFDataGetTypeID()) {
272                 CFHTTPMessageSetBody(httpRequest, (CFDataRef)d);
273                 done = true;
274             }
275         }
276
277         if (!done) {
278             // Precompute the content length so NSURLConnection doesn't use chunked mode.
279             long long length = 0;
280             unsigned i;
281             bool success = true;
282             for (i = 0; success && i < count; ++i) {
283                 CFTypeRef data = CFArrayGetValueAtIndex(formArray, i);
284                 CFIndex typeID = CFGetTypeID(data);
285                 if (typeID == CFDataGetTypeID()) {
286                     CFDataRef d = (CFDataRef)data;
287                     length += CFDataGetLength(d);
288                 } else {
289                     // data is a CFStringRef
290                     CFStringRef s = (CFStringRef)data;
291                     CFIndex bufLen = CFStringGetMaximumSizeOfFileSystemRepresentation(s);
292                     char* buf = (char*)malloc(bufLen);
293                     if (CFStringGetFileSystemRepresentation(s, buf, bufLen)) {
294                         struct _stat64i32 sb;
295                         int statResult = _stat(buf, &sb);
296                         if (statResult == 0 && (sb.st_mode & S_IFMT) == S_IFREG)
297                             length += sb.st_size;
298                         else
299                             success = false;
300                     } else {
301                         success = false;
302                     }
303                     free(buf);
304                 }
305             }
306             if (success) {
307                 CFStringRef lengthStr = CFStringCreateWithFormat(0, 0, CFSTR("%lld"), length);
308                 CFHTTPMessageSetHeaderFieldValue(httpRequest, CFSTR("Content-Length"), lengthStr);
309                 CFRelease(lengthStr);
310             }
311             bodyStream = CFReadStreamCreateWithFormArray(0, formArray);
312         }
313         CFRelease(formArray);
314     }
315
316     CFURLRequestRef request = CFURLRequestCreateHTTPRequest(0, httpRequest, bodyStream, kCFURLRequestCachePolicyProtocolDefault, 30.0, 0);
317     CFURLConnectionClient client = {0, this, 0, 0, 0, willSendRequest, didReceiveResponse, didReceiveData, NULL, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge};
318     d->m_connection = CFURLConnectionCreate(0, request, &client);
319     CFRelease(request);
320
321     if (!loaderRL) {
322         _beginthread(runLoaderThread, 0, 0);
323         while (loaderRL == 0) {
324             Sleep(10);
325         }
326     }
327
328     CFURLConnectionScheduleWithCurrentMessageQueue(d->m_connection);
329     CFURLConnectionScheduleDownloadWithRunLoop(d->m_connection, loaderRL, kCFRunLoopDefaultMode);
330     CFURLConnectionStart(d->m_connection);
331
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);
334     CFShow(outStr);
335     CFRelease(outStr);
336 #endif
337     CFRelease(url);
338     return true;
339 }
340
341 void ResourceHandle::cancel()
342 {
343     if (d->m_connection) {
344         CFURLConnectionCancel(d->m_connection);
345         CFRelease(d->m_connection);
346         d->m_connection = 0;
347     }
348
349     // Copied directly from ResourceHandleWin.cpp
350     setError(1);
351     d->m_client->receivedAllData(this, 0);
352     d->m_client->didFinishLoading(this);
353 }
354
355 } // namespace WebCore
356
357 #endif // USE(CFNETWORK)