OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 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 "modules/fetch/MultipartParser.h" | |
6 | |
7 #include "platform/HTTPNames.h" | |
8 #include "platform/network/HTTPParsers.h" | |
9 #include "public/platform/Platform.h" | |
10 | |
11 #include <algorithm> | |
12 #include <utility> | |
13 | |
14 namespace blink { | |
15 | |
16 namespace { | |
17 | |
18 constexpr char kCloseDelimiterSuffix[] = "--\r\n"; | |
19 constexpr size_t kCloseDelimiterSuffixSize = | |
20 WTF_ARRAY_LENGTH(kCloseDelimiterSuffix) - 1u; | |
21 constexpr size_t kDashBoundaryOffset = 2u; // The length of "\r\n". | |
22 constexpr char kDelimiterSuffix[] = "\r\n"; | |
23 constexpr size_t kDelimiterSuffixSize = WTF_ARRAY_LENGTH(kDelimiterSuffix) - 1u; | |
24 | |
25 bool octetsNeedDecoding(const HTTPHeaderMap& headerFields) { | |
26 const AtomicString& encoding = | |
27 headerFields.get(HTTPNames::Content_Transfer_Encoding); | |
28 | |
29 // Decoding is not needed if a transfer encoding is not used. | |
30 if (encoding.isNull()) | |
31 return false; | |
32 | |
33 // Decoding is not needed if a no-op transfer encoding is used. | |
34 if (equalIgnoringCase(encoding, "binary") || | |
35 equalIgnoringCase(encoding, "7bit") || | |
36 equalIgnoringCase(encoding, "8bit")) { | |
37 return false; | |
38 } | |
39 | |
40 return true; | |
41 } | |
42 | |
43 } // namespace | |
44 | |
45 MultipartParser::Matcher::Matcher() = default; | |
46 | |
47 MultipartParser::Matcher::Matcher(const char* data, | |
48 size_t numMatchedBytes, | |
49 size_t size) | |
50 : m_data(data), m_numMatchedBytes(numMatchedBytes), m_size(size) {} | |
51 | |
52 bool MultipartParser::Matcher::match(const char* first, const char* last) { | |
53 while (first < last) { | |
54 if (!match(*first++)) | |
55 return false; | |
56 } | |
57 return true; | |
58 } | |
59 | |
60 void MultipartParser::Matcher::setNumMatchedBytes(size_t numMatchedBytes) { | |
61 DCHECK_LE(numMatchedBytes, m_size); | |
62 m_numMatchedBytes = numMatchedBytes; | |
63 } | |
64 | |
65 MultipartParser::MultipartParser(Vector<char> boundary, Client* client) | |
66 : m_client(client), | |
67 m_delimiter(std::move(boundary)), | |
68 m_state(State::ParsingPreamble) { | |
69 // The delimiter consists of "\r\n" and a dash boundary which consists of | |
70 // "--" and a boundary. | |
71 m_delimiter.prepend("\r\n--", 4u); | |
72 m_matcher = delimiterMatcher(kDashBoundaryOffset); | |
73 } | |
74 | |
75 bool MultipartParser::appendData(const char* bytes, size_t size) { | |
76 DCHECK_NE(State::Finished, m_state); | |
77 DCHECK_NE(State::Cancelled, m_state); | |
78 | |
79 const char* const bytesEnd = bytes + size; | |
80 | |
81 while (bytes < bytesEnd) { | |
82 switch (m_state) { | |
83 case State::ParsingPreamble: | |
84 // Parse either a preamble and a delimiter or a dash boundary. | |
85 parseDelimiter(&bytes, bytesEnd); | |
86 if (!m_matcher.isMatchComplete() && bytes < bytesEnd) { | |
87 // Parse a preamble data (by ignoring it) and then a delimiter. | |
88 m_matcher.setNumMatchedBytes(0u); | |
89 parseDataAndDelimiter(&bytes, bytesEnd); | |
90 } | |
91 if (m_matcher.isMatchComplete()) { | |
92 // Prepare for a delimiter suffix. | |
93 m_matcher = delimiterSuffixMatcher(); | |
94 m_state = State::ParsingDelimiterSuffix; | |
95 } | |
96 break; | |
97 | |
98 case State::ParsingDelimiterSuffix: | |
99 // Parse transport padding and "\r\n" after a delimiter. | |
100 // This state can be reached after either a preamble or part | |
101 // octets are parsed. | |
102 if (m_matcher.numMatchedBytes() == 0u) | |
103 parseTransportPadding(&bytes, bytesEnd); | |
104 while (bytes < bytesEnd) { | |
105 if (!m_matcher.match(*bytes++)) | |
106 return false; | |
107 if (m_matcher.isMatchComplete()) { | |
108 // Prepare for part header fields. | |
109 m_state = State::ParsingPartHeaderFields; | |
110 break; | |
111 } | |
112 } | |
113 break; | |
114 | |
115 case State::ParsingPartHeaderFields: { | |
116 // Parse part header fields (which ends with "\r\n") and an empty | |
117 // line (which also ends with "\r\n"). | |
118 // This state can be reached after a delimiter and a delimiter | |
119 // suffix after either a preamble or part octets are parsed. | |
120 HTTPHeaderMap headerFields; | |
121 if (parseHeaderFields(&bytes, bytesEnd, &headerFields)) { | |
122 // Decoding is not implemented. | |
123 if (octetsNeedDecoding(headerFields)) | |
yhirano
2017/01/11 08:54:03
Is this needed? With the current spec we end up ig
e_hakkinen
2017/05/02 23:38:56
Yes, let's remove this.
| |
124 return false; | |
125 // Prepare for part octets. | |
126 m_matcher = delimiterMatcher(); | |
127 m_state = State::ParsingPartOctets; | |
128 m_client->partHeaderFieldsInMultipartReceived(headerFields); | |
129 } | |
130 break; | |
131 } | |
132 | |
133 case State::ParsingPartOctets: { | |
134 // Parse part octets and a delimiter. | |
135 // This state can be reached only after part header fields are | |
136 // parsed. | |
137 const size_t numInitiallyMatchedBytes = m_matcher.numMatchedBytes(); | |
138 const char* octetsBegin = bytes; | |
139 parseDelimiter(&bytes, bytesEnd); | |
140 if (!m_matcher.isMatchComplete() && bytes < bytesEnd) { | |
141 if (m_matcher.numMatchedBytes() >= numInitiallyMatchedBytes && | |
142 numInitiallyMatchedBytes > 0u) { | |
143 // Since the matched bytes did not form a complete | |
144 // delimiter, the matched bytes turned out to be octet | |
145 // bytes instead of being delimiter bytes. Additionally, | |
146 // some of the matched bytes are from the previous call and | |
147 // are therefore not in the range [octetsBegin, bytesEnd[. | |
148 m_client->partDataInMultipartReceived(m_matcher.data(), | |
149 m_matcher.numMatchedBytes()); | |
150 if (m_state != State::ParsingPartOctets) | |
151 break; | |
152 octetsBegin = bytes; | |
153 } | |
154 m_matcher.setNumMatchedBytes(0u); | |
155 parseDataAndDelimiter(&bytes, bytesEnd); | |
156 const char* const octetsEnd = bytes - m_matcher.numMatchedBytes(); | |
157 if (octetsBegin < octetsEnd) { | |
158 m_client->partDataInMultipartReceived( | |
159 octetsBegin, static_cast<size_t>(octetsEnd - octetsBegin)); | |
160 if (m_state != State::ParsingPartOctets) | |
161 break; | |
162 } | |
163 } | |
164 if (m_matcher.isMatchComplete()) { | |
165 m_state = State::ParsingDelimiterOrCloseDelimiterSuffix; | |
166 m_client->partDataInMultipartFullyReceived(); | |
167 } | |
168 break; | |
169 } | |
170 | |
171 case State::ParsingDelimiterOrCloseDelimiterSuffix: | |
172 // Determine whether this is a delimiter suffix or a close | |
173 // delimiter suffix. | |
174 // This state can be reached only after part octets are parsed. | |
175 if (*bytes == '-') { | |
176 // Prepare for a close delimiter suffix. | |
177 m_matcher = closeDelimiterSuffixMatcher(); | |
178 m_state = State::ParsingCloseDelimiterSuffix; | |
179 } else { | |
180 // Prepare for a delimiter suffix. | |
181 m_matcher = delimiterSuffixMatcher(); | |
182 m_state = State::ParsingDelimiterSuffix; | |
183 } | |
184 break; | |
185 | |
186 case State::ParsingCloseDelimiterSuffix: | |
187 // Parse "--", transport padding and "\r\n" after a delimiter | |
188 // (a delimiter and "--" constitute a close delimiter). | |
189 // This state can be reached only after part octets are parsed. | |
190 for (;;) { | |
191 if (m_matcher.numMatchedBytes() == 2u) | |
192 parseTransportPadding(&bytes, bytesEnd); | |
193 if (bytes >= bytesEnd) | |
194 break; | |
195 if (!m_matcher.match(*bytes++)) | |
196 return false; | |
197 if (m_matcher.isMatchComplete()) { | |
198 // Prepare for an epilogue. | |
199 m_state = State::ParsingEpilogue; | |
200 break; | |
201 } | |
202 } | |
203 break; | |
204 | |
205 case State::ParsingEpilogue: | |
206 // Parse an epilogue (by ignoring it). | |
207 // This state can be reached only after a delimiter and a close | |
208 // delimiter suffix after part octets are parsed. | |
209 return true; | |
210 | |
211 case State::Cancelled: | |
212 case State::Finished: | |
213 // The client changed the state. | |
214 return false; | |
215 } | |
216 } | |
217 | |
218 DCHECK_EQ(bytesEnd, bytes); | |
219 | |
220 return true; | |
221 } | |
222 | |
223 void MultipartParser::cancel() { | |
224 m_state = State::Cancelled; | |
225 } | |
226 | |
227 bool MultipartParser::finish() { | |
228 DCHECK_NE(State::Cancelled, m_state); | |
229 DCHECK_NE(State::Finished, m_state); | |
230 | |
231 const State initialState = m_state; | |
232 m_state = State::Finished; | |
233 | |
234 switch (initialState) { | |
235 case State::ParsingPartOctets: | |
236 if (m_matcher.numMatchedBytes() > 0u) { | |
237 // Since the matched bytes did not form a complete delimiter, | |
238 // the matched bytes turned out to be octet bytes instead of being | |
239 // delimiter bytes. | |
240 m_client->partDataInMultipartReceived(m_matcher.data(), | |
241 m_matcher.numMatchedBytes()); | |
242 } | |
243 return false; | |
244 case State::ParsingCloseDelimiterSuffix: | |
245 // Require a full close delimiter consisting of a delimiter and "--" | |
246 // but ignore missing or partial "\r\n" after that. | |
247 return m_matcher.numMatchedBytes() >= 2u; | |
248 case State::ParsingEpilogue: | |
249 return true; | |
250 default: | |
251 return false; | |
252 } | |
253 } | |
254 | |
255 MultipartParser::Matcher MultipartParser::closeDelimiterSuffixMatcher() const { | |
256 return Matcher(kCloseDelimiterSuffix, 0u, kCloseDelimiterSuffixSize); | |
257 } | |
258 | |
259 MultipartParser::Matcher MultipartParser::delimiterMatcher( | |
260 size_t numAlreadyMatchedBytes) const { | |
261 return Matcher(m_delimiter.data(), numAlreadyMatchedBytes, | |
262 m_delimiter.size()); | |
263 } | |
264 | |
265 MultipartParser::Matcher MultipartParser::delimiterSuffixMatcher() const { | |
266 return Matcher(kDelimiterSuffix, 0u, kDelimiterSuffixSize); | |
267 } | |
268 | |
269 void MultipartParser::parseDataAndDelimiter(const char** bytesPointer, | |
270 const char* bytesEnd) { | |
271 DCHECK_EQ(0u, m_matcher.numMatchedBytes()); | |
272 | |
273 // Search for a complete delimiter within the bytes. | |
274 const char* delimiterBegin = std::search( | |
275 *bytesPointer, bytesEnd, m_delimiter.begin(), m_delimiter.end()); | |
276 if (delimiterBegin != bytesEnd) { | |
277 // A complete delimiter was found. The bytes before that are octet | |
278 // bytes. | |
279 const char* const delimiterEnd = delimiterBegin + m_delimiter.size(); | |
280 const bool matched = m_matcher.match(delimiterBegin, delimiterEnd); | |
281 DCHECK(matched); | |
282 DCHECK(m_matcher.isMatchComplete()); | |
283 *bytesPointer = delimiterEnd; | |
284 } else { | |
285 // Search for a partial delimiter in the end of the bytes. | |
286 const size_t size = static_cast<size_t>(bytesEnd - *bytesPointer); | |
287 for (delimiterBegin = bytesEnd - std::min(m_delimiter.size() - 1u, size); | |
288 delimiterBegin < bytesEnd; ++delimiterBegin) { | |
289 if (m_matcher.match(delimiterBegin, bytesEnd)) | |
290 break; | |
291 m_matcher.setNumMatchedBytes(0u); | |
292 } | |
293 // If a partial delimiter was found in the end of bytes, the bytes | |
294 // before the partial delimiter are definitely octets bytes and | |
295 // the partial delimiter bytes are buffered for now. | |
296 // If a partial delimiter was not found in the end of bytes, all bytes | |
297 // are definitely octets bytes. | |
298 // In all cases, all bytes are parsed now. | |
299 *bytesPointer = bytesEnd; | |
300 } | |
301 | |
302 DCHECK(m_matcher.isMatchComplete() || *bytesPointer == bytesEnd); | |
303 } | |
304 | |
305 void MultipartParser::parseDelimiter(const char** bytesPointer, | |
306 const char* bytesEnd) { | |
307 DCHECK(!m_matcher.isMatchComplete()); | |
308 while (*bytesPointer < bytesEnd && m_matcher.match(*(*bytesPointer))) { | |
309 ++(*bytesPointer); | |
310 if (m_matcher.isMatchComplete()) | |
311 break; | |
312 } | |
313 } | |
314 | |
315 bool MultipartParser::parseHeaderFields(const char** bytesPointer, | |
316 const char* bytesEnd, | |
317 HTTPHeaderMap* headerFields) { | |
318 // Combine the current bytes with buffered header bytes if needed. | |
319 const char* headerBytes = *bytesPointer; | |
320 size_t headerSize = static_cast<size_t>(bytesEnd - *bytesPointer); | |
321 if (!m_bufferedHeaderBytes.isEmpty()) { | |
322 m_bufferedHeaderBytes.append(headerBytes, headerSize); | |
323 headerBytes = m_bufferedHeaderBytes.data(); | |
324 headerSize = m_bufferedHeaderBytes.size(); | |
325 } | |
326 | |
327 size_t end = 0u; | |
328 if (!parseMultipartFormHeadersFromBody(headerBytes, headerSize, headerFields, | |
329 &end)) { | |
330 // Store the current header bytes for the next call unless that has | |
331 // already been done. | |
332 if (m_bufferedHeaderBytes.isEmpty()) | |
333 m_bufferedHeaderBytes.append(headerBytes, headerSize); | |
334 *bytesPointer = bytesEnd; | |
335 return false; | |
336 } | |
337 m_bufferedHeaderBytes.clear(); | |
338 *bytesPointer = bytesEnd - (headerSize - end); | |
339 | |
340 return true; | |
341 } | |
342 | |
343 void MultipartParser::parseTransportPadding(const char** bytesPointer, | |
344 const char* bytesEnd) const { | |
345 while (*bytesPointer < bytesEnd && | |
346 (*(*bytesPointer) == '\t' || *(*bytesPointer) == ' ')) | |
347 ++(*bytesPointer); | |
348 } | |
349 | |
350 DEFINE_TRACE(MultipartParser) { | |
351 visitor->trace(m_client); | |
352 } | |
353 | |
354 } // namespace blink | |
OLD | NEW |