OLD | NEW |
| (Empty) |
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 "core/fetch/FetchUtils.h" | |
6 | |
7 #include "platform/HTTPNames.h" | |
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 bool isHTTPWhitespace(UChar chr) { | |
20 return chr == ' ' || chr == '\n' || chr == '\t' || chr == '\r'; | |
21 } | |
22 | |
23 class ForbiddenHeaderNames { | |
24 WTF_MAKE_NONCOPYABLE(ForbiddenHeaderNames); | |
25 USING_FAST_MALLOC(ForbiddenHeaderNames); | |
26 | |
27 public: | |
28 bool has(const String& name) const { | |
29 return m_fixedNames.contains(name) || | |
30 name.startsWith(m_proxyHeaderPrefix, TextCaseASCIIInsensitive) || | |
31 name.startsWith(m_secHeaderPrefix, TextCaseASCIIInsensitive); | |
32 } | |
33 | |
34 static const ForbiddenHeaderNames& get(); | |
35 | |
36 private: | |
37 ForbiddenHeaderNames(); | |
38 | |
39 String m_proxyHeaderPrefix; | |
40 String m_secHeaderPrefix; | |
41 HashSet<String, CaseFoldingHash> m_fixedNames; | |
42 }; | |
43 | |
44 ForbiddenHeaderNames::ForbiddenHeaderNames() | |
45 : m_proxyHeaderPrefix("proxy-"), m_secHeaderPrefix("sec-") { | |
46 m_fixedNames = { | |
47 "accept-charset", | |
48 "accept-encoding", | |
49 "access-control-request-headers", | |
50 "access-control-request-method", | |
51 "connection", | |
52 "content-length", | |
53 "cookie", | |
54 "cookie2", | |
55 "date", | |
56 "dnt", | |
57 "expect", | |
58 "host", | |
59 "keep-alive", | |
60 "origin", | |
61 "referer", | |
62 "te", | |
63 "trailer", | |
64 "transfer-encoding", | |
65 "upgrade", | |
66 "user-agent", | |
67 "via", | |
68 }; | |
69 } | |
70 | |
71 const ForbiddenHeaderNames& ForbiddenHeaderNames::get() { | |
72 DEFINE_THREAD_SAFE_STATIC_LOCAL(const ForbiddenHeaderNames, instance, | |
73 new ForbiddenHeaderNames); | |
74 return instance; | |
75 } | |
76 | |
77 } // namespace | |
78 | |
79 bool FetchUtils::isSimpleMethod(const String& method) { | |
80 // http://fetch.spec.whatwg.org/#simple-method | |
81 // "A simple method is a method that is `GET`, `HEAD`, or `POST`." | |
82 return method == "GET" || method == "HEAD" || method == "POST"; | |
83 } | |
84 | |
85 bool FetchUtils::isSimpleHeader(const AtomicString& name, | |
86 const AtomicString& value) { | |
87 // http://fetch.spec.whatwg.org/#simple-header | |
88 // "A simple header is a header whose name is either one of `Accept`, | |
89 // `Accept-Language`, and `Content-Language`, or whose name is | |
90 // `Content-Type` and value, once parsed, is one of | |
91 // `application/x-www-form-urlencoded`, `multipart/form-data`, and | |
92 // `text/plain`." | |
93 // Treat 'Save-Data' as a simple header, since it is added by Chrome when | |
94 // Data Saver feature is enabled. | |
95 // Treat inspector header as a simple header, since it is added by blink when | |
96 // inspector is open. | |
97 | |
98 if (equalIgnoringCase(name, "accept") || | |
99 equalIgnoringCase(name, "accept-language") || | |
100 equalIgnoringCase(name, "content-language") || | |
101 equalIgnoringCase( | |
102 name, HTTPNames::X_DevTools_Emulate_Network_Conditions_Client_Id) || | |
103 equalIgnoringCase(name, "save-data")) | |
104 return true; | |
105 | |
106 if (equalIgnoringCase(name, "content-type")) | |
107 return isSimpleContentType(value); | |
108 | |
109 return false; | |
110 } | |
111 | |
112 bool FetchUtils::isSimpleContentType(const AtomicString& mediaType) { | |
113 AtomicString mimeType = extractMIMETypeFromMediaType(mediaType); | |
114 return equalIgnoringCase(mimeType, "application/x-www-form-urlencoded") || | |
115 equalIgnoringCase(mimeType, "multipart/form-data") || | |
116 equalIgnoringCase(mimeType, "text/plain"); | |
117 } | |
118 | |
119 bool FetchUtils::isSimpleRequest(const String& method, | |
120 const HTTPHeaderMap& headerMap) { | |
121 if (!isSimpleMethod(method)) | |
122 return false; | |
123 | |
124 for (const auto& header : headerMap) { | |
125 // Preflight is required for MIME types that can not be sent via form | |
126 // submission. | |
127 if (!isSimpleHeader(header.key, header.value)) | |
128 return false; | |
129 } | |
130 | |
131 return true; | |
132 } | |
133 | |
134 bool FetchUtils::isForbiddenMethod(const String& method) { | |
135 // http://fetch.spec.whatwg.org/#forbidden-method | |
136 // "A forbidden method is a method that is a byte case-insensitive match" | |
137 // for one of `CONNECT`, `TRACE`, and `TRACK`." | |
138 return equalIgnoringCase(method, "TRACE") || | |
139 equalIgnoringCase(method, "TRACK") || | |
140 equalIgnoringCase(method, "CONNECT"); | |
141 } | |
142 | |
143 bool FetchUtils::isForbiddenHeaderName(const String& name) { | |
144 // http://fetch.spec.whatwg.org/#forbidden-header-name | |
145 // "A forbidden header name is a header names that is one of: | |
146 // `Accept-Charset`, `Accept-Encoding`, `Access-Control-Request-Headers`, | |
147 // `Access-Control-Request-Method`, `Connection`, | |
148 // `Content-Length, Cookie`, `Cookie2`, `Date`, `DNT`, `Expect`, `Host`, | |
149 // `Keep-Alive`, `Origin`, `Referer`, `TE`, `Trailer`, | |
150 // `Transfer-Encoding`, `Upgrade`, `User-Agent`, `Via` | |
151 // or starts with `Proxy-` or `Sec-` (including when it is just `Proxy-` or | |
152 // `Sec-`)." | |
153 | |
154 return ForbiddenHeaderNames::get().has(name); | |
155 } | |
156 | |
157 bool FetchUtils::isForbiddenResponseHeaderName(const String& name) { | |
158 // http://fetch.spec.whatwg.org/#forbidden-response-header-name | |
159 // "A forbidden response header name is a header name that is one of: | |
160 // `Set-Cookie`, `Set-Cookie2`" | |
161 | |
162 return equalIgnoringCase(name, "set-cookie") || | |
163 equalIgnoringCase(name, "set-cookie2"); | |
164 } | |
165 | |
166 bool FetchUtils::isSimpleOrForbiddenRequest(const String& method, | |
167 const HTTPHeaderMap& headerMap) { | |
168 if (!isSimpleMethod(method)) | |
169 return false; | |
170 | |
171 for (const auto& header : headerMap) { | |
172 if (!isSimpleHeader(header.key, header.value) && | |
173 !isForbiddenHeaderName(header.key)) | |
174 return false; | |
175 } | |
176 | |
177 return true; | |
178 } | |
179 | |
180 AtomicString FetchUtils::normalizeMethod(const AtomicString& method) { | |
181 // https://fetch.spec.whatwg.org/#concept-method-normalize | |
182 | |
183 // We place GET and POST first because they are more commonly used than | |
184 // others. | |
185 const char* const methods[] = { | |
186 "GET", "POST", "DELETE", "HEAD", "OPTIONS", "PUT", | |
187 }; | |
188 | |
189 for (const auto& known : methods) { | |
190 if (equalIgnoringCase(method, known)) { | |
191 // Don't bother allocating a new string if it's already all | |
192 // uppercase. | |
193 return method == known ? method : known; | |
194 } | |
195 } | |
196 return method; | |
197 } | |
198 | |
199 String FetchUtils::normalizeHeaderValue(const String& value) { | |
200 // https://fetch.spec.whatwg.org/#concept-header-value-normalize | |
201 // Strip leading and trailing whitespace from header value. | |
202 // HTTP whitespace bytes are 0x09, 0x0A, 0x0D, and 0x20. | |
203 | |
204 return value.stripWhiteSpace(isHTTPWhitespace); | |
205 } | |
206 | |
207 } // namespace blink | |
OLD | NEW |