Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(366)

Side by Side Diff: third_party/WebKit/Source/modules/fetch/MultipartParser.cpp

Issue 2292763002: [Fetch API] Implement Request.formData and Response.formData. (Closed)
Patch Set: Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698