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