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
findCFNetworkModule()51 static HMODULE findCFNetworkModule()
52 {
53 #ifndef DEBUG_ALL
54 return GetModuleHandleA("CFNetwork");
55 #else
56 return GetModuleHandleA("CFNetwork_debug");
57 #endif
58 }
59
findCFURLRequestSetContentDispositionEncodingFallbackArrayFunction()60 static CFURLRequestSetContentDispositionEncodingFallbackArrayFunction findCFURLRequestSetContentDispositionEncodingFallbackArrayFunction()
61 {
62 return reinterpret_cast<CFURLRequestSetContentDispositionEncodingFallbackArrayFunction>(GetProcAddress(findCFNetworkModule(), "_CFURLRequestSetContentDispositionEncodingFallbackArray"));
63 }
64
findCFURLRequestCopyContentDispositionEncodingFallbackArrayFunction()65 static CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction findCFURLRequestCopyContentDispositionEncodingFallbackArrayFunction()
66 {
67 return reinterpret_cast<CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction>(GetProcAddress(findCFNetworkModule(), "_CFURLRequestCopyContentDispositionEncodingFallbackArray"));
68 }
69
setContentDispositionEncodingFallbackArray(CFMutableURLRequestRef request,CFArrayRef fallbackArray)70 static void setContentDispositionEncodingFallbackArray(CFMutableURLRequestRef request, CFArrayRef fallbackArray)
71 {
72 static CFURLRequestSetContentDispositionEncodingFallbackArrayFunction function = findCFURLRequestSetContentDispositionEncodingFallbackArrayFunction();
73 if (function)
74 function(request, fallbackArray);
75 }
76
copyContentDispositionEncodingFallbackArray(CFURLRequestRef request)77 static CFArrayRef copyContentDispositionEncodingFallbackArray(CFURLRequestRef request)
78 {
79 static CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction function = findCFURLRequestCopyContentDispositionEncodingFallbackArrayFunction();
80 if (!function)
81 return 0;
82 return function(request);
83 }
84
cfURLRequest() const85 CFURLRequestRef ResourceRequest::cfURLRequest() const
86 {
87 updatePlatformRequest();
88
89 return m_cfRequest.get();
90 }
91
setHeaderFields(CFMutableURLRequestRef request,const HTTPHeaderMap & requestHeaders)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
doUpdatePlatformRequest()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 setHeaderFields(cfRequest, httpHeaderFields());
134 WebCore::setHTTPBody(cfRequest, httpBody());
135 CFURLRequestSetShouldHandleHTTPCookies(cfRequest, allowCookies());
136
137 unsigned fallbackCount = m_responseContentDispositionEncodingFallbackArray.size();
138 RetainPtr<CFMutableArrayRef> encodingFallbacks(AdoptCF, CFArrayCreateMutable(kCFAllocatorDefault, fallbackCount, 0));
139 for (unsigned i = 0; i != fallbackCount; ++i) {
140 RetainPtr<CFStringRef> encodingName(AdoptCF, m_responseContentDispositionEncodingFallbackArray[i].createCFString());
141 CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding(encodingName.get());
142 if (encoding != kCFStringEncodingInvalidId)
143 CFArrayAppendValue(encodingFallbacks.get(), reinterpret_cast<const void*>(encoding));
144 }
145 setContentDispositionEncodingFallbackArray(cfRequest, encodingFallbacks.get());
146
147 if (m_cfRequest) {
148 RetainPtr<CFHTTPCookieStorageRef> cookieStorage(AdoptCF, CFURLRequestCopyHTTPCookieStorage(m_cfRequest.get()));
149 if (cookieStorage)
150 CFURLRequestSetHTTPCookieStorage(cfRequest, cookieStorage.get());
151 CFURLRequestSetHTTPCookieStorageAcceptPolicy(cfRequest, CFURLRequestGetHTTPCookieStorageAcceptPolicy(m_cfRequest.get()));
152 CFURLRequestSetSSLProperties(cfRequest, CFURLRequestGetSSLProperties(m_cfRequest.get()));
153 }
154
155 m_cfRequest.adoptCF(cfRequest);
156 }
157
doUpdateResourceRequest()158 void ResourceRequest::doUpdateResourceRequest()
159 {
160 if (!m_cfRequest) {
161 *this = ResourceRequest();
162 return;
163 }
164
165 m_url = CFURLRequestGetURL(m_cfRequest.get());
166
167 m_cachePolicy = (ResourceRequestCachePolicy)CFURLRequestGetCachePolicy(m_cfRequest.get());
168 m_timeoutInterval = CFURLRequestGetTimeoutInterval(m_cfRequest.get());
169 m_firstPartyForCookies = CFURLRequestGetMainDocumentURL(m_cfRequest.get());
170 if (CFStringRef method = CFURLRequestCopyHTTPRequestMethod(m_cfRequest.get())) {
171 m_httpMethod = method;
172 CFRelease(method);
173 }
174 m_allowCookies = CFURLRequestShouldHandleHTTPCookies(m_cfRequest.get());
175
176 m_httpHeaderFields.clear();
177 if (CFDictionaryRef headers = CFURLRequestCopyAllHTTPHeaderFields(m_cfRequest.get())) {
178 CFIndex headerCount = CFDictionaryGetCount(headers);
179 Vector<const void*, 128> keys(headerCount);
180 Vector<const void*, 128> values(headerCount);
181 CFDictionaryGetKeysAndValues(headers, keys.data(), values.data());
182 for (int i = 0; i < headerCount; ++i)
183 m_httpHeaderFields.set((CFStringRef)keys[i], (CFStringRef)values[i]);
184 CFRelease(headers);
185 }
186
187 m_responseContentDispositionEncodingFallbackArray.clear();
188 RetainPtr<CFArrayRef> encodingFallbacks(AdoptCF, copyContentDispositionEncodingFallbackArray(m_cfRequest.get()));
189 if (encodingFallbacks) {
190 CFIndex count = CFArrayGetCount(encodingFallbacks.get());
191 for (CFIndex i = 0; i < count; ++i) {
192 CFStringEncoding encoding = reinterpret_cast<CFIndex>(CFArrayGetValueAtIndex(encodingFallbacks.get(), i));
193 if (encoding != kCFStringEncodingInvalidId)
194 m_responseContentDispositionEncodingFallbackArray.append(CFStringConvertEncodingToIANACharSetName(encoding));
195 }
196 }
197
198 m_httpBody = httpBodyFromRequest(m_cfRequest.get());
199 }
200
201 #if USE(CFURLSTORAGESESSIONS)
202
setStorageSession(CFURLStorageSessionRef storageSession)203 void ResourceRequest::setStorageSession(CFURLStorageSessionRef storageSession)
204 {
205 CFMutableURLRequestRef cfRequest = CFURLRequestCreateMutableCopy(0, m_cfRequest.get());
206 wkSetRequestStorageSession(storageSession, cfRequest);
207 m_cfRequest.adoptCF(cfRequest);
208 }
209
210 #endif
211
212 #endif // USE(CFNETWORK)
213
httpPipeliningEnabled()214 bool ResourceRequest::httpPipeliningEnabled()
215 {
216 return s_httpPipeliningEnabled;
217 }
218
setHTTPPipeliningEnabled(bool flag)219 void ResourceRequest::setHTTPPipeliningEnabled(bool flag)
220 {
221 s_httpPipeliningEnabled = flag;
222 }
223
initializeMaximumHTTPConnectionCountPerHost()224 unsigned initializeMaximumHTTPConnectionCountPerHost()
225 {
226 static const unsigned preferredConnectionCount = 6;
227 static const unsigned unlimitedConnectionCount = 10000;
228
229 // Always set the connection count per host, even when pipelining.
230 unsigned maximumHTTPConnectionCountPerHost = wkInitializeMaximumHTTPConnectionCountPerHost(preferredConnectionCount);
231
232 #if PLATFORM(MAC)
233 if (ResourceRequest::httpPipeliningEnabled()) {
234 wkSetHTTPPipeliningMaximumPriority(ResourceLoadPriorityHighest);
235 wkSetHTTPPipeliningMinimumFastLanePriority(ResourceLoadPriorityMedium);
236 // When pipelining do not rate-limit requests sent from WebCore since CFNetwork handles that.
237 return unlimitedConnectionCount;
238 }
239 #endif
240
241 return maximumHTTPConnectionCountPerHost;
242 }
243
244 } // namespace WebCore
245