Get rid of unnecessary null check in wrap(JSDOMGlobalObject*, DOMClass*)
[WebKit-https.git] / Source / WebCore / Modules / fetch / FetchHeaders.cpp
1 /*
2  * Copyright (C) 2016 Canon Inc.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted, provided that the following conditions
6  * are required to be met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Canon Inc. nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "FetchHeaders.h"
31
32 #if ENABLE(FETCH_API)
33
34 #include "ExceptionCode.h"
35 #include "HTTPParsers.h"
36
37 namespace WebCore {
38
39 // FIXME: Optimize these routines for HTTPHeaderMap keys and/or refactor them with XMLHttpRequest code.
40 static bool isForbiddenHeaderName(const String& name)
41 {
42     HTTPHeaderName headerName;
43     if (findHTTPHeaderName(name, headerName)) {
44         switch (headerName) {
45         case HTTPHeaderName::AcceptCharset:
46         case HTTPHeaderName::AcceptEncoding:
47         case HTTPHeaderName::AccessControlRequestHeaders:
48         case HTTPHeaderName::AccessControlRequestMethod:
49         case HTTPHeaderName::Connection:
50         case HTTPHeaderName::ContentLength:
51         case HTTPHeaderName::Cookie:
52         case HTTPHeaderName::Cookie2:
53         case HTTPHeaderName::Date:
54         case HTTPHeaderName::DNT:
55         case HTTPHeaderName::Expect:
56         case HTTPHeaderName::Host:
57         case HTTPHeaderName::KeepAlive:
58         case HTTPHeaderName::Origin:
59         case HTTPHeaderName::Referer:
60         case HTTPHeaderName::TE:
61         case HTTPHeaderName::Trailer:
62         case HTTPHeaderName::TransferEncoding:
63         case HTTPHeaderName::Upgrade:
64         case HTTPHeaderName::Via:
65             return true;
66         default:
67             break;
68         }
69     }
70     return startsWithLettersIgnoringASCIICase(name, "sec-") || startsWithLettersIgnoringASCIICase(name, "proxy-");
71 }
72
73 static bool isForbiddenResponseHeaderName(const String& name)
74 {
75     return equalLettersIgnoringASCIICase(name, "set-cookie") || equalLettersIgnoringASCIICase(name, "set-cookie2");
76 }
77
78 static bool isSimpleHeader(const String& name, const String& value)
79 {
80     HTTPHeaderName headerName;
81     if (!findHTTPHeaderName(name, headerName))
82         return false;
83     switch (headerName) {
84     case HTTPHeaderName::Accept:
85     case HTTPHeaderName::AcceptLanguage:
86     case HTTPHeaderName::ContentLanguage:
87         return true;
88     case HTTPHeaderName::ContentType: {
89         String mimeType = extractMIMETypeFromMediaType(value);
90         return equalLettersIgnoringASCIICase(mimeType, "application/x-www-form-urlencoded") || equalLettersIgnoringASCIICase(mimeType, "multipart/form-data") || equalLettersIgnoringASCIICase(mimeType, "text/plain");
91     }
92     default:
93         return false;
94     }
95 }
96
97 static bool canWriteHeader(const String& name, const String& value, FetchHeaders::Guard guard, ExceptionCode& ec)
98 {
99     if (!isValidHTTPToken(name) || !isValidHTTPHeaderValue(value)) {
100         ec = TypeError;
101         return false;
102     }
103     if (guard == FetchHeaders::Guard::Immutable) {
104         ec = TypeError;
105         return false;
106     }
107     if (guard == FetchHeaders::Guard::Request && isForbiddenHeaderName(name))
108         return false;
109     if (guard == FetchHeaders::Guard::RequestNoCors && !isSimpleHeader(name, value))
110         return false;
111     if (guard == FetchHeaders::Guard::Response && isForbiddenResponseHeaderName(name))
112         return false;
113     return true;
114 }
115
116 void FetchHeaders::append(const String& name, const String& value, ExceptionCode& ec)
117 {
118     String normalizedValue = stripLeadingAndTrailingHTTPSpaces(value);
119     if (!canWriteHeader(name, normalizedValue, m_guard, ec))
120         return;
121     m_headers.add(name, normalizedValue);
122 }
123
124 void FetchHeaders::remove(const String& name, ExceptionCode& ec)
125 {
126     if (!canWriteHeader(name, String(), m_guard, ec))
127         return;
128     m_headers.remove(name);
129 }
130
131 String FetchHeaders::get(const String& name, ExceptionCode& ec) const
132 {
133     if (!isValidHTTPToken(name)) {
134         ec = TypeError;
135         return String();
136     }
137     return m_headers.get(name);
138 }
139
140 bool FetchHeaders::has(const String& name, ExceptionCode& ec) const
141 {
142     if (!isValidHTTPToken(name)) {
143         ec = TypeError;
144         return false;
145     }
146     return m_headers.contains(name);
147 }
148
149 void FetchHeaders::set(const String& name, const String& value, ExceptionCode& ec)
150 {
151     String normalizedValue = stripLeadingAndTrailingHTTPSpaces(value);
152     if (!canWriteHeader(name, normalizedValue, m_guard, ec))
153         return;
154     m_headers.set(name, normalizedValue);
155 }
156
157 void FetchHeaders::fill(const FetchHeaders* headers)
158 {
159     ASSERT(m_guard != Guard::Immutable);
160
161     if (!headers)
162         return;
163
164     filterAndFill(headers->m_headers, m_guard);
165 }
166
167 void FetchHeaders::filterAndFill(const HTTPHeaderMap& headers, Guard guard)
168 {
169     ExceptionCode ec;
170     for (auto& header : headers) {
171         if (canWriteHeader(header.key, header.value, guard, ec)) {
172             if (header.keyAsHTTPHeaderName)
173                 m_headers.add(header.keyAsHTTPHeaderName.value(), header.value);
174             else
175                 m_headers.add(header.key, header.value);
176         }
177     }
178 }
179
180 Optional<WTF::KeyValuePair<String, String>> FetchHeaders::Iterator::next(JSC::ExecState&)
181 {
182     while (m_currentIndex < m_keys.size()) {
183         String key = m_keys[m_currentIndex++];
184         String value = m_headers->m_headers.get(key);
185         if (!value.isNull())
186             return WTF::KeyValuePair<String, String>(WTFMove(key), WTFMove(value));
187     }
188     m_keys.clear();
189     return Nullopt;
190 }
191
192 FetchHeaders::Iterator::Iterator(FetchHeaders& headers)
193     : m_headers(headers)
194 {
195     m_keys.reserveInitialCapacity(headers.m_headers.size());
196     for (auto& header : headers.m_headers)
197         m_keys.uncheckedAppend(header.key.convertToASCIILowercase());
198
199     std::sort(m_keys.begin(), m_keys.end(), WTF::codePointCompareLessThan);
200 }
201
202 } // namespace WebCore
203
204 #endif // ENABLE(FETCH_API)