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 | #include "HTTPParsers.h" |
33 | |
34 | namespace WebCore { |
35 | |
36 | static ExceptionOr<bool> (const String& name, const String& value, const String& combinedValue, FetchHeaders::Guard guard) |
37 | { |
38 | if (!isValidHTTPToken(name)) |
39 | return Exception { TypeError, makeString("Invalid header name: '" , name, "'" ) }; |
40 | if (!isValidHTTPHeaderValue(value)) |
41 | return Exception { TypeError, makeString("Header '" , name, "' has invalid value: '" , value, "'" ) }; |
42 | if (guard == FetchHeaders::Guard::Immutable) |
43 | return Exception { TypeError, "Headers object's guard is 'immutable'"_s }; |
44 | if (guard == FetchHeaders::Guard::Request && isForbiddenHeaderName(name)) |
45 | return false; |
46 | if (guard == FetchHeaders::Guard::RequestNoCors && !combinedValue.isEmpty() && !isSimpleHeader(name, combinedValue)) |
47 | return false; |
48 | if (guard == FetchHeaders::Guard::Response && isForbiddenResponseHeaderName(name)) |
49 | return false; |
50 | return true; |
51 | } |
52 | |
53 | static ExceptionOr<void> (const String& name, const String& value, HTTPHeaderMap& , FetchHeaders::Guard guard) |
54 | { |
55 | String normalizedValue = stripLeadingAndTrailingHTTPSpaces(value); |
56 | String combinedValue = normalizedValue; |
57 | if (headers.contains(name)) |
58 | combinedValue = makeString(headers.get(name), ", " , normalizedValue); |
59 | auto canWriteResult = canWriteHeader(name, normalizedValue, combinedValue, guard); |
60 | if (canWriteResult.hasException()) |
61 | return canWriteResult.releaseException(); |
62 | if (!canWriteResult.releaseReturnValue()) |
63 | return { }; |
64 | headers.set(name, combinedValue); |
65 | return { }; |
66 | } |
67 | |
68 | static ExceptionOr<void> (const HTTPHeaderMap::HTTPHeaderMapConstIterator::KeyValue& , HTTPHeaderMap& , FetchHeaders::Guard guard) |
69 | { |
70 | auto canWriteResult = canWriteHeader(header.key, header.value, header.value, guard); |
71 | if (canWriteResult.hasException()) |
72 | return canWriteResult.releaseException(); |
73 | if (!canWriteResult.releaseReturnValue()) |
74 | return { }; |
75 | if (header.keyAsHTTPHeaderName) |
76 | headers.add(header.keyAsHTTPHeaderName.value(), header.value); |
77 | else |
78 | headers.add(header.key, header.value); |
79 | return { }; |
80 | } |
81 | |
82 | // https://fetch.spec.whatwg.org/#concept-headers-fill |
83 | static ExceptionOr<void> (HTTPHeaderMap& , const FetchHeaders::Init& , FetchHeaders::Guard guard) |
84 | { |
85 | if (WTF::holds_alternative<Vector<Vector<String>>>(headersInit)) { |
86 | auto& sequence = WTF::get<Vector<Vector<String>>>(headersInit); |
87 | for (auto& : sequence) { |
88 | if (header.size() != 2) |
89 | return Exception { TypeError, "Header sub-sequence must contain exactly two items" }; |
90 | auto result = appendToHeaderMap(header[0], header[1], headers, guard); |
91 | if (result.hasException()) |
92 | return result.releaseException(); |
93 | } |
94 | } else { |
95 | auto& record = WTF::get<Vector<WTF::KeyValuePair<String, String>>>(headersInit); |
96 | for (auto& : record) { |
97 | auto result = appendToHeaderMap(header.key, header.value, headers, guard); |
98 | if (result.hasException()) |
99 | return result.releaseException(); |
100 | } |
101 | } |
102 | |
103 | return { }; |
104 | } |
105 | |
106 | ExceptionOr<Ref<FetchHeaders>> FetchHeaders::(Optional<Init>&& ) |
107 | { |
108 | HTTPHeaderMap ; |
109 | |
110 | if (headersInit) { |
111 | auto result = fillHeaderMap(headers, *headersInit, Guard::None); |
112 | if (result.hasException()) |
113 | return result.releaseException(); |
114 | } |
115 | |
116 | return adoptRef(*new FetchHeaders { Guard::None, WTFMove(headers) }); |
117 | } |
118 | |
119 | ExceptionOr<void> FetchHeaders::(const Init& ) |
120 | { |
121 | return fillHeaderMap(m_headers, headerInit, m_guard); |
122 | } |
123 | |
124 | ExceptionOr<void> FetchHeaders::(const FetchHeaders& ) |
125 | { |
126 | for (auto& : otherHeaders.m_headers) { |
127 | auto result = appendToHeaderMap(header, m_headers, m_guard); |
128 | if (result.hasException()) |
129 | return result.releaseException(); |
130 | } |
131 | |
132 | return { }; |
133 | } |
134 | |
135 | ExceptionOr<void> FetchHeaders::(const String& name, const String& value) |
136 | { |
137 | return appendToHeaderMap(name, value, m_headers, m_guard); |
138 | } |
139 | |
140 | ExceptionOr<void> FetchHeaders::(const String& name) |
141 | { |
142 | auto canWriteResult = canWriteHeader(name, { }, { }, m_guard); |
143 | if (canWriteResult.hasException()) |
144 | return canWriteResult.releaseException(); |
145 | if (!canWriteResult.releaseReturnValue()) |
146 | return { }; |
147 | m_headers.remove(name); |
148 | return { }; |
149 | } |
150 | |
151 | ExceptionOr<String> FetchHeaders::(const String& name) const |
152 | { |
153 | if (!isValidHTTPToken(name)) |
154 | return Exception { TypeError, makeString("Invalid header name: '" , name, "'" ) }; |
155 | return m_headers.get(name); |
156 | } |
157 | |
158 | ExceptionOr<bool> FetchHeaders::(const String& name) const |
159 | { |
160 | if (!isValidHTTPToken(name)) |
161 | return Exception { TypeError, makeString("Invalid header name: '" , name, "'" ) }; |
162 | return m_headers.contains(name); |
163 | } |
164 | |
165 | ExceptionOr<void> FetchHeaders::(const String& name, const String& value) |
166 | { |
167 | String normalizedValue = stripLeadingAndTrailingHTTPSpaces(value); |
168 | auto canWriteResult = canWriteHeader(name, normalizedValue, normalizedValue, m_guard); |
169 | if (canWriteResult.hasException()) |
170 | return canWriteResult.releaseException(); |
171 | if (!canWriteResult.releaseReturnValue()) |
172 | return { }; |
173 | m_headers.set(name, normalizedValue); |
174 | return { }; |
175 | } |
176 | |
177 | void FetchHeaders::filterAndFill(const HTTPHeaderMap& , Guard guard) |
178 | { |
179 | for (auto& : headers) { |
180 | auto canWriteResult = canWriteHeader(header.key, header.value, header.value, guard); |
181 | if (canWriteResult.hasException()) |
182 | continue; |
183 | if (!canWriteResult.releaseReturnValue()) |
184 | continue; |
185 | if (header.keyAsHTTPHeaderName) |
186 | m_headers.add(header.keyAsHTTPHeaderName.value(), header.value); |
187 | else |
188 | m_headers.add(header.key, header.value); |
189 | } |
190 | } |
191 | |
192 | Optional<WTF::KeyValuePair<String, String>> FetchHeaders::Iterator::() |
193 | { |
194 | while (m_currentIndex < m_keys.size()) { |
195 | auto key = m_keys[m_currentIndex++]; |
196 | auto value = m_headers->m_headers.get(key); |
197 | if (!value.isNull()) |
198 | return WTF::KeyValuePair<String, String> { WTFMove(key), WTFMove(value) }; |
199 | } |
200 | return WTF::nullopt; |
201 | } |
202 | |
203 | FetchHeaders::Iterator::(FetchHeaders& ) |
204 | : m_headers(headers) |
205 | { |
206 | m_keys.reserveInitialCapacity(headers.m_headers.size()); |
207 | for (auto& : headers.m_headers) |
208 | m_keys.uncheckedAppend(header.key.convertToASCIILowercase()); |
209 | std::sort(m_keys.begin(), m_keys.end(), WTF::codePointCompareLessThan); |
210 | } |
211 | |
212 | } // namespace WebCore |
213 | |