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
42namespace WebCore {
43
44bool ResourceRequest::s_httpPipeliningEnabled = false;
45
46#if USE(CFNETWORK)
47
48typedef void (*CFURLRequestSetContentDispositionEncodingFallbackArrayFunction)(CFMutableURLRequestRef, CFArrayRef);
49typedef CFArrayRef (*CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction)(CFURLRequestRef);
50
51static HMODULE findCFNetworkModule()
52{
53#ifndef DEBUG_ALL
54    return GetModuleHandleA("CFNetwork");
55#else
56    return GetModuleHandleA("CFNetwork_debug");
57#endif
58}
59
60static CFURLRequestSetContentDispositionEncodingFallbackArrayFunction findCFURLRequestSetContentDispositionEncodingFallbackArrayFunction()
61{
62    return reinterpret_cast<CFURLRequestSetContentDispositionEncodingFallbackArrayFunction>(GetProcAddress(findCFNetworkModule(), "_CFURLRequestSetContentDispositionEncodingFallbackArray"));
63}
64
65static CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction findCFURLRequestCopyContentDispositionEncodingFallbackArrayFunction()
66{
67    return reinterpret_cast<CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction>(GetProcAddress(findCFNetworkModule(), "_CFURLRequestCopyContentDispositionEncodingFallbackArray"));
68}
69
70static void setContentDispositionEncodingFallbackArray(CFMutableURLRequestRef request, CFArrayRef fallbackArray)
71{
72    static CFURLRequestSetContentDispositionEncodingFallbackArrayFunction function = findCFURLRequestSetContentDispositionEncodingFallbackArrayFunction();
73    if (function)
74        function(request, fallbackArray);
75}
76
77static CFArrayRef copyContentDispositionEncodingFallbackArray(CFURLRequestRef request)
78{
79    static CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction function = findCFURLRequestCopyContentDispositionEncodingFallbackArrayFunction();
80    if (!function)
81        return 0;
82    return function(request);
83}
84
85CFURLRequestRef ResourceRequest::cfURLRequest() const
86{
87    updatePlatformRequest();
88
89    return m_cfRequest.get();
90}
91
92static 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
114void 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
158void 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
203void 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
214bool ResourceRequest::httpPipeliningEnabled()
215{
216    return s_httpPipeliningEnabled;
217}
218
219void ResourceRequest::setHTTPPipeliningEnabled(bool flag)
220{
221    s_httpPipeliningEnabled = flag;
222}
223
224unsigned 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