Home | History | Annotate | Download | only in fetch
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "config.h"
      6 #include "core/fetch/FetchUtils.h"
      7 
      8 #include "platform/network/HTTPHeaderMap.h"
      9 #include "platform/network/HTTPParsers.h"
     10 #include "wtf/HashSet.h"
     11 #include "wtf/Threading.h"
     12 #include "wtf/text/AtomicString.h"
     13 #include "wtf/text/WTFString.h"
     14 
     15 namespace blink {
     16 
     17 namespace {
     18 
     19 class ForbiddenHeaderNames {
     20     WTF_MAKE_NONCOPYABLE(ForbiddenHeaderNames); WTF_MAKE_FAST_ALLOCATED;
     21 public:
     22     bool has(const String& name) const
     23     {
     24         return m_fixedNames.contains(name)
     25             || name.startsWith(m_proxyHeaderPrefix, false)
     26             || name.startsWith(m_secHeaderPrefix, false);
     27     }
     28 
     29     static const ForbiddenHeaderNames* get();
     30 
     31 private:
     32     ForbiddenHeaderNames();
     33 
     34     String m_proxyHeaderPrefix;
     35     String m_secHeaderPrefix;
     36     HashSet<String, CaseFoldingHash> m_fixedNames;
     37 };
     38 
     39 ForbiddenHeaderNames::ForbiddenHeaderNames()
     40     : m_proxyHeaderPrefix("proxy-")
     41     , m_secHeaderPrefix("sec-")
     42 {
     43     m_fixedNames.add("accept-charset");
     44     m_fixedNames.add("accept-encoding");
     45     m_fixedNames.add("access-control-request-headers");
     46     m_fixedNames.add("access-control-request-method");
     47     m_fixedNames.add("connection");
     48     m_fixedNames.add("content-length");
     49     m_fixedNames.add("cookie");
     50     m_fixedNames.add("cookie2");
     51     m_fixedNames.add("date");
     52     m_fixedNames.add("dnt");
     53     m_fixedNames.add("expect");
     54     m_fixedNames.add("host");
     55     m_fixedNames.add("keep-alive");
     56     m_fixedNames.add("origin");
     57     m_fixedNames.add("referer");
     58     m_fixedNames.add("te");
     59     m_fixedNames.add("trailer");
     60     m_fixedNames.add("transfer-encoding");
     61     m_fixedNames.add("upgrade");
     62     m_fixedNames.add("user-agent");
     63     m_fixedNames.add("via");
     64 }
     65 
     66 const ForbiddenHeaderNames* ForbiddenHeaderNames::get()
     67 {
     68     AtomicallyInitializedStatic(const ForbiddenHeaderNames*, instance = new ForbiddenHeaderNames);
     69     return instance;
     70 }
     71 
     72 } // namespace
     73 
     74 bool FetchUtils::isSimpleMethod(const String& method)
     75 {
     76     // http://fetch.spec.whatwg.org/#simple-method
     77     // "A simple method is a method that is `GET`, `HEAD`, or `POST`."
     78     return method == "GET" || method == "HEAD" || method == "POST";
     79 }
     80 
     81 bool FetchUtils::isSimpleHeader(const AtomicString& name, const AtomicString& value)
     82 {
     83     // http://fetch.spec.whatwg.org/#simple-header
     84     // "A simple header is a header whose name is either one of `Accept`,
     85     // `Accept-Language`, and `Content-Language`, or whose name is
     86     // `Content-Type` and value, once parsed, is one of
     87     // `application/x-www-form-urlencoded`, `multipart/form-data`, and
     88     // `text/plain`."
     89 
     90     if (equalIgnoringCase(name, "accept")
     91         || equalIgnoringCase(name, "accept-language")
     92         || equalIgnoringCase(name, "content-language"))
     93         return true;
     94 
     95     if (equalIgnoringCase(name, "content-type")) {
     96         AtomicString mimeType = extractMIMETypeFromMediaType(value);
     97         return equalIgnoringCase(mimeType, "application/x-www-form-urlencoded")
     98             || equalIgnoringCase(mimeType, "multipart/form-data")
     99             || equalIgnoringCase(mimeType, "text/plain");
    100     }
    101 
    102     return false;
    103 }
    104 
    105 bool FetchUtils::isSimpleRequest(const String& method, const HTTPHeaderMap& headerMap)
    106 {
    107     if (!isSimpleMethod(method))
    108         return false;
    109 
    110     HTTPHeaderMap::const_iterator end = headerMap.end();
    111     for (HTTPHeaderMap::const_iterator it = headerMap.begin(); it != end; ++it) {
    112         // Preflight is required for MIME types that can not be sent via form
    113         // submission.
    114         if (!isSimpleHeader(it->key, it->value))
    115             return false;
    116     }
    117 
    118     return true;
    119 }
    120 
    121 bool FetchUtils::isForbiddenMethod(const String& method)
    122 {
    123     // http://fetch.spec.whatwg.org/#forbidden-method
    124     // "A forbidden method is a method that is a byte case-insensitive match"
    125     //  for one of `CONNECT`, `TRACE`, and `TRACK`."
    126     return equalIgnoringCase(method, "TRACE")
    127         || equalIgnoringCase(method, "TRACK")
    128         || equalIgnoringCase(method, "CONNECT");
    129 }
    130 
    131 bool FetchUtils::isForbiddenHeaderName(const String& name)
    132 {
    133     // http://fetch.spec.whatwg.org/#forbidden-header-name
    134     // "A forbidden header name is a header names that is one of:
    135     //   `Accept-Charset`, `Accept-Encoding`, `Access-Control-Request-Headers`,
    136     //   `Access-Control-Request-Method`, `Connection`,
    137     //   `Content-Length, Cookie`, `Cookie2`, `Date`, `DNT`, `Expect`, `Host`,
    138     //   `Keep-Alive`, `Origin`, `Referer`, `TE`, `Trailer`,
    139     //   `Transfer-Encoding`, `Upgrade`, `User-Agent`, `Via`
    140     // or starts with `Proxy-` or `Sec-` (including when it is just `Proxy-` or
    141     // `Sec-`)."
    142 
    143     return ForbiddenHeaderNames::get()->has(name);
    144 }
    145 
    146 bool FetchUtils::isForbiddenResponseHeaderName(const String& name)
    147 {
    148     // http://fetch.spec.whatwg.org/#forbidden-response-header-name
    149     // "A forbidden response header name is a header name that is one of:
    150     // `Set-Cookie`, `Set-Cookie2`"
    151 
    152     return equalIgnoringCase(name, "set-cookie") || equalIgnoringCase(name, "set-cookie2");
    153 }
    154 
    155 bool FetchUtils::isSimpleOrForbiddenRequest(const String& method, const HTTPHeaderMap& headerMap)
    156 {
    157     if (!isSimpleMethod(method))
    158         return false;
    159 
    160     HTTPHeaderMap::const_iterator end = headerMap.end();
    161     for (HTTPHeaderMap::const_iterator it = headerMap.begin(); it != end; ++it) {
    162         if (!isSimpleHeader(it->key, it->value) && !isForbiddenHeaderName(it->key))
    163             return false;
    164     }
    165 
    166     return true;
    167 }
    168 
    169 } // namespace blink
    170