 Chromium Code Reviews
 Chromium Code Reviews Issue 2292763002:
  [Fetch API] Implement Request.formData and Response.formData.  (Closed)
    
  
    Issue 2292763002:
  [Fetch API] Implement Request.formData and Response.formData.  (Closed) 
  | 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 "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 | |
| OLD | NEW |