WebCore/loader/CrossOriginPreflightResultCache.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2008, 2009 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 
       
    27 #include "config.h"
       
    28 #include "CrossOriginPreflightResultCache.h"
       
    29 
       
    30 #include "CrossOriginAccessControl.h"
       
    31 #include "ResourceResponse.h"
       
    32 #include <wtf/CurrentTime.h>
       
    33 #include <wtf/StdLibExtras.h>
       
    34 #include <wtf/Threading.h>
       
    35 
       
    36 namespace WebCore {
       
    37 
       
    38 using namespace std;
       
    39 
       
    40 // These values are at the discretion of the user agent.
       
    41 static const unsigned defaultPreflightCacheTimeoutSeconds = 5;
       
    42 static const unsigned maxPreflightCacheTimeoutSeconds = 600; // Should be short enough to minimize the risk of using a poisoned cache after switching to a secure network.
       
    43 
       
    44 static 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 
       
    52 template<class HashType>
       
    53 static 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 
       
    71     set.add(string.substring(start, end - start + 1));
       
    72 }
       
    73 
       
    74 template<class HashType>
       
    75 static bool parseAccessControlAllowList(const String& string, HashSet<String, HashType>& set)
       
    76 {
       
    77     int start = 0;
       
    78     int end;
       
    79     while ((end = string.find(',', start)) != -1) {
       
    80         if (start == end)
       
    81             return false;
       
    82 
       
    83         addToAccessControlAllowList(string, start, end - 1, set);
       
    84         start = end + 1;
       
    85     }
       
    86     if (start != static_cast<int>(string.length()))
       
    87         addToAccessControlAllowList(string, start, string.length() - 1, set);
       
    88 
       
    89     return true;
       
    90 }
       
    91 
       
    92 bool CrossOriginPreflightResultCacheItem::parse(const ResourceResponse& response, String& errorDescription)
       
    93 {
       
    94     m_methods.clear();
       
    95     if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Methods"), m_methods)) {
       
    96         errorDescription = "Cannot parse Access-Control-Allow-Methods response header field.";
       
    97         return false;
       
    98     }
       
    99 
       
   100     m_headers.clear();
       
   101     if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Headers"), m_headers)) {
       
   102         errorDescription = "Cannot parse Access-Control-Allow-Headers response header field.";
       
   103         return false;
       
   104     }
       
   105 
       
   106     unsigned expiryDelta;
       
   107     if (parseAccessControlMaxAge(response.httpHeaderField("Access-Control-Max-Age"), expiryDelta)) {
       
   108         if (expiryDelta > maxPreflightCacheTimeoutSeconds)
       
   109             expiryDelta = maxPreflightCacheTimeoutSeconds;
       
   110     } else
       
   111         expiryDelta = defaultPreflightCacheTimeoutSeconds;
       
   112 
       
   113     m_absoluteExpiryTime = currentTime() + expiryDelta;
       
   114     return true;
       
   115 }
       
   116 
       
   117 bool CrossOriginPreflightResultCacheItem::allowsCrossOriginMethod(const String& method, String& errorDescription) const
       
   118 {
       
   119     if (m_methods.contains(method) || isOnAccessControlSimpleRequestMethodWhitelist(method))
       
   120         return true;
       
   121 
       
   122     errorDescription = "Method " + method + " is not allowed by Access-Control-Allow-Methods.";
       
   123     return false;
       
   124 }
       
   125 
       
   126 bool CrossOriginPreflightResultCacheItem::allowsCrossOriginHeaders(const HTTPHeaderMap& requestHeaders, String& errorDescription) const
       
   127 {
       
   128     HTTPHeaderMap::const_iterator end = requestHeaders.end();
       
   129     for (HTTPHeaderMap::const_iterator it = requestHeaders.begin(); it != end; ++it) {
       
   130         if (!m_headers.contains(it->first) && !isOnAccessControlSimpleRequestHeaderWhitelist(it->first, it->second)) {
       
   131             errorDescription = "Request header field " + it->first + " is not allowed by Access-Control-Allow-Headers.";
       
   132             return false;
       
   133         }
       
   134     }
       
   135     return true;
       
   136 }
       
   137 
       
   138 bool CrossOriginPreflightResultCacheItem::allowsRequest(bool includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders) const
       
   139 {
       
   140     String ignoredExplanation;
       
   141     if (m_absoluteExpiryTime < currentTime())
       
   142         return false;
       
   143     if (includeCredentials && !m_credentials)
       
   144         return false;
       
   145     if (!allowsCrossOriginMethod(method, ignoredExplanation))
       
   146         return false;
       
   147     if (!allowsCrossOriginHeaders(requestHeaders, ignoredExplanation))
       
   148         return false;
       
   149     return true;
       
   150 }
       
   151 
       
   152 CrossOriginPreflightResultCache& CrossOriginPreflightResultCache::shared()
       
   153 {
       
   154     DEFINE_STATIC_LOCAL(CrossOriginPreflightResultCache, cache, ());
       
   155     ASSERT(isMainThread());
       
   156     return cache;
       
   157 }
       
   158 
       
   159 void CrossOriginPreflightResultCache::appendEntry(const String& origin, const KURL& url, PassOwnPtr<CrossOriginPreflightResultCacheItem> preflightResult)
       
   160 {
       
   161     ASSERT(isMainThread());
       
   162     CrossOriginPreflightResultCacheItem* resultPtr = preflightResult.leakPtr();
       
   163     pair<CrossOriginPreflightResultHashMap::iterator, bool> addResult = m_preflightHashMap.add(make_pair(origin, url), resultPtr);
       
   164     if (!addResult.second) {
       
   165         // FIXME: We need to delete the old value before replacing with the new one.
       
   166         addResult.first->second = resultPtr;
       
   167     }
       
   168 }
       
   169 
       
   170 bool CrossOriginPreflightResultCache::canSkipPreflight(const String& origin, const KURL& url, bool includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders)
       
   171 {
       
   172     ASSERT(isMainThread());
       
   173     CrossOriginPreflightResultHashMap::iterator cacheIt = m_preflightHashMap.find(std::make_pair(origin, url));
       
   174     if (cacheIt == m_preflightHashMap.end())
       
   175         return false;
       
   176 
       
   177     if (cacheIt->second->allowsRequest(includeCredentials, method, requestHeaders))
       
   178         return true;
       
   179 
       
   180     delete cacheIt->second;
       
   181     m_preflightHashMap.remove(cacheIt);
       
   182     return false;
       
   183 }
       
   184 
       
   185 void CrossOriginPreflightResultCache::empty()
       
   186 {
       
   187     ASSERT(isMainThread());
       
   188     deleteAllValues(m_preflightHashMap);
       
   189     m_preflightHashMap.clear();
       
   190 }
       
   191 
       
   192 } // namespace WebCore