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

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

Powered by Google App Engine
This is Rietveld 408576698