blob: 3580a504fdd72ad207fa57370e1c41815fa3cbf3 [file] [log] [blame]
ap@webkit.org213b4b02009-03-10 08:11:04 +00001/*
ap@webkit.orgb05cfe42009-03-16 07:12:01 +00002 * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved.
ap@webkit.org213b4b02009-03-10 08:11:04 +00003 *
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
27#include "config.h"
28#include "CrossOriginPreflightResultCache.h"
29
30#include "CrossOriginAccessControl.h"
31#include "ResourceResponse.h"
32#include <wtf/CurrentTime.h>
paroga@webkit.orgc309e0e2011-07-31 02:23:31 +000033#include <wtf/MainThread.h>
dimich@chromium.org582ec8f2009-08-22 02:06:45 +000034#include <wtf/StdLibExtras.h>
ap@webkit.org213b4b02009-03-10 08:11:04 +000035
36namespace WebCore {
37
darin@apple.comb07ab5a2010-07-06 16:14:44 +000038using namespace std;
39
ap@webkit.orgb05cfe42009-03-16 07:12:01 +000040// These values are at the discretion of the user agent.
41static const unsigned defaultPreflightCacheTimeoutSeconds = 5;
42static const unsigned maxPreflightCacheTimeoutSeconds = 600; // Should be short enough to minimize the risk of using a poisoned cache after switching to a secure network.
43
ap@webkit.org213b4b02009-03-10 08:11:04 +000044static bool parseAccessControlMaxAge(const String& string, unsigned& expiryDelta)
45{
46 // FIXME: this will not do the correct thing for a number starting with a '+'
47 bool ok = false;
48 expiryDelta = string.toUIntStrict(&ok);
49 return ok;
50}
51
52template<class HashType>
53static void addToAccessControlAllowList(const String& string, unsigned start, unsigned end, HashSet<String, HashType>& set)
54{
55 StringImpl* stringImpl = string.impl();
56 if (!stringImpl)
57 return;
58
59 // Skip white space from start.
60 while (start <= end && isSpaceOrNewline((*stringImpl)[start]))
61 ++start;
62
63 // only white space
64 if (start > end)
65 return;
66
67 // Skip white space from end.
68 while (end && isSpaceOrNewline((*stringImpl)[end]))
69 --end;
70
dimich@chromium.org582ec8f2009-08-22 02:06:45 +000071 set.add(string.substring(start, end - start + 1));
ap@webkit.org213b4b02009-03-10 08:11:04 +000072}
73
74template<class HashType>
75static bool parseAccessControlAllowList(const String& string, HashSet<String, HashType>& set)
76{
barraclough@apple.comd643fde2010-08-16 23:31:33 +000077 unsigned start = 0;
78 size_t end;
79 while ((end = string.find(',', start)) != notFound) {
commit-queue@webkit.org7a4d9ce2012-06-10 20:18:36 +000080 if (start != end)
81 addToAccessControlAllowList(string, start, end - 1, set);
ap@webkit.org213b4b02009-03-10 08:11:04 +000082 start = end + 1;
83 }
barraclough@apple.comd643fde2010-08-16 23:31:33 +000084 if (start != string.length())
ap@webkit.org213b4b02009-03-10 08:11:04 +000085 addToAccessControlAllowList(string, start, string.length() - 1, set);
86
87 return true;
88}
89
ap@apple.com67639a22010-07-06 19:10:34 +000090bool CrossOriginPreflightResultCacheItem::parse(const ResourceResponse& response, String& errorDescription)
ap@webkit.org213b4b02009-03-10 08:11:04 +000091{
92 m_methods.clear();
ap@apple.com67639a22010-07-06 19:10:34 +000093 if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Methods"), m_methods)) {
94 errorDescription = "Cannot parse Access-Control-Allow-Methods response header field.";
ap@webkit.org213b4b02009-03-10 08:11:04 +000095 return false;
ap@apple.com67639a22010-07-06 19:10:34 +000096 }
ap@webkit.org213b4b02009-03-10 08:11:04 +000097
98 m_headers.clear();
ap@apple.com67639a22010-07-06 19:10:34 +000099 if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Headers"), m_headers)) {
100 errorDescription = "Cannot parse Access-Control-Allow-Headers response header field.";
ap@webkit.org213b4b02009-03-10 08:11:04 +0000101 return false;
ap@apple.com67639a22010-07-06 19:10:34 +0000102 }
ap@webkit.org213b4b02009-03-10 08:11:04 +0000103
104 unsigned expiryDelta;
ap@webkit.orgb05cfe42009-03-16 07:12:01 +0000105 if (parseAccessControlMaxAge(response.httpHeaderField("Access-Control-Max-Age"), expiryDelta)) {
106 if (expiryDelta > maxPreflightCacheTimeoutSeconds)
107 expiryDelta = maxPreflightCacheTimeoutSeconds;
108 } else
109 expiryDelta = defaultPreflightCacheTimeoutSeconds;
ap@webkit.org213b4b02009-03-10 08:11:04 +0000110
commit-queue@webkit.org7aee36d2013-08-27 20:47:27 +0000111 m_absoluteExpiryTime = monotonicallyIncreasingTime() + expiryDelta;
ap@webkit.org213b4b02009-03-10 08:11:04 +0000112 return true;
113}
114
ap@apple.com67639a22010-07-06 19:10:34 +0000115bool CrossOriginPreflightResultCacheItem::allowsCrossOriginMethod(const String& method, String& errorDescription) const
ap@webkit.org213b4b02009-03-10 08:11:04 +0000116{
ap@apple.com67639a22010-07-06 19:10:34 +0000117 if (m_methods.contains(method) || isOnAccessControlSimpleRequestMethodWhitelist(method))
118 return true;
119
ap@apple.com928494d2010-07-08 20:38:07 +0000120 errorDescription = "Method " + method + " is not allowed by Access-Control-Allow-Methods.";
ap@apple.com67639a22010-07-06 19:10:34 +0000121 return false;
ap@webkit.org213b4b02009-03-10 08:11:04 +0000122}
123
ap@apple.com67639a22010-07-06 19:10:34 +0000124bool CrossOriginPreflightResultCacheItem::allowsCrossOriginHeaders(const HTTPHeaderMap& requestHeaders, String& errorDescription) const
ap@webkit.org213b4b02009-03-10 08:11:04 +0000125{
126 HTTPHeaderMap::const_iterator end = requestHeaders.end();
127 for (HTTPHeaderMap::const_iterator it = requestHeaders.begin(); it != end; ++it) {
benjamin@webkit.orgee554052012-10-07 23:12:07 +0000128 if (!m_headers.contains(it->key) && !isOnAccessControlSimpleRequestHeaderWhitelist(it->key, it->value)) {
129 errorDescription = "Request header field " + it->key.string() + " is not allowed by Access-Control-Allow-Headers.";
ap@webkit.org213b4b02009-03-10 08:11:04 +0000130 return false;
ap@apple.com67639a22010-07-06 19:10:34 +0000131 }
ap@webkit.org213b4b02009-03-10 08:11:04 +0000132 }
133 return true;
134}
135
japhet@chromium.org40d9a462011-08-26 17:41:25 +0000136bool CrossOriginPreflightResultCacheItem::allowsRequest(StoredCredentials includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders) const
ap@webkit.org213b4b02009-03-10 08:11:04 +0000137{
ap@apple.com67639a22010-07-06 19:10:34 +0000138 String ignoredExplanation;
commit-queue@webkit.org7aee36d2013-08-27 20:47:27 +0000139 if (m_absoluteExpiryTime < monotonicallyIncreasingTime())
ap@webkit.org213b4b02009-03-10 08:11:04 +0000140 return false;
japhet@chromium.org40d9a462011-08-26 17:41:25 +0000141 if (includeCredentials == AllowStoredCredentials && m_credentials == DoNotAllowStoredCredentials)
ap@webkit.org213b4b02009-03-10 08:11:04 +0000142 return false;
ap@apple.com67639a22010-07-06 19:10:34 +0000143 if (!allowsCrossOriginMethod(method, ignoredExplanation))
ap@webkit.org213b4b02009-03-10 08:11:04 +0000144 return false;
ap@apple.com67639a22010-07-06 19:10:34 +0000145 if (!allowsCrossOriginHeaders(requestHeaders, ignoredExplanation))
ap@webkit.org213b4b02009-03-10 08:11:04 +0000146 return false;
147 return true;
148}
149
150CrossOriginPreflightResultCache& CrossOriginPreflightResultCache::shared()
151{
dimich@chromium.org582ec8f2009-08-22 02:06:45 +0000152 DEFINE_STATIC_LOCAL(CrossOriginPreflightResultCache, cache, ());
153 ASSERT(isMainThread());
ap@webkit.org213b4b02009-03-10 08:11:04 +0000154 return cache;
155}
156
darin@apple.com5ffbb5c2013-09-27 16:39:41 +0000157void CrossOriginPreflightResultCache::appendEntry(const String& origin, const URL& url, PassOwnPtr<CrossOriginPreflightResultCacheItem> preflightResult)
ap@webkit.org213b4b02009-03-10 08:11:04 +0000158{
dimich@chromium.org582ec8f2009-08-22 02:06:45 +0000159 ASSERT(isMainThread());
darin@apple.comb3b28cc2011-12-06 02:55:47 +0000160 m_preflightHashMap.set(make_pair(origin, url), preflightResult);
ap@webkit.org213b4b02009-03-10 08:11:04 +0000161}
162
darin@apple.com5ffbb5c2013-09-27 16:39:41 +0000163bool CrossOriginPreflightResultCache::canSkipPreflight(const String& origin, const URL& url, StoredCredentials includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders)
ap@webkit.org213b4b02009-03-10 08:11:04 +0000164{
dimich@chromium.org582ec8f2009-08-22 02:06:45 +0000165 ASSERT(isMainThread());
darin@apple.comb3b28cc2011-12-06 02:55:47 +0000166 CrossOriginPreflightResultHashMap::iterator cacheIt = m_preflightHashMap.find(make_pair(origin, url));
ap@webkit.org213b4b02009-03-10 08:11:04 +0000167 if (cacheIt == m_preflightHashMap.end())
168 return false;
169
benjamin@webkit.orgee554052012-10-07 23:12:07 +0000170 if (cacheIt->value->allowsRequest(includeCredentials, method, requestHeaders))
ap@webkit.org213b4b02009-03-10 08:11:04 +0000171 return true;
172
ap@webkit.org213b4b02009-03-10 08:11:04 +0000173 m_preflightHashMap.remove(cacheIt);
174 return false;
175}
176
weinig@apple.com376bdde2009-03-24 01:31:50 +0000177void CrossOriginPreflightResultCache::empty()
178{
dimich@chromium.org582ec8f2009-08-22 02:06:45 +0000179 ASSERT(isMainThread());
weinig@apple.com376bdde2009-03-24 01:31:50 +0000180 m_preflightHashMap.clear();
181}
182
ap@webkit.org213b4b02009-03-10 08:11:04 +0000183} // namespace WebCore