1/*
2 * Copyright (C) 2003, 2006 Apple Inc.  All rights reserved.
3 * Copyright (C) 2009, 2012 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26#include "config.h"
27#include "ResourceRequestBase.h"
28
29#include "HTTPHeaderNames.h"
30#include "ResourceRequest.h"
31#include <wtf/PassOwnPtr.h>
32
33namespace WebCore {
34
35#if !USE(SOUP) && (!PLATFORM(COCOA) || USE(CFNETWORK))
36double ResourceRequestBase::s_defaultTimeoutInterval = INT_MAX;
37#else
38// Will use NSURLRequest default timeout unless set to a non-zero value with setDefaultTimeoutInterval().
39// For libsoup the timeout enabled with integer milliseconds. We set 0 as the default value to avoid integer overflow.
40double ResourceRequestBase::s_defaultTimeoutInterval = 0;
41#endif
42
43#if PLATFORM(IOS)
44bool ResourceRequestBase::s_defaultAllowCookies = true;
45#endif
46
47inline const ResourceRequest& ResourceRequestBase::asResourceRequest() const
48{
49    return *static_cast<const ResourceRequest*>(this);
50}
51
52PassOwnPtr<ResourceRequest> ResourceRequestBase::adopt(PassOwnPtr<CrossThreadResourceRequestData> data)
53{
54    OwnPtr<ResourceRequest> request = adoptPtr(new ResourceRequest());
55    request->setURL(data->m_url);
56    request->setCachePolicy(data->m_cachePolicy);
57    request->setTimeoutInterval(data->m_timeoutInterval);
58    request->setFirstPartyForCookies(data->m_firstPartyForCookies);
59    request->setHTTPMethod(data->m_httpMethod);
60    request->setPriority(data->m_priority);
61
62    request->updateResourceRequest();
63    request->m_httpHeaderFields.adopt(WTF::move(data->m_httpHeaders));
64
65    size_t encodingCount = data->m_responseContentDispositionEncodingFallbackArray.size();
66    if (encodingCount > 0) {
67        String encoding1 = data->m_responseContentDispositionEncodingFallbackArray[0];
68        String encoding2;
69        String encoding3;
70        if (encodingCount > 1) {
71            encoding2 = data->m_responseContentDispositionEncodingFallbackArray[1];
72            if (encodingCount > 2)
73                encoding3 = data->m_responseContentDispositionEncodingFallbackArray[2];
74        }
75        ASSERT(encodingCount <= 3);
76        request->setResponseContentDispositionEncodingFallbackArray(encoding1, encoding2, encoding3);
77    }
78    request->setHTTPBody(data->m_httpBody);
79    request->setAllowCookies(data->m_allowCookies);
80    request->doPlatformAdopt(data);
81    return request.release();
82}
83
84PassOwnPtr<CrossThreadResourceRequestData> ResourceRequestBase::copyData() const
85{
86    OwnPtr<CrossThreadResourceRequestData> data = adoptPtr(new CrossThreadResourceRequestData());
87    data->m_url = url().copy();
88    data->m_cachePolicy = cachePolicy();
89    data->m_timeoutInterval = timeoutInterval();
90    data->m_firstPartyForCookies = firstPartyForCookies().copy();
91    data->m_httpMethod = httpMethod().isolatedCopy();
92    data->m_httpHeaders = httpHeaderFields().copyData();
93    data->m_priority = priority();
94
95    data->m_responseContentDispositionEncodingFallbackArray.reserveInitialCapacity(m_responseContentDispositionEncodingFallbackArray.size());
96    size_t encodingArraySize = m_responseContentDispositionEncodingFallbackArray.size();
97    for (size_t index = 0; index < encodingArraySize; ++index) {
98        data->m_responseContentDispositionEncodingFallbackArray.append(m_responseContentDispositionEncodingFallbackArray[index].isolatedCopy());
99    }
100    if (m_httpBody)
101        data->m_httpBody = m_httpBody->deepCopy();
102    data->m_allowCookies = m_allowCookies;
103    return asResourceRequest().doPlatformCopyData(data.release());
104}
105
106bool ResourceRequestBase::isEmpty() const
107{
108    updateResourceRequest();
109
110    return m_url.isEmpty();
111}
112
113bool ResourceRequestBase::isNull() const
114{
115    updateResourceRequest();
116
117    return m_url.isNull();
118}
119
120const URL& ResourceRequestBase::url() const
121{
122    updateResourceRequest();
123
124    return m_url;
125}
126
127void ResourceRequestBase::setURL(const URL& url)
128{
129    updateResourceRequest();
130
131    m_url = url;
132
133    m_platformRequestUpdated = false;
134}
135
136void ResourceRequestBase::removeCredentials()
137{
138    updateResourceRequest();
139
140    if (m_url.user().isEmpty() && m_url.pass().isEmpty())
141        return;
142
143    m_url.setUser(String());
144    m_url.setPass(String());
145
146    m_platformRequestUpdated = false;
147}
148
149ResourceRequestCachePolicy ResourceRequestBase::cachePolicy() const
150{
151    updateResourceRequest();
152
153    return static_cast<ResourceRequestCachePolicy>(m_cachePolicy);
154}
155
156void ResourceRequestBase::setCachePolicy(ResourceRequestCachePolicy cachePolicy)
157{
158    updateResourceRequest();
159
160    if (m_cachePolicy == cachePolicy)
161        return;
162
163    m_cachePolicy = cachePolicy;
164
165    if (url().protocolIsInHTTPFamily())
166        m_platformRequestUpdated = false;
167}
168
169double ResourceRequestBase::timeoutInterval() const
170{
171    updateResourceRequest();
172
173    return m_timeoutInterval;
174}
175
176void ResourceRequestBase::setTimeoutInterval(double timeoutInterval)
177{
178    updateResourceRequest();
179
180    if (m_timeoutInterval == timeoutInterval)
181        return;
182
183    m_timeoutInterval = timeoutInterval;
184
185    if (url().protocolIsInHTTPFamily())
186        m_platformRequestUpdated = false;
187}
188
189const URL& ResourceRequestBase::firstPartyForCookies() const
190{
191    updateResourceRequest();
192
193    return m_firstPartyForCookies;
194}
195
196void ResourceRequestBase::setFirstPartyForCookies(const URL& firstPartyForCookies)
197{
198    updateResourceRequest();
199
200    if (m_firstPartyForCookies == firstPartyForCookies)
201        return;
202
203    m_firstPartyForCookies = firstPartyForCookies;
204
205    m_platformRequestUpdated = false;
206}
207
208const String& ResourceRequestBase::httpMethod() const
209{
210    updateResourceRequest();
211
212    return m_httpMethod;
213}
214
215void ResourceRequestBase::setHTTPMethod(const String& httpMethod)
216{
217    updateResourceRequest();
218
219    if (m_httpMethod == httpMethod)
220        return;
221
222    m_httpMethod = httpMethod;
223
224    if (url().protocolIsInHTTPFamily())
225        m_platformRequestUpdated = false;
226}
227
228const HTTPHeaderMap& ResourceRequestBase::httpHeaderFields() const
229{
230    updateResourceRequest();
231
232    return m_httpHeaderFields;
233}
234
235String ResourceRequestBase::httpHeaderField(const String& name) const
236{
237    updateResourceRequest();
238
239    return m_httpHeaderFields.get(name);
240}
241
242String ResourceRequestBase::httpHeaderField(HTTPHeaderName name) const
243{
244    updateResourceRequest();
245
246    return m_httpHeaderFields.get(name);
247}
248
249void ResourceRequestBase::setHTTPHeaderField(const String& name, const String& value)
250{
251    updateResourceRequest();
252
253    m_httpHeaderFields.set(name, value);
254
255    if (url().protocolIsInHTTPFamily())
256        m_platformRequestUpdated = false;
257}
258
259void ResourceRequestBase::setHTTPHeaderField(HTTPHeaderName name, const String& value)
260{
261    updateResourceRequest();
262
263    m_httpHeaderFields.set(name, value);
264
265    if (url().protocolIsInHTTPFamily())
266        m_platformRequestUpdated = false;
267}
268
269void ResourceRequestBase::clearHTTPAuthorization()
270{
271    updateResourceRequest();
272
273    if (!m_httpHeaderFields.remove(HTTPHeaderName::Authorization))
274        return;
275
276    if (url().protocolIsInHTTPFamily())
277        m_platformRequestUpdated = false;
278}
279
280String ResourceRequestBase::httpContentType() const
281{
282    return httpHeaderField(HTTPHeaderName::ContentType);
283}
284
285void ResourceRequestBase::setHTTPContentType(const String& httpContentType)
286{
287    setHTTPHeaderField(HTTPHeaderName::ContentType, httpContentType);
288}
289
290void ResourceRequestBase::clearHTTPContentType()
291{
292    updateResourceRequest();
293
294    m_httpHeaderFields.remove(HTTPHeaderName::ContentType);
295
296    if (url().protocolIsInHTTPFamily())
297        m_platformRequestUpdated = false;
298}
299
300String ResourceRequestBase::httpReferrer() const
301{
302    return httpHeaderField(HTTPHeaderName::Referer);
303}
304
305void ResourceRequestBase::setHTTPReferrer(const String& httpReferrer)
306{
307    setHTTPHeaderField(HTTPHeaderName::Referer, httpReferrer);
308}
309
310void ResourceRequestBase::clearHTTPReferrer()
311{
312    updateResourceRequest();
313
314    m_httpHeaderFields.remove(HTTPHeaderName::Referer);
315
316    if (url().protocolIsInHTTPFamily())
317        m_platformRequestUpdated = false;
318}
319
320String ResourceRequestBase::httpOrigin() const
321{
322    return httpHeaderField(HTTPHeaderName::Origin);
323}
324
325void ResourceRequestBase::setHTTPOrigin(const String& httpOrigin)
326{
327    setHTTPHeaderField(HTTPHeaderName::Origin, httpOrigin);
328}
329
330void ResourceRequestBase::clearHTTPOrigin()
331{
332    updateResourceRequest();
333
334    m_httpHeaderFields.remove(HTTPHeaderName::Origin);
335
336    if (url().protocolIsInHTTPFamily())
337        m_platformRequestUpdated = false;
338}
339
340String ResourceRequestBase::httpUserAgent() const
341{
342    return httpHeaderField(HTTPHeaderName::UserAgent);
343}
344
345void ResourceRequestBase::setHTTPUserAgent(const String& httpUserAgent)
346{
347    setHTTPHeaderField(HTTPHeaderName::UserAgent, httpUserAgent);
348}
349
350void ResourceRequestBase::clearHTTPUserAgent()
351{
352    updateResourceRequest();
353
354    m_httpHeaderFields.remove(HTTPHeaderName::UserAgent);
355
356    if (url().protocolIsInHTTPFamily())
357        m_platformRequestUpdated = false;
358}
359
360String ResourceRequestBase::httpAccept() const
361{
362    return httpHeaderField(HTTPHeaderName::Accept);
363}
364
365void ResourceRequestBase::setHTTPAccept(const String& httpAccept)
366{
367    setHTTPHeaderField(HTTPHeaderName::Accept, httpAccept);
368}
369
370void ResourceRequestBase::clearHTTPAccept()
371{
372    updateResourceRequest();
373
374    m_httpHeaderFields.remove(HTTPHeaderName::Accept);
375
376    if (url().protocolIsInHTTPFamily())
377        m_platformRequestUpdated = false;
378}
379
380void ResourceRequestBase::setResponseContentDispositionEncodingFallbackArray(const String& encoding1, const String& encoding2, const String& encoding3)
381{
382    updateResourceRequest();
383
384    m_responseContentDispositionEncodingFallbackArray.clear();
385    m_responseContentDispositionEncodingFallbackArray.reserveInitialCapacity(!encoding1.isNull() + !encoding2.isNull() + !encoding3.isNull());
386    if (!encoding1.isNull())
387        m_responseContentDispositionEncodingFallbackArray.uncheckedAppend(encoding1);
388    if (!encoding2.isNull())
389        m_responseContentDispositionEncodingFallbackArray.uncheckedAppend(encoding2);
390    if (!encoding3.isNull())
391        m_responseContentDispositionEncodingFallbackArray.uncheckedAppend(encoding3);
392
393    if (url().protocolIsInHTTPFamily())
394        m_platformRequestUpdated = false;
395}
396
397FormData* ResourceRequestBase::httpBody() const
398{
399    updateResourceRequest(UpdateHTTPBody);
400
401    return m_httpBody.get();
402}
403
404void ResourceRequestBase::setHTTPBody(PassRefPtr<FormData> httpBody)
405{
406    updateResourceRequest();
407
408    m_httpBody = httpBody;
409
410    m_resourceRequestBodyUpdated = true;
411
412    if (url().protocolIsInHTTPFamily())
413        m_platformRequestBodyUpdated = false;
414}
415
416bool ResourceRequestBase::allowCookies() const
417{
418    updateResourceRequest();
419
420    return m_allowCookies;
421}
422
423void ResourceRequestBase::setAllowCookies(bool allowCookies)
424{
425    updateResourceRequest();
426
427    if (m_allowCookies == allowCookies)
428        return;
429
430    m_allowCookies = allowCookies;
431
432    if (url().protocolIsInHTTPFamily())
433        m_platformRequestUpdated = false;
434}
435
436ResourceLoadPriority ResourceRequestBase::priority() const
437{
438    updateResourceRequest();
439
440    return m_priority;
441}
442
443void ResourceRequestBase::setPriority(ResourceLoadPriority priority)
444{
445    updateResourceRequest();
446
447    if (m_priority == priority)
448        return;
449
450    m_priority = priority;
451
452    if (url().protocolIsInHTTPFamily())
453        m_platformRequestUpdated = false;
454}
455
456void ResourceRequestBase::addHTTPHeaderField(const String& name, const String& value)
457{
458    updateResourceRequest();
459
460    m_httpHeaderFields.add(name, value);
461
462    if (url().protocolIsInHTTPFamily())
463        m_platformRequestUpdated = false;
464}
465
466void ResourceRequestBase::setHTTPHeaderFields(HTTPHeaderMap headerFields)
467{
468    updateResourceRequest();
469
470    m_httpHeaderFields = WTF::move(headerFields);
471
472    if (url().protocolIsInHTTPFamily())
473        m_platformRequestUpdated = false;
474}
475
476bool equalIgnoringHeaderFields(const ResourceRequestBase& a, const ResourceRequestBase& b)
477{
478    if (a.url() != b.url())
479        return false;
480
481    if (a.cachePolicy() != b.cachePolicy())
482        return false;
483
484    if (a.timeoutInterval() != b.timeoutInterval())
485        return false;
486
487    if (a.firstPartyForCookies() != b.firstPartyForCookies())
488        return false;
489
490    if (a.httpMethod() != b.httpMethod())
491        return false;
492
493    if (a.allowCookies() != b.allowCookies())
494        return false;
495
496    if (a.priority() != b.priority())
497        return false;
498
499    FormData* formDataA = a.httpBody();
500    FormData* formDataB = b.httpBody();
501
502    if (!formDataA)
503        return !formDataB;
504    if (!formDataB)
505        return !formDataA;
506
507    if (*formDataA != *formDataB)
508        return false;
509
510    return true;
511}
512
513bool ResourceRequestBase::compare(const ResourceRequest& a, const ResourceRequest& b)
514{
515    if (!equalIgnoringHeaderFields(a, b))
516        return false;
517
518    if (a.httpHeaderFields() != b.httpHeaderFields())
519        return false;
520
521    return ResourceRequest::platformCompare(a, b);
522}
523
524static const HTTPHeaderName conditionalHeaderNames[] = {
525    HTTPHeaderName::IfMatch,
526    HTTPHeaderName::IfModifiedSince,
527    HTTPHeaderName::IfNoneMatch,
528    HTTPHeaderName::IfRange,
529    HTTPHeaderName::IfUnmodifiedSince
530};
531
532bool ResourceRequestBase::isConditional() const
533{
534    for (auto headerName : conditionalHeaderNames) {
535        if (m_httpHeaderFields.contains(headerName))
536            return true;
537    }
538
539    return false;
540}
541
542void ResourceRequestBase::makeUnconditional()
543{
544    for (auto headerName : conditionalHeaderNames)
545        m_httpHeaderFields.remove(headerName);
546}
547
548double ResourceRequestBase::defaultTimeoutInterval()
549{
550    return s_defaultTimeoutInterval;
551}
552
553void ResourceRequestBase::setDefaultTimeoutInterval(double timeoutInterval)
554{
555    s_defaultTimeoutInterval = timeoutInterval;
556}
557
558void ResourceRequestBase::updatePlatformRequest(HTTPBodyUpdatePolicy bodyPolicy) const
559{
560    if (!m_platformRequestUpdated) {
561        ASSERT(m_resourceRequestUpdated);
562        const_cast<ResourceRequest&>(asResourceRequest()).doUpdatePlatformRequest();
563        m_platformRequestUpdated = true;
564    }
565
566    if (!m_platformRequestBodyUpdated && bodyPolicy == UpdateHTTPBody) {
567        ASSERT(m_resourceRequestBodyUpdated);
568        const_cast<ResourceRequest&>(asResourceRequest()).doUpdatePlatformHTTPBody();
569        m_platformRequestBodyUpdated = true;
570    }
571}
572
573void ResourceRequestBase::updateResourceRequest(HTTPBodyUpdatePolicy bodyPolicy) const
574{
575    if (!m_resourceRequestUpdated) {
576        ASSERT(m_platformRequestUpdated);
577        const_cast<ResourceRequest&>(asResourceRequest()).doUpdateResourceRequest();
578        m_resourceRequestUpdated = true;
579    }
580
581    if (!m_resourceRequestBodyUpdated && bodyPolicy == UpdateHTTPBody) {
582        ASSERT(m_platformRequestBodyUpdated);
583        const_cast<ResourceRequest&>(asResourceRequest()).doUpdateResourceHTTPBody();
584        m_resourceRequestBodyUpdated = true;
585    }
586}
587
588#if !PLATFORM(COCOA) && !USE(CFNETWORK) && !USE(SOUP)
589unsigned initializeMaximumHTTPConnectionCountPerHost()
590{
591    // This is used by the loader to control the number of issued parallel load requests.
592    // Four seems to be a common default in HTTP frameworks.
593    return 4;
594}
595#endif
596
597#if PLATFORM(IOS)
598void ResourceRequestBase::setDefaultAllowCookies(bool allowCookies)
599{
600    s_defaultAllowCookies = allowCookies;
601}
602
603bool ResourceRequestBase::defaultAllowCookies()
604{
605    return s_defaultAllowCookies;
606}
607#endif
608
609}
610