<http://webkit.org/b/91015> Remove BUILDING_ON / TARGETING macros in favor of system...
[WebKit-https.git] / Source / WebCore / platform / network / cf / ResourceRequestCFNet.cpp
1 /*
2  * Copyright (C) 2006, 2007, 2008 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 #include "ResourceRequestCFNet.h"
28
29 #include "ResourceHandle.h"
30 #include "ResourceRequest.h"
31
32 #if USE(CFNETWORK)
33 #include "FormDataStreamCFNet.h"
34 #include <CFNetwork/CFURLRequestPriv.h>
35 #endif
36
37 #if PLATFORM(MAC)
38 #include "ResourceLoadPriority.h"
39 #include "WebCoreSystemInterface.h"
40 #include <dlfcn.h>
41 #endif
42
43 #if PLATFORM(WIN)
44 #include <WebKitSystemInterface/WebKitSystemInterface.h>
45 #endif
46
47 namespace WebCore {
48
49 bool ResourceRequest::s_httpPipeliningEnabled = false;
50
51 #if USE(CFNETWORK)
52
53 typedef void (*CFURLRequestSetContentDispositionEncodingFallbackArrayFunction)(CFMutableURLRequestRef, CFArrayRef);
54 typedef CFArrayRef (*CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction)(CFURLRequestRef);
55
56 #if PLATFORM(WIN)
57 static HMODULE findCFNetworkModule()
58 {
59 #ifndef DEBUG_ALL
60     return GetModuleHandleA("CFNetwork");
61 #else
62     return GetModuleHandleA("CFNetwork_debug");
63 #endif
64 }
65
66 static CFURLRequestSetContentDispositionEncodingFallbackArrayFunction findCFURLRequestSetContentDispositionEncodingFallbackArrayFunction()
67 {
68     return reinterpret_cast<CFURLRequestSetContentDispositionEncodingFallbackArrayFunction>(GetProcAddress(findCFNetworkModule(), "_CFURLRequestSetContentDispositionEncodingFallbackArray"));
69 }
70
71 static CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction findCFURLRequestCopyContentDispositionEncodingFallbackArrayFunction()
72 {
73     return reinterpret_cast<CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction>(GetProcAddress(findCFNetworkModule(), "_CFURLRequestCopyContentDispositionEncodingFallbackArray"));
74 }
75 #elif PLATFORM(MAC)
76 static CFURLRequestSetContentDispositionEncodingFallbackArrayFunction findCFURLRequestSetContentDispositionEncodingFallbackArrayFunction()
77 {
78     return reinterpret_cast<CFURLRequestSetContentDispositionEncodingFallbackArrayFunction>(dlsym(RTLD_DEFAULT, "_CFURLRequestSetContentDispositionEncodingFallbackArray"));
79 }
80
81 static CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction findCFURLRequestCopyContentDispositionEncodingFallbackArrayFunction()
82 {
83     return reinterpret_cast<CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction>(dlsym(RTLD_DEFAULT, "_CFURLRequestCopyContentDispositionEncodingFallbackArray"));
84 }
85 #endif
86
87 static void setContentDispositionEncodingFallbackArray(CFMutableURLRequestRef request, CFArrayRef fallbackArray)
88 {
89     static CFURLRequestSetContentDispositionEncodingFallbackArrayFunction function = findCFURLRequestSetContentDispositionEncodingFallbackArrayFunction();
90     if (function)
91         function(request, fallbackArray);
92 }
93
94 static CFArrayRef copyContentDispositionEncodingFallbackArray(CFURLRequestRef request)
95 {
96     static CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction function = findCFURLRequestCopyContentDispositionEncodingFallbackArrayFunction();
97     if (!function)
98         return 0;
99     return function(request);
100 }
101
102 CFURLRequestRef ResourceRequest::cfURLRequest() const
103 {
104     updatePlatformRequest();
105
106     return m_cfRequest.get();
107 }
108
109 static inline void setHeaderFields(CFMutableURLRequestRef request, const HTTPHeaderMap& requestHeaders) 
110 {
111     // Remove existing headers first, as some of them may no longer be present in the map.
112     RetainPtr<CFDictionaryRef> oldHeaderFields(AdoptCF, CFURLRequestCopyAllHTTPHeaderFields(request));
113     CFIndex oldHeaderFieldCount = CFDictionaryGetCount(oldHeaderFields.get());
114     if (oldHeaderFieldCount) {
115         Vector<CFStringRef> oldHeaderFieldNames(oldHeaderFieldCount);
116         CFDictionaryGetKeysAndValues(oldHeaderFields.get(), reinterpret_cast<const void**>(&oldHeaderFieldNames[0]), 0);
117         for (CFIndex i = 0; i < oldHeaderFieldCount; ++i)
118             CFURLRequestSetHTTPHeaderFieldValue(request, oldHeaderFieldNames[i], 0);
119     }
120
121     HTTPHeaderMap::const_iterator end = requestHeaders.end();
122     for (HTTPHeaderMap::const_iterator it = requestHeaders.begin(); it != end; ++it) {
123         CFStringRef key = it->first.createCFString();
124         CFStringRef value = it->second.createCFString();
125         CFURLRequestSetHTTPHeaderFieldValue(request, key, value);
126         CFRelease(key);
127         CFRelease(value);
128     }
129 }
130
131 void ResourceRequest::doUpdatePlatformRequest()
132 {
133     CFMutableURLRequestRef cfRequest;
134
135     RetainPtr<CFURLRef> url(AdoptCF, ResourceRequest::url().createCFURL());
136     RetainPtr<CFURLRef> firstPartyForCookies(AdoptCF, ResourceRequest::firstPartyForCookies().createCFURL());
137     if (m_cfRequest) {
138         cfRequest = CFURLRequestCreateMutableCopy(0, m_cfRequest.get());
139         CFURLRequestSetURL(cfRequest, url.get());
140         CFURLRequestSetMainDocumentURL(cfRequest, firstPartyForCookies.get());
141         CFURLRequestSetCachePolicy(cfRequest, (CFURLRequestCachePolicy)cachePolicy());
142         CFURLRequestSetTimeoutInterval(cfRequest, timeoutInterval());
143     } else
144         cfRequest = CFURLRequestCreateMutable(0, url.get(), (CFURLRequestCachePolicy)cachePolicy(), timeoutInterval(), firstPartyForCookies.get());
145 #if USE(CFURLSTORAGESESSIONS)
146     wkSetRequestStorageSession(ResourceHandle::currentStorageSession(), cfRequest);
147 #endif
148
149     RetainPtr<CFStringRef> requestMethod(AdoptCF, httpMethod().createCFString());
150     CFURLRequestSetHTTPRequestMethod(cfRequest, requestMethod.get());
151
152     if (httpPipeliningEnabled())
153         wkSetHTTPPipeliningPriority(cfRequest, toHTTPPipeliningPriority(m_priority));
154 #if !PLATFORM(WIN)
155     wkCFURLRequestAllowAllPostCaching(cfRequest);
156 #endif
157
158     setHeaderFields(cfRequest, httpHeaderFields());
159     RefPtr<FormData> formData = httpBody();
160     if (formData && !formData->isEmpty())
161         WebCore::setHTTPBody(cfRequest, formData);
162     CFURLRequestSetShouldHandleHTTPCookies(cfRequest, allowCookies());
163
164     unsigned fallbackCount = m_responseContentDispositionEncodingFallbackArray.size();
165     RetainPtr<CFMutableArrayRef> encodingFallbacks(AdoptCF, CFArrayCreateMutable(kCFAllocatorDefault, fallbackCount, 0));
166     for (unsigned i = 0; i != fallbackCount; ++i) {
167         RetainPtr<CFStringRef> encodingName(AdoptCF, m_responseContentDispositionEncodingFallbackArray[i].createCFString());
168         CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding(encodingName.get());
169         if (encoding != kCFStringEncodingInvalidId)
170             CFArrayAppendValue(encodingFallbacks.get(), reinterpret_cast<const void*>(encoding));
171     }
172     setContentDispositionEncodingFallbackArray(cfRequest, encodingFallbacks.get());
173
174     if (m_cfRequest) {
175         RetainPtr<CFHTTPCookieStorageRef> cookieStorage(AdoptCF, CFURLRequestCopyHTTPCookieStorage(m_cfRequest.get()));
176         if (cookieStorage)
177             CFURLRequestSetHTTPCookieStorage(cfRequest, cookieStorage.get());
178         CFURLRequestSetHTTPCookieStorageAcceptPolicy(cfRequest, CFURLRequestGetHTTPCookieStorageAcceptPolicy(m_cfRequest.get()));
179         CFURLRequestSetSSLProperties(cfRequest, CFURLRequestGetSSLProperties(m_cfRequest.get()));
180     }
181
182     m_cfRequest.adoptCF(cfRequest);
183 #if PLATFORM(MAC)
184     updateNSURLRequest();
185 #endif
186 }
187
188 void ResourceRequest::doUpdateResourceRequest()
189 {
190     if (!m_cfRequest) {
191         *this = ResourceRequest();
192         return;
193     }
194
195     m_url = CFURLRequestGetURL(m_cfRequest.get());
196
197     m_cachePolicy = (ResourceRequestCachePolicy)CFURLRequestGetCachePolicy(m_cfRequest.get());
198     m_timeoutInterval = CFURLRequestGetTimeoutInterval(m_cfRequest.get());
199     m_firstPartyForCookies = CFURLRequestGetMainDocumentURL(m_cfRequest.get());
200     if (CFStringRef method = CFURLRequestCopyHTTPRequestMethod(m_cfRequest.get())) {
201         m_httpMethod = method;
202         CFRelease(method);
203     }
204     m_allowCookies = CFURLRequestShouldHandleHTTPCookies(m_cfRequest.get());
205
206     if (httpPipeliningEnabled())
207         m_priority = toResourceLoadPriority(wkGetHTTPPipeliningPriority(m_cfRequest.get()));
208
209     m_httpHeaderFields.clear();
210     if (CFDictionaryRef headers = CFURLRequestCopyAllHTTPHeaderFields(m_cfRequest.get())) {
211         CFIndex headerCount = CFDictionaryGetCount(headers);
212         Vector<const void*, 128> keys(headerCount);
213         Vector<const void*, 128> values(headerCount);
214         CFDictionaryGetKeysAndValues(headers, keys.data(), values.data());
215         for (int i = 0; i < headerCount; ++i)
216             m_httpHeaderFields.set((CFStringRef)keys[i], (CFStringRef)values[i]);
217         CFRelease(headers);
218     }
219
220     m_responseContentDispositionEncodingFallbackArray.clear();
221     RetainPtr<CFArrayRef> encodingFallbacks(AdoptCF, copyContentDispositionEncodingFallbackArray(m_cfRequest.get()));
222     if (encodingFallbacks) {
223         CFIndex count = CFArrayGetCount(encodingFallbacks.get());
224         for (CFIndex i = 0; i < count; ++i) {
225             CFStringEncoding encoding = reinterpret_cast<CFIndex>(CFArrayGetValueAtIndex(encodingFallbacks.get(), i));
226             if (encoding != kCFStringEncodingInvalidId)
227                 m_responseContentDispositionEncodingFallbackArray.append(CFStringConvertEncodingToIANACharSetName(encoding));
228         }
229     }
230
231     m_httpBody = httpBodyFromRequest(m_cfRequest.get());
232 }
233
234 #if USE(CFURLSTORAGESESSIONS)
235
236 void ResourceRequest::setStorageSession(CFURLStorageSessionRef storageSession)
237 {
238     CFMutableURLRequestRef cfRequest = CFURLRequestCreateMutableCopy(0, m_cfRequest.get());
239     wkSetRequestStorageSession(storageSession, cfRequest);
240     m_cfRequest.adoptCF(cfRequest);
241 #if PLATFORM(MAC)
242     updateNSURLRequest();
243 #endif
244 }
245
246 #endif
247
248 #if PLATFORM(MAC)
249 void ResourceRequest::applyWebArchiveHackForMail()
250 {
251     // Hack because Mail checks for this property to detect data / archive loads
252     _CFURLRequestSetProtocolProperty(cfURLRequest(), CFSTR("WebDataRequest"), CFSTR(""));
253 }
254 #endif
255
256 #endif // USE(CFNETWORK)
257
258 bool ResourceRequest::httpPipeliningEnabled()
259 {
260     return s_httpPipeliningEnabled;
261 }
262
263 void ResourceRequest::setHTTPPipeliningEnabled(bool flag)
264 {
265     s_httpPipeliningEnabled = flag;
266 }
267
268 #if USE(CFNETWORK) || PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
269 static inline bool readBooleanPreference(CFStringRef key)
270 {
271     Boolean keyExistsAndHasValidFormat;
272     Boolean result = CFPreferencesGetAppBooleanValue(key, kCFPreferencesCurrentApplication, &keyExistsAndHasValidFormat);
273     return keyExistsAndHasValidFormat ? result : false;
274 }
275 #endif
276
277 unsigned initializeMaximumHTTPConnectionCountPerHost()
278 {
279     static const unsigned preferredConnectionCount = 6;
280
281     // Always set the connection count per host, even when pipelining.
282     unsigned maximumHTTPConnectionCountPerHost = wkInitializeMaximumHTTPConnectionCountPerHost(preferredConnectionCount);
283
284 #if USE(CFNETWORK) || PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
285     static const unsigned unlimitedConnectionCount = 10000;
286
287     if (!ResourceRequest::httpPipeliningEnabled() && readBooleanPreference(CFSTR("WebKitEnableHTTPPipelining")))
288         ResourceRequest::setHTTPPipeliningEnabled(true);
289
290     if (ResourceRequest::httpPipeliningEnabled()) {
291         wkSetHTTPPipeliningMaximumPriority(toHTTPPipeliningPriority(ResourceLoadPriorityHighest));
292 #if !PLATFORM(WIN)
293         // FIXME: <rdar://problem/9375609> Implement minimum fast lane priority setting on Windows
294         wkSetHTTPPipeliningMinimumFastLanePriority(toHTTPPipeliningPriority(ResourceLoadPriorityMedium));
295 #endif
296         // When pipelining do not rate-limit requests sent from WebCore since CFNetwork handles that.
297         return unlimitedConnectionCount;
298     }
299 #endif
300
301     return maximumHTTPConnectionCountPerHost;
302 }
303
304 } // namespace WebCore