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

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: Parse functions etc Created 4 years, 3 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 s().get(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 size, size_t capacity )
46 : m_capacity(capacity), m_data(data), m_size(size) {}
47
48 bool MultipartParser::Matcher::appendIfExpected(char value)
49 {
50 DCHECK_LT(m_size, m_capacity);
51 if (value != m_data[m_size])
52 return false;
53 ++m_size;
54 return true;
55 }
56
57 bool MultipartParser::Matcher::appendIfExpected(const char* first, const char* l ast)
58 {
59 while (first < last) {
60 if (!appendIfExpected(*first++))
61 return false;
62 }
63 return true;
64 }
65
66 void MultipartParser::Matcher::clear()
67 {
68 m_size = 0u;
69 }
70
71 MultipartParser::MultipartParser(Vector<char> boundary, Client* client)
72 : m_client(client)
73 , m_delimiter(std::move(boundary))
74 , m_state(ParsingPreamble)
75 {
76 // The delimiter consists of "\r\n" and a dash boundary which consists of
77 // "--" and a boundary.
78 m_delimiter.prepend("\r\n--", 4u);
79 m_bufferedBytes = delimiterMatcher(kDashBoundaryOffset);
80 }
81
82 bool MultipartParser::appendData(const char* bytes, size_t size)
83 {
84 DCHECK_NE(Finished, m_state);
85 DCHECK_NE(Cancelled, m_state);
86
87 const char* bytesEnd = bytes + size;
88
89 while (bytes < bytesEnd) {
90 switch (m_state) {
91 case ParsingPreamble:
92 // Parse either a preamble and a delimiter or a dash boundary.
93 parseDelimiter(&bytes, bytesEnd);
94 if (!m_bufferedBytes.isComplete() && bytes < bytesEnd) {
95 // Parse a preamble (by ignoring it) and parse a delimiter.
96 m_bufferedBytes.clear();
97 parseDataAndDelimiter(&bytes, bytesEnd);
98 }
99 if (m_bufferedBytes.isComplete()) {
100 // Prepare for a delimiter suffix.
101 m_bufferedBytes = delimiterSuffixMatcher();
102 m_state = ParsingDelimiterSuffix;
103 }
104 break;
105
106 case ParsingDelimiterSuffix:
107 // Parse transport padding and "\r\n" after a delimiter.
108 // This can happen after either a preamble or part octets.
109 if (m_bufferedBytes.empty())
110 parseTransportPadding(&bytes, bytesEnd);
111 while (bytes < bytesEnd) {
112 if (!m_bufferedBytes.appendIfExpected(*bytes++))
113 return false;
114 if (m_bufferedBytes.isComplete()) {
115 // Prepare for part header fields.
116 m_state = ParsingPartHeaderFields;
117 break;
118 }
119 }
120 break;
121
122 case ParsingPartHeaderFields: {
123 // Parse part header fields (which ends with "\r\n") and an empty
124 // line (which also ends with "\r\n").
125 // This can happen after a delimiter and a delimiter suffix which
126 // can happen after either a preamble or part octets.
127 WebURLResponse response;
128 if (parseHeaderFields(&bytes, bytesEnd, &response)) {
129 // Decoding is not implemented.
130 if (octetsNeedDecoding(response))
131 return false;
132 // Prepare for part octets.
133 m_bufferedBytes = delimiterMatcher();
134 m_state = ParsingPartOctets;
135 m_client->partHeaderFieldsInMultipartReceived(response.toResourc eResponse());
136 }
137 break;
138 }
139
140 case ParsingPartOctets: {
141 // Parse part octets and a delimiter.
142 // This can happen only after part header fields.
yhirano 2016/09/21 09:02:57 Can you tell me what "This can happen" mean?
e_hakkinen 2016/09/22 22:27:16 It refers to this parsing part octets and a delimi
143 bool hasInitiallyBufferedBytes = !m_bufferedBytes.empty();
144 const char* octetsBegin = bytes;
145 parseDelimiter(&bytes, bytesEnd);
146 if (!m_bufferedBytes.isComplete() && bytes < bytesEnd) {
147 if (!m_bufferedBytes.empty() && hasInitiallyBufferedBytes) {
148 // The buffered bytes turned out to be octet bytes instead
149 // of delimiter bytes. Additionally, some of the buffered
150 // bytes are from the previous call and are therefore not
151 // in the range [octetsBegin, bytesEnd[.
152 m_client->partDataInMultipartReceived(m_bufferedBytes.data() , m_bufferedBytes.size());
153 octetsBegin = bytes;
154 }
155 if (m_state == ParsingPartOctets) {
yhirano 2016/09/21 09:02:57 [optional] Moving the if statement to just after t
yhirano 2016/09/21 09:02:57 if (m_state != ParsingPartOctets) break; would
e_hakkinen 2016/09/22 22:27:16 Done.
156 m_bufferedBytes.clear();
157 parseDataAndDelimiter(&bytes, bytesEnd);
158 const char* octetsEnd = bytes - m_bufferedBytes.size();
159 if (octetsEnd > octetsBegin)
yhirano 2016/09/21 09:02:57 You are using the |begin < end| pattern in this fi
e_hakkinen 2016/09/22 22:27:16 Done.
160 m_client->partDataInMultipartReceived(octetsBegin, stati c_cast<size_t>(octetsEnd - octetsBegin));
161 }
162 }
163 if (m_state == ParsingPartOctets && m_bufferedBytes.isComplete()) {
164 m_state = ParsingDelimiterOrCloseDelimiterSuffix;
165 m_client->partDataInMultipartFullyReceived();
166 }
167 break;
168 }
169
170 case ParsingDelimiterOrCloseDelimiterSuffix:
171 // Determine whether this is a delimiter suffix or a close
172 // delimiter suffix.
173 // This can happen only after part octets.
174 if (*bytes == '-') {
175 // Prepare for a close delimiter suffix.
176 m_bufferedBytes = closeDelimiterSuffixMatcher();
177 m_state = ParsingCloseDelimiterSuffix;
178 } else {
179 // Prepare for a delimiter suffix.
180 m_bufferedBytes = delimiterSuffixMatcher();
181 m_state = ParsingDelimiterSuffix;
182 }
183 break;
184
185 case ParsingCloseDelimiterSuffix:
186 // Parse "--", transport padding and "\r\n" after a delimiter
187 // (a delimiter and "--" constitute a close delimiter).
188 // This can happen only after part octets.
189 for (;;) {
190 if (m_bufferedBytes.size() == 2u)
191 parseTransportPadding(&bytes, bytesEnd);
192 if (bytes >= bytesEnd)
193 break;
194 if (!m_bufferedBytes.appendIfExpected(*bytes++))
195 return false;
196 if (m_bufferedBytes.isComplete()) {
197 // Prepare for an epilogue.
198 m_state = ParsingEpilogue;
199 break;
200 }
201 }
202 break;
203
204 case ParsingEpilogue:
205 // Parse an epilogue (by ignoring it).
206 // This can happen only after a delimiter and a close delimiter
207 // suffix which can happen only after part octets.
208 return true;
209
210 case Cancelled:
211 case Finished:
212 // The client changed the state.
213 return true;
214 }
215 }
216
217 DCHECK_EQ(bytesEnd, bytes);
218
219 return true;
220 }
221
222 void MultipartParser::cancel()
223 {
224 m_state = Cancelled;
225 }
226
227 bool MultipartParser::finish()
228 {
229 DCHECK_NE(Cancelled, m_state);
230
231 State initialState = m_state;
232
233 if (m_state == ParsingPartOctets && !m_bufferedBytes.empty()) {
234 // The end of append bytes looked like a delimiter but was not a full
235 // one, after all. Treat the those bytes as octet bytes.
236 m_client->partDataInMultipartReceived(m_bufferedBytes.data(), m_buffered Bytes.size());
237 }
238 m_state = Finished;
239
240 switch (initialState) {
241 case ParsingCloseDelimiterSuffix:
242 // Require a full close delimiter consisting of a delimiter and "--"
243 // but ignore missing or partial "\r\n" after that.
244 return m_bufferedBytes.size() >= 2u;
245 case ParsingEpilogue:
246 case Finished:
247 return true;
248 default:
249 return false;
250 }
251 }
252
253 MultipartParser::Matcher MultipartParser::closeDelimiterSuffixMatcher() const
254 {
255 return Matcher(kCloseDelimiterSuffix, 0u, kCloseDelimiterSuffixSize);
256 }
257
258 MultipartParser::Matcher MultipartParser::delimiterMatcher(size_t size) const
259 {
260 return Matcher(m_delimiter.data(), size, m_delimiter.size());
261 }
262
263 MultipartParser::Matcher MultipartParser::delimiterSuffixMatcher() const
264 {
265 return Matcher(kDelimiterSuffix, 0u, kDelimiterSuffixSize);
266 }
267
268 void MultipartParser::parseDataAndDelimiter(const char** bytesPointer, const cha r* bytesEnd)
269 {
270 DCHECK(m_bufferedBytes.empty());
271
272 // Search for a complete delimiter within the bytes.
273 const char* delimiterBegin = std::search(*bytesPointer, bytesEnd, m_delimite r.begin(), m_delimiter.end());
274 if (delimiterBegin != bytesEnd) {
275 // A complete delimiter was found. The bytes before that are octet
276 // bytes.
277 const char* delimiterEnd = delimiterBegin + m_delimiter.size();
278 if (!m_bufferedBytes.appendIfExpected(delimiterBegin, delimiterEnd))
yhirano 2016/09/21 09:02:57 bool matched = m_bufferedBytes.appendIfExpected(de
e_hakkinen 2016/09/22 22:27:16 Done.
279 NOTREACHED();
280 DCHECK(m_bufferedBytes.isComplete());
281 *bytesPointer = delimiterEnd;
282 } else {
283 // Search for a partial delimiter in the end of the bytes.
284 size_t size = static_cast<size_t>(bytesEnd - *bytesPointer);
285 delimiterBegin = bytesEnd - std::min(m_delimiter.size() - 1u, size);
286 for (;;) {
yhirano 2016/09/21 09:02:57 How about for (const char* begin = ...; begin < b
e_hakkinen 2016/09/22 22:27:16 Done. That decreases the number of times the loop
287 if (m_bufferedBytes.appendIfExpected(delimiterBegin, bytesEnd))
288 break;
289 m_bufferedBytes.clear();
290 DCHECK_LT(delimiterBegin, bytesEnd);
291 ++delimiterBegin;
292 }
293 // A partial delimiter (consisting of zero or more bytes) was found in
294 // the end of bytes. The partial delimiter bytes are buffered for now.
295 // The bytes before that are definitely octet bytes.
296 *bytesPointer = bytesEnd;
297 }
298
299 DCHECK(m_bufferedBytes.isComplete() || *bytesPointer == bytesEnd);
300 }
301
302 void MultipartParser::parseDelimiter(const char** bytesPointer, const char* byte sEnd)
303 {
304 DCHECK(!m_bufferedBytes.isComplete());
305 while (*bytesPointer < bytesEnd && m_bufferedBytes.appendIfExpected(*(*bytes Pointer))) {
306 ++(*bytesPointer);
307 if (m_bufferedBytes.isComplete())
308 break;
309 }
310 }
311
312 bool MultipartParser::parseHeaderFields(const char** bytesPointer, const char* b ytesEnd, WebURLResponse* response)
313 {
314 // Combine the current bytes with buffered header bytes if needed.
315 const char* headerBytes = *bytesPointer;
316 size_t headerSize = static_cast<size_t>(bytesEnd - *bytesPointer);
317 if (!m_bufferedHeaderBytes.isEmpty()) {
318 m_bufferedHeaderBytes.append(headerBytes, headerSize);
319 headerBytes = m_bufferedHeaderBytes.data();
320 headerSize = m_bufferedHeaderBytes.size();
321 }
322
323 size_t end = 0u;
324 if (!Platform::current()->parseMultipartHeadersFromBody(headerBytes, headerS ize, response, &end)) {
325 // Store the current header bytes for the next call unless that has
326 // already been done.
327 if (m_bufferedHeaderBytes.isEmpty())
328 m_bufferedHeaderBytes.append(headerBytes, headerSize);
329 *bytesPointer = bytesEnd;
330 return false;
331 }
332 m_bufferedHeaderBytes.clear();
333 *bytesPointer = bytesEnd - (headerSize - end);
334
335 return true;
336 }
337
338 void MultipartParser::parseTransportPadding(const char** bytesPointer, const cha r* bytesEnd) const
339 {
340 while (*bytesPointer < bytesEnd && (*(*bytesPointer) == '\t' || *(*bytesPoin ter) == ' '))
341 ++(*bytesPointer);
342 }
343
344 DEFINE_TRACE(MultipartParser)
345 {
346 visitor->trace(m_client);
347 }
348
349 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698