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

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: More global-interface-listing*-expected.txt, urlencoded-parser-expected.txt Created 3 years, 7 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 } // namespace
26
27 MultipartParser::Matcher::Matcher() = default;
28
29 MultipartParser::Matcher::Matcher(const char* data,
30 size_t num_matched_bytes,
31 size_t size)
32 : data_(data), num_matched_bytes_(num_matched_bytes), size_(size) {}
33
34 bool MultipartParser::Matcher::Match(const char* first, const char* last) {
35 while (first < last) {
36 if (!Match(*first++))
37 return false;
38 }
39 return true;
40 }
41
42 void MultipartParser::Matcher::SetNumMatchedBytes(size_t num_matched_bytes) {
43 DCHECK_LE(num_matched_bytes, size_);
44 num_matched_bytes_ = num_matched_bytes;
45 }
46
47 MultipartParser::MultipartParser(Vector<char> boundary, Client* client)
48 : client_(client),
49 delimiter_(std::move(boundary)),
50 state_(State::kParsingPreamble) {
51 // The delimiter consists of "\r\n" and a dash boundary which consists of
52 // "--" and a boundary.
53 delimiter_.push_front("\r\n--", 4u);
54 matcher_ = DelimiterMatcher(kDashBoundaryOffset);
55 }
56
57 bool MultipartParser::AppendData(const char* bytes, size_t size) {
58 DCHECK_NE(State::kFinished, state_);
59 DCHECK_NE(State::kCancelled, state_);
60
61 const char* const bytes_end = bytes + size;
62
63 while (bytes < bytes_end) {
64 switch (state_) {
65 case State::kParsingPreamble:
66 // Parse either a preamble and a delimiter or a dash boundary.
67 ParseDelimiter(&bytes, bytes_end);
68 if (!matcher_.IsMatchComplete() && bytes < bytes_end) {
69 // Parse a preamble data (by ignoring it) and then a delimiter.
70 matcher_.SetNumMatchedBytes(0u);
71 ParseDataAndDelimiter(&bytes, bytes_end);
72 }
73 if (matcher_.IsMatchComplete()) {
74 // Prepare for a delimiter suffix.
75 matcher_ = DelimiterSuffixMatcher();
76 state_ = State::kParsingDelimiterSuffix;
77 }
78 break;
79
80 case State::kParsingDelimiterSuffix:
81 // Parse transport padding and "\r\n" after a delimiter.
82 // This state can be reached after either a preamble or part
83 // octets are parsed.
84 if (matcher_.NumMatchedBytes() == 0u)
85 ParseTransportPadding(&bytes, bytes_end);
86 while (bytes < bytes_end) {
87 if (!matcher_.Match(*bytes++))
88 return false;
89 if (matcher_.IsMatchComplete()) {
90 // Prepare for part header fields.
91 state_ = State::kParsingPartHeaderFields;
92 break;
93 }
94 }
95 break;
96
97 case State::kParsingPartHeaderFields: {
98 // Parse part header fields (which ends with "\r\n") and an empty
99 // line (which also ends with "\r\n").
100 // This state can be reached after a delimiter and a delimiter
101 // suffix after either a preamble or part octets are parsed.
102 HTTPHeaderMap header_fields;
103 if (ParseHeaderFields(&bytes, bytes_end, &header_fields)) {
104 // Prepare for part octets.
105 matcher_ = DelimiterMatcher();
106 state_ = State::kParsingPartOctets;
107 client_->PartHeaderFieldsInMultipartReceived(header_fields);
108 }
109 break;
110 }
111
112 case State::kParsingPartOctets: {
113 // Parse part octets and a delimiter.
114 // This state can be reached only after part header fields are
115 // parsed.
116 const size_t num_initially_matched_bytes = matcher_.NumMatchedBytes();
117 const char* octets_begin = bytes;
118 ParseDelimiter(&bytes, bytes_end);
119 if (!matcher_.IsMatchComplete() && bytes < bytes_end) {
120 if (matcher_.NumMatchedBytes() >= num_initially_matched_bytes &&
121 num_initially_matched_bytes > 0u) {
122 // Since the matched bytes did not form a complete
123 // delimiter, the matched bytes turned out to be octet
124 // bytes instead of being delimiter bytes. Additionally,
125 // some of the matched bytes are from the previous call and
126 // are therefore not in the range [octetsBegin, bytesEnd[.
127 client_->PartDataInMultipartReceived(matcher_.Data(),
128 matcher_.NumMatchedBytes());
129 if (state_ != State::kParsingPartOctets)
130 break;
131 octets_begin = bytes;
132 }
133 matcher_.SetNumMatchedBytes(0u);
134 ParseDataAndDelimiter(&bytes, bytes_end);
135 const char* const octets_end = bytes - matcher_.NumMatchedBytes();
136 if (octets_begin < octets_end) {
137 client_->PartDataInMultipartReceived(
138 octets_begin, static_cast<size_t>(octets_end - octets_begin));
139 if (state_ != State::kParsingPartOctets)
140 break;
141 }
142 }
143 if (matcher_.IsMatchComplete()) {
144 state_ = State::kParsingDelimiterOrCloseDelimiterSuffix;
145 client_->PartDataInMultipartFullyReceived();
146 }
147 break;
148 }
149
150 case State::kParsingDelimiterOrCloseDelimiterSuffix:
151 // Determine whether this is a delimiter suffix or a close
152 // delimiter suffix.
153 // This state can be reached only after part octets are parsed.
154 if (*bytes == '-') {
155 // Prepare for a close delimiter suffix.
156 matcher_ = CloseDelimiterSuffixMatcher();
157 state_ = State::kParsingCloseDelimiterSuffix;
158 } else {
159 // Prepare for a delimiter suffix.
160 matcher_ = DelimiterSuffixMatcher();
161 state_ = State::kParsingDelimiterSuffix;
162 }
163 break;
164
165 case State::kParsingCloseDelimiterSuffix:
166 // Parse "--", transport padding and "\r\n" after a delimiter
167 // (a delimiter and "--" constitute a close delimiter).
168 // This state can be reached only after part octets are parsed.
169 for (;;) {
170 if (matcher_.NumMatchedBytes() == 2u)
171 ParseTransportPadding(&bytes, bytes_end);
172 if (bytes >= bytes_end)
173 break;
174 if (!matcher_.Match(*bytes++))
175 return false;
176 if (matcher_.IsMatchComplete()) {
177 // Prepare for an epilogue.
178 state_ = State::kParsingEpilogue;
179 break;
180 }
181 }
182 break;
183
184 case State::kParsingEpilogue:
185 // Parse an epilogue (by ignoring it).
186 // This state can be reached only after a delimiter and a close
187 // delimiter suffix after part octets are parsed.
188 return true;
189
190 case State::kCancelled:
191 case State::kFinished:
192 // The client changed the state.
193 return false;
194 }
195 }
196
197 DCHECK_EQ(bytes_end, bytes);
198
199 return true;
200 }
201
202 void MultipartParser::Cancel() {
203 state_ = State::kCancelled;
204 }
205
206 bool MultipartParser::Finish() {
207 DCHECK_NE(State::kCancelled, state_);
208 DCHECK_NE(State::kFinished, state_);
209
210 const State initial_state = state_;
211 state_ = State::kFinished;
212
213 switch (initial_state) {
214 case State::kParsingPartOctets:
215 if (matcher_.NumMatchedBytes() > 0u) {
216 // Since the matched bytes did not form a complete delimiter,
217 // the matched bytes turned out to be octet bytes instead of being
218 // delimiter bytes.
219 client_->PartDataInMultipartReceived(matcher_.Data(),
220 matcher_.NumMatchedBytes());
221 }
222 return false;
223 case State::kParsingCloseDelimiterSuffix:
224 // Require a full close delimiter consisting of a delimiter and "--"
225 // but ignore missing or partial "\r\n" after that.
226 return matcher_.NumMatchedBytes() >= 2u;
227 case State::kParsingEpilogue:
228 return true;
229 default:
230 return false;
231 }
232 }
233
234 MultipartParser::Matcher MultipartParser::CloseDelimiterSuffixMatcher() const {
235 return Matcher(kCloseDelimiterSuffix, 0u, kCloseDelimiterSuffixSize);
236 }
237
238 MultipartParser::Matcher MultipartParser::DelimiterMatcher(
239 size_t num_already_matched_bytes) const {
240 return Matcher(delimiter_.data(), num_already_matched_bytes,
241 delimiter_.size());
242 }
243
244 MultipartParser::Matcher MultipartParser::DelimiterSuffixMatcher() const {
245 return Matcher(kDelimiterSuffix, 0u, kDelimiterSuffixSize);
246 }
247
248 void MultipartParser::ParseDataAndDelimiter(const char** bytes_pointer,
249 const char* bytes_end) {
250 DCHECK_EQ(0u, matcher_.NumMatchedBytes());
251
252 // Search for a complete delimiter within the bytes.
253 const char* delimiter_begin = std::search(
254 *bytes_pointer, bytes_end, delimiter_.begin(), delimiter_.end());
255 if (delimiter_begin != bytes_end) {
256 // A complete delimiter was found. The bytes before that are octet
257 // bytes.
258 const char* const delimiter_end = delimiter_begin + delimiter_.size();
259 const bool matched = matcher_.Match(delimiter_begin, delimiter_end);
260 DCHECK(matched);
261 DCHECK(matcher_.IsMatchComplete());
262 *bytes_pointer = delimiter_end;
263 } else {
264 // Search for a partial delimiter in the end of the bytes.
265 const size_t size = static_cast<size_t>(bytes_end - *bytes_pointer);
266 for (delimiter_begin = bytes_end - std::min(delimiter_.size() - 1u, size);
267 delimiter_begin < bytes_end; ++delimiter_begin) {
268 if (matcher_.Match(delimiter_begin, bytes_end))
269 break;
270 matcher_.SetNumMatchedBytes(0u);
271 }
272 // If a partial delimiter was found in the end of bytes, the bytes
273 // before the partial delimiter are definitely octets bytes and
274 // the partial delimiter bytes are buffered for now.
275 // If a partial delimiter was not found in the end of bytes, all bytes
276 // are definitely octets bytes.
277 // In all cases, all bytes are parsed now.
278 *bytes_pointer = bytes_end;
279 }
280
281 DCHECK(matcher_.IsMatchComplete() || *bytes_pointer == bytes_end);
282 }
283
284 void MultipartParser::ParseDelimiter(const char** bytes_pointer,
285 const char* bytes_end) {
286 DCHECK(!matcher_.IsMatchComplete());
287 while (*bytes_pointer < bytes_end && matcher_.Match(*(*bytes_pointer))) {
288 ++(*bytes_pointer);
289 if (matcher_.IsMatchComplete())
290 break;
291 }
292 }
293
294 bool MultipartParser::ParseHeaderFields(const char** bytes_pointer,
295 const char* bytes_end,
296 HTTPHeaderMap* header_fields) {
297 // Combine the current bytes with buffered header bytes if needed.
298 const char* header_bytes = *bytes_pointer;
299 size_t header_size = static_cast<size_t>(bytes_end - *bytes_pointer);
300 if (!buffered_header_bytes_.IsEmpty()) {
301 buffered_header_bytes_.Append(header_bytes, header_size);
302 header_bytes = buffered_header_bytes_.data();
303 header_size = buffered_header_bytes_.size();
304 }
305
306 size_t end = 0u;
307 if (!ParseMultipartFormHeadersFromBody(header_bytes, header_size,
308 header_fields, &end)) {
309 // Store the current header bytes for the next call unless that has
310 // already been done.
311 if (buffered_header_bytes_.IsEmpty())
312 buffered_header_bytes_.Append(header_bytes, header_size);
313 *bytes_pointer = bytes_end;
314 return false;
315 }
316 buffered_header_bytes_.clear();
317 *bytes_pointer = bytes_end - (header_size - end);
318
319 return true;
320 }
321
322 void MultipartParser::ParseTransportPadding(const char** bytes_pointer,
323 const char* bytes_end) const {
324 while (*bytes_pointer < bytes_end &&
325 (*(*bytes_pointer) == '\t' || *(*bytes_pointer) == ' '))
326 ++(*bytes_pointer);
327 }
328
329 DEFINE_TRACE(MultipartParser) {
330 visitor->Trace(client_);
331 }
332
333 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698