Chromium Code Reviews| Index: Source/core/fetch/FetchUtils.cpp | 
| diff --git a/Source/core/fetch/FetchUtils.cpp b/Source/core/fetch/FetchUtils.cpp | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..7157a6128f79e148fe5b15a6273af8cf4905e578 | 
| --- /dev/null | 
| +++ b/Source/core/fetch/FetchUtils.cpp | 
| @@ -0,0 +1,168 @@ | 
| +// Copyright 2014 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#include "config.h" | 
| +#include "core/fetch/FetchUtils.h" | 
| + | 
| +#include "platform/network/HTTPHeaderMap.h" | 
| +#include "platform/network/HTTPParsers.h" | 
| +#include "wtf/HashSet.h" | 
| +#include "wtf/Threading.h" | 
| +#include "wtf/text/AtomicString.h" | 
| +#include "wtf/text/WTFString.h" | 
| + | 
| +namespace blink { | 
| + | 
| +namespace { | 
| + | 
| +struct ForbiddenHeaderNames { | 
| 
 
sof
2014/08/06 19:48:15
nit: might as well make it 'class'?
 
yhirano
2014/08/07 03:21:50
Done.
 
 | 
| + WTF_MAKE_NONCOPYABLE(ForbiddenHeaderNames); WTF_MAKE_FAST_ALLOCATED; | 
| +public: | 
| + bool has(const String& name) const | 
| + { | 
| + return m_fixedNames.contains(name) | 
| + || name.startsWith(m_proxyHeaderPrefix, false) | 
| + || name.startsWith(m_secHeaderPrefix, false); | 
| + } | 
| + | 
| + static const ForbiddenHeaderNames* get(); | 
| + | 
| +private: | 
| + ForbiddenHeaderNames(); | 
| + | 
| + String m_proxyHeaderPrefix; | 
| + String m_secHeaderPrefix; | 
| + HashSet<String, CaseFoldingHash> m_fixedNames; | 
| +}; | 
| + | 
| +ForbiddenHeaderNames::ForbiddenHeaderNames() | 
| + : m_proxyHeaderPrefix("proxy-") | 
| + , m_secHeaderPrefix("sec-") | 
| +{ | 
| + m_fixedNames.add("accept-charset"); | 
| + m_fixedNames.add("accept-encoding"); | 
| + m_fixedNames.add("access-control-request-headers"); | 
| + m_fixedNames.add("access-control-request-method"); | 
| + m_fixedNames.add("connection"); | 
| + m_fixedNames.add("content-length"); | 
| + m_fixedNames.add("cookie"); | 
| + m_fixedNames.add("cookie2"); | 
| + m_fixedNames.add("date"); | 
| + m_fixedNames.add("dnt"); | 
| + m_fixedNames.add("expect"); | 
| + m_fixedNames.add("host"); | 
| + m_fixedNames.add("keep-alive"); | 
| + m_fixedNames.add("origin"); | 
| + m_fixedNames.add("referer"); | 
| + m_fixedNames.add("te"); | 
| + m_fixedNames.add("trailer"); | 
| + m_fixedNames.add("transfer-encoding"); | 
| + m_fixedNames.add("upgrade"); | 
| + m_fixedNames.add("user-agent"); | 
| + m_fixedNames.add("via"); | 
| +} | 
| + | 
| +const ForbiddenHeaderNames* ForbiddenHeaderNames::get() | 
| +{ | 
| + AtomicallyInitializedStatic(const ForbiddenHeaderNames*, instance = new ForbiddenHeaderNames); | 
| + return instance; | 
| +} | 
| + | 
| +} // namespace | 
| + | 
| +bool FetchUtils::isSimpleMethod(const String& method) | 
| +{ | 
| + // http://fetch.spec.whatwg.org/#simple-method | 
| + // "A simple method is a method that is `GET`, `HEAD`, or `POST`." | 
| + return method == "GET" || method == "HEAD" || method == "POST"; | 
| +} | 
| + | 
| +bool FetchUtils::isSimpleHeader(const AtomicString& name, const AtomicString& value) | 
| +{ | 
| + // http://fetch.spec.whatwg.org/#simple-header | 
| + // "A simple header is a header whose name is either one of `Accept`, | 
| + // `Accept-Language`, and `Content-Language`, or whose name is | 
| + // `Content-Type` and value, once parsed, is one of | 
| + // `application/x-www-form-urlencoded`, `multipart/form-data`, and | 
| + // `text/plain`." | 
| + | 
| + if (equalIgnoringCase(name, "accept") | 
| + || equalIgnoringCase(name, "accept-language") | 
| + || equalIgnoringCase(name, "content-language")) | 
| + return true; | 
| + | 
| + // Preflight is required for MIME types that can not be sent via form submission. | 
| 
 
sof
2014/08/06 19:48:15
Move this comment to the call site in isSimpleRequ
 
yhirano
2014/08/07 03:21:50
Done.
 
 | 
| + if (equalIgnoringCase(name, "content-type")) { | 
| + AtomicString mimeType = extractMIMETypeFromMediaType(value); | 
| + return equalIgnoringCase(mimeType, "application/x-www-form-urlencoded") | 
| + || equalIgnoringCase(mimeType, "multipart/form-data") | 
| + || equalIgnoringCase(mimeType, "text/plain"); | 
| + } | 
| + | 
| + return false; | 
| +} | 
| + | 
| +bool FetchUtils::isSimpleRequest(const String& method, const HTTPHeaderMap& headerMap) | 
| +{ | 
| + if (!isSimpleMethod(method)) | 
| + return false; | 
| + | 
| + HTTPHeaderMap::const_iterator end = headerMap.end(); | 
| + for (HTTPHeaderMap::const_iterator it = headerMap.begin(); it != end; ++it) { | 
| + if (!isSimpleHeader(it->key, it->value)) | 
| + return false; | 
| + } | 
| + | 
| + return true; | 
| +} | 
| + | 
| +bool FetchUtils::isForbiddenMethod(const String& method) | 
| +{ | 
| + // http://fetch.spec.whatwg.org/#forbidden-method | 
| + // "A forbidden method is a method that is a byte case-insensitive match" | 
| + // for one of `CONNECT`, `TRACE`, and `TRACK`." | 
| + return equalIgnoringCase(method, "TRACE") | 
| + || equalIgnoringCase(method, "TRACK") | 
| + || equalIgnoringCase(method, "CONNECT"); | 
| +} | 
| + | 
| +bool FetchUtils::isForbiddenHeaderName(const String& name) | 
| +{ | 
| + // http://fetch.spec.whatwg.org/#forbidden-header-name | 
| + // "A forbidden header name is a header names that is one of: | 
| + // `Accept-Charset`, `Accept-Encoding`, `Access-Control-Request-Headers`, | 
| + // `Access-Control-Request-Method`, `Connection`, | 
| + // `Content-Length, Cookie`, `Cookie2`, `Date`, `DNT`, `Expect`, `Host`, | 
| + // `Keep-Alive`, `Origin`, `Referer`, `TE`, `Trailer`, | 
| + // `Transfer-Encoding`, `Upgrade`, `User-Agent`, `Via` | 
| + // or starts with `Proxy-` or `Sec-` (including when it is just `Proxy-` or | 
| + // `Sec-`)." | 
| + | 
| + return ForbiddenHeaderNames::get()->has(name); | 
| +} | 
| + | 
| +bool FetchUtils::isForbiddenResponseHeaderName(const String& name) | 
| +{ | 
| + // http://fetch.spec.whatwg.org/#forbidden-response-header-name | 
| + // "A forbidden response header name is a header name that is one of: | 
| + // `Set-Cookie`, `Set-Cookie2`" | 
| + | 
| + return equalIgnoringCase(name, "set-cookie") || equalIgnoringCase(name, "set-cookie2"); | 
| +} | 
| + | 
| +bool FetchUtils::isSimpleOrForbiddenRequest(const String& method, const HTTPHeaderMap& headerMap) | 
| +{ | 
| + if (!isSimpleMethod(method)) | 
| + return false; | 
| + | 
| + HTTPHeaderMap::const_iterator end = headerMap.end(); | 
| + for (HTTPHeaderMap::const_iterator it = headerMap.begin(); it != end; ++it) { | 
| + if (!isSimpleHeader(it->key, it->value) && !isForbiddenHeaderName(it->key)) | 
| + return false; | 
| + } | 
| + | 
| + return true; | 
| +} | 
| + | 
| +} // namespace blink |