Index: third_party/WebKit/Source/modules/fetch/MultipartParser.cpp |
diff --git a/third_party/WebKit/Source/modules/fetch/MultipartParser.cpp b/third_party/WebKit/Source/modules/fetch/MultipartParser.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d1b6b2a250cf8da0df959e156b6db892296c1252 |
--- /dev/null |
+++ b/third_party/WebKit/Source/modules/fetch/MultipartParser.cpp |
@@ -0,0 +1,239 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "modules/fetch/MultipartParser.h" |
+ |
+#include "public/platform/Platform.h" |
+ |
+#include <algorithm> |
+#include <utility> |
+ |
+namespace blink { |
+ |
+namespace { |
+ constexpr char kCloseDelimiterSuffix[] = "--\r\n"; |
+ constexpr char kDelimiterSuffix[] = "\r\n"; |
+ constexpr size_t kDelimiterOffsetForEmptyPreamble = 2u; // For no "\r\n" prefix. |
+ constexpr size_t kDelimiterOffsetForEmptyBody = kDelimiterOffsetForEmptyPreamble; |
+} |
yhirano
2016/09/12 02:20:49
// namespace
e_hakkinen
2016/09/16 13:41:36
Done.
|
+ |
+MultipartParser::MultipartParser(Vector<char> boundary, Client* client) |
+ : m_delimiter(std::move(boundary)) |
+ , m_client(client) |
+ , m_seenDelimiterLength(0u) |
+ , m_seenDelimiterOffset(kDelimiterOffsetForEmptyPreamble) |
+{ |
+ // The delimiter consists of "\r\n" and a dash delimiter which consists of |
+ // "--" and a boundary. |
+ m_delimiter.prepend("\r\n--", 4u); |
+} |
+ |
+bool MultipartParser::appendData(const char* bytes, size_t size) |
+{ |
+ DCHECK_NE(Finished, m_state); |
+ DCHECK_NE(Cancelled, m_state); |
+ |
+ while (size > 0u) { |
+ size_t index = 0u; |
+ |
+ switch (m_state) { |
+ case ParsingPreamble: |
+ // Parse either a preamble and a delimiter or a dash delimiter. |
+ if (parseDelimiter(bytes, size, &index)) |
+ m_state = ParsingDelimiterSuffix; |
+ break; |
+ |
+ case ParsingDelimiterSuffix: |
+ // Parse transport padding and "\r\n" after a delimiter. |
+ if (parseDelimiterSuffix(bytes, size, &index, kDelimiterSuffix)) |
+ m_state = ParsingPartHeaderFields; |
+ break; |
+ |
+ case ParsingPartHeaderFields: { |
+ // Parse part header fields (which ends with "\r\n") and an empty |
+ // line (which also ends with "\r\n"). |
+ WebURLResponse response; |
+ |
+ // Combine the current bytes with previously seen header bytes if |
+ // needed. |
+ const char* headerBytes = bytes + index; |
+ size_t headerSize = size - index; |
+ if (!m_seenHeaderBytes.isEmpty()) { |
+ m_seenHeaderBytes.append(headerBytes, headerSize); |
+ headerBytes = m_seenHeaderBytes.data(); |
+ headerSize = m_seenHeaderBytes.size(); |
+ } |
+ |
+ size_t end = 0; |
+ if (!Platform::current()->parseMultipartHeadersFromBody(headerBytes, headerSize, &response, &end)) { |
+ // Store the current bytes for the next call. |
+ if (headerBytes != m_seenHeaderBytes.data()) |
yhirano
2016/09/12 02:20:49
if (m_seenHeaderBytes.isEmpty()) is easier to unde
e_hakkinen
2016/09/16 13:41:36
Done.
|
+ m_seenHeaderBytes.append(headerBytes, headerSize); |
+ return true; |
+ } |
+ |
+ m_seenDelimiterLength = 0u; |
+ m_seenDelimiterOffset = kDelimiterOffsetForEmptyBody; |
+ m_seenHeaderBytes.shrink(0); |
yhirano
2016/09/12 02:20:49
clear()
e_hakkinen
2016/09/16 13:41:36
Done.
|
+ m_state = ParsingPartOctets; |
+ index = size - (headerSize - end); |
+ m_client->partHeaderFieldsInMultipartReceived( |
+ response.toResourceResponse()); |
+ break; |
+ } |
+ |
+ case ParsingPartOctets: { |
+ // Parse either a non-empty part octets and a delimiter or an empty |
+ // part octets and a dash delimiter. |
+ size_t initialSeenDelimiterLength = m_seenDelimiterLength; |
+ size_t initialSeenDelimiterOffset = m_seenDelimiterOffset; |
+ if (parseDelimiter(bytes, size, &index)) |
+ m_state = ParsingDelimiterOrCloseDelimiterSuffix; |
+ if (index >= m_seenDelimiterLength && initialSeenDelimiterLength > 0u) |
+ m_client->partDataInMultipartReceived(m_delimiter.data() + initialSeenDelimiterOffset, initialSeenDelimiterLength); |
+ if (index > m_seenDelimiterLength) |
+ m_client->partDataInMultipartReceived(bytes, index - m_seenDelimiterLength); |
+ if (m_state == ParsingDelimiterOrCloseDelimiterSuffix) |
+ m_client->partDataInMultipartFullyReceived(); |
+ break; |
+ } |
+ |
+ case ParsingDelimiterOrCloseDelimiterSuffix: |
+ m_state = bytes[index] != '-' ? ParsingDelimiterSuffix : ParsingCloseDelimiterSuffix; |
yhirano
2016/09/12 02:20:49
This is not correct. When parsing the first "dash-
e_hakkinen
2016/09/16 13:41:36
No, it is correct.
yhirano
2016/09/20 09:49:53
Thank you, I understand.
|
+ break; |
+ |
+ case ParsingCloseDelimiterSuffix: |
+ // Parse "--", transport padding and "\r\n" after a delimiter |
+ // (a delimiter and "--" constitute a close delimiter). |
+ if (parseDelimiterSuffix(bytes, size, &index, kCloseDelimiterSuffix)) |
+ m_state = ParsingEpilogue; |
+ break; |
+ |
+ case ParsingEpilogue: |
+ // Data in an epilogue should be ignored. |
+ return true; |
+ |
+ case Cancelled: |
+ case Finished: |
+ // The client changed the state. |
+ return true; |
+ |
+ case Failed: |
+ // Keep failing. |
+ return false; |
+ } |
+ |
+ bytes += index; |
+ size -= index; |
+ } |
+ |
+ return true; |
+} |
+ |
+void MultipartParser::cancel() |
+{ |
+ m_state = Cancelled; |
+} |
+ |
+bool MultipartParser::finish() |
+{ |
+ DCHECK_NE(Cancelled, m_state); |
+ |
+ State initialState = m_state; |
+ |
+ if (m_state == ParsingPartOctets && m_seenDelimiterLength > 0u && m_seenDelimiterOffset + m_seenDelimiterLength < m_delimiter.size()) { |
+ // The end of append bytes looked like a delimiter but was not a full |
+ // one, after all. Treat the those bytes as part of part octets. |
+ m_client->partDataInMultipartReceived( |
+ m_delimiter.data() + m_seenDelimiterOffset, m_seenDelimiterLength); |
+ } |
+ m_state = Finished; |
+ |
+ switch (initialState) { |
+ case ParsingCloseDelimiterSuffix: |
+ // Require a full close delimiter consisting of a delimiter and "--" |
+ // but ignore missing or partial "\r\n" after that. |
+ return seenDelimiterSuffixLength() >= 2u; |
+ case ParsingEpilogue: |
+ case Finished: |
+ return true; |
+ default: |
+ return false; |
+ } |
+} |
+ |
+size_t MultipartParser::countNonDelimiterBytes(const char* bytes, size_t size) const |
+{ |
+ const char* p = static_cast<const char*>(memchr(bytes, '\r', size)); |
+ if (p) |
+ return static_cast<size_t>(p - bytes); |
+ return size; |
+} |
+ |
+size_t MultipartParser::countPossibleDelimiterBytes(const char* bytes, size_t size) const |
+{ |
+ size_t index = 0u; |
+ while (index < size && m_seenDelimiterOffset + m_seenDelimiterLength + index < m_delimiter.size() && bytes[index] == m_delimiter[m_seenDelimiterOffset + m_seenDelimiterLength + index]) |
+ ++index; |
+ return index; |
+} |
+ |
+size_t MultipartParser::countTransportPaddingBytes(const char* bytes, size_t size) const |
+{ |
+ size_t index = 0u; |
+ while (index < size && (bytes[index] == '\t' || bytes[index] == ' ')) |
+ ++index; |
+ return index; |
+} |
+ |
+bool MultipartParser::parseDelimiter(const char* bytes, size_t size, size_t* index) |
+{ |
+ for (;;) { |
+ // Try to continue reading a delimiter. |
+ size_t possibleDelimiterBytes = countPossibleDelimiterBytes(bytes + *index, size - *index); |
+ if (possibleDelimiterBytes > 0u) { |
yhirano
2016/09/12 02:20:49
Is it OK to execute these statements unconditional
|
+ m_seenDelimiterLength += possibleDelimiterBytes; |
+ *index += possibleDelimiterBytes; |
+ } |
yhirano
2016/09/12 02:20:49
DCHECK_LE(*index, size);
|
+ size_t seenDelimiterEnd = m_seenDelimiterOffset + m_seenDelimiterLength; |
+ if (seenDelimiterEnd == m_delimiter.size()) |
+ return true; |
+ if (*index >= size) |
+ break; |
+ |
+ // Jump to the next possible delimiter or to the end of bytes. |
+ m_seenDelimiterLength = 0u; |
+ m_seenDelimiterOffset = 0u; |
+ *index += countNonDelimiterBytes(bytes + *index, size - *index); |
yhirano
2016/09/12 02:20:49
DCHECK_LE(*index, size);
|
+ } |
+ return false; |
+} |
+ |
+bool MultipartParser::parseDelimiterSuffix(const char* bytes, size_t size, size_t* index, const char* suffix) |
+{ |
+ while (char expected = suffix[seenDelimiterSuffixLength()]) { |
+ if (expected == '\r') |
yhirano
2016/09/12 02:20:49
Hmm... I don't like this special case. The functio
e_hakkinen
2016/09/16 13:41:36
Removed.
|
+ *index += countTransportPaddingBytes(bytes + *index, size - *index); |
+ if (*index >= size) |
+ return false; |
+ if (bytes[(*index)++] != expected) { |
+ m_state = Failed; |
+ return false; |
+ } |
+ ++m_seenDelimiterLength; |
yhirano
2016/09/12 02:20:49
I don't understand - what you are seeing here is n
e_hakkinen
2016/09/16 13:41:36
Sorry for reusing m_seenDelimiterLength for m_seen
|
+ } |
+ return true; |
+} |
+ |
+size_t MultipartParser::seenDelimiterSuffixLength() const |
+{ |
+ return m_seenDelimiterOffset + m_seenDelimiterLength - m_delimiter.size(); |
+} |
+ |
+DEFINE_TRACE(MultipartParser) |
+{ |
+ visitor->trace(m_client); |
+} |
+ |
+} // namespace blink |