Index: third_party/WebKit/Source/core/page/EventSourceParser.cpp |
diff --git a/third_party/WebKit/Source/core/page/EventSourceParser.cpp b/third_party/WebKit/Source/core/page/EventSourceParser.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..861bb6d3552a1e3b59bf5e6fb89895e3ccd35b5c |
--- /dev/null |
+++ b/third_party/WebKit/Source/core/page/EventSourceParser.cpp |
@@ -0,0 +1,135 @@ |
+// 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 "core/page/EventSourceParser.h" |
+ |
+#include "core/EventTypeNames.h" |
+#include "core/page/EventSource.h" |
+#include "wtf/ASCIICType.h" |
+#include "wtf/Assertions.h" |
+#include "wtf/NotFound.h" |
+#include "wtf/StdLibExtras.h" |
+#include "wtf/text/TextEncoding.h" |
+#include "wtf/text/TextEncodingRegistry.h" |
+ |
+namespace blink { |
+ |
+EventSourceParser::EventSourceParser(const AtomicString& lastEventId, Client* client) |
+ : m_id(lastEventId) |
+ , m_lastEventId(lastEventId) |
+ , m_client(client) |
+ , m_codec(newTextCodec(UTF8Encoding())) |
+{ |
+} |
+ |
+void EventSourceParser::addBytes(const char* bytes, size_t size) |
+{ |
+ // A line consists of |m_line| followed by |
+ // |bytes[start..(next line break)]|. |
+ size_t start = 0; |
+ const unsigned char kBOM[] = {0xef, 0xbb, 0xbf}; |
+ for (size_t i = 0; i < size && !m_isStopped; ++i) { |
+ // As kBOM contains neither CR nor LF, we can think BOM and the line |
+ // break separately. |
+ if (m_isRecognizingBOM && m_line.size() + (i - start) == WTF_ARRAY_LENGTH(kBOM)) { |
+ Vector<char> line = m_line; |
+ line.append(&bytes[start], i - start); |
+ ASSERT(line.size() == WTF_ARRAY_LENGTH(kBOM)); |
+ m_isRecognizingBOM = false; |
+ if (memcmp(line.data(), kBOM, sizeof(kBOM)) == 0) { |
+ start = i; |
+ m_line.clear(); |
+ continue; |
+ } |
+ } |
+ if (m_isRecognizingCRLF && bytes[i] == '\n') { |
+ // This is the latter part of "\r\n". |
+ m_isRecognizingCRLF = false; |
+ ++start; |
+ continue; |
+ } |
+ m_isRecognizingCRLF = false; |
+ if (bytes[i] == '\r' || bytes[i] == '\n') { |
+ m_line.append(&bytes[start], i - start); |
+ parseLine(); |
+ m_line.clear(); |
+ start = i + 1; |
+ m_isRecognizingCRLF = bytes[i] == '\r'; |
+ m_isRecognizingBOM = false; |
+ } |
+ } |
+ if (m_isStopped) |
+ return; |
+ m_line.append(&bytes[start], size - start); |
+} |
+ |
+void EventSourceParser::parseLine() |
+{ |
+ if (m_line.size() == 0) { |
+ m_lastEventId = m_id; |
+ // We dispatch an event when seeing an empty line. |
+ if (!m_data.isEmpty()) { |
+ ASSERT(m_data[m_data.size() - 1] == '\n'); |
+ String data = fromUTF8(m_data.data(), m_data.size() - 1); |
+ m_client->onMessageEvent(m_eventType.isEmpty() ? EventTypeNames::message : m_eventType, data, m_lastEventId); |
+ m_data.clear(); |
+ } |
+ m_eventType = nullAtom; |
+ return; |
+ } |
+ size_t fieldNameEnd = m_line.find(':'); |
+ size_t fieldValueStart; |
+ if (fieldNameEnd == WTF::kNotFound) { |
+ fieldNameEnd = m_line.size(); |
+ fieldValueStart = fieldNameEnd; |
+ } else { |
+ fieldValueStart = fieldNameEnd + 1; |
+ if (fieldValueStart < m_line.size() && m_line[fieldValueStart] == ' ') { |
+ ++fieldValueStart; |
+ } |
+ } |
+ size_t fieldValueSize = m_line.size() - fieldValueStart; |
+ String fieldName = fromUTF8(m_line.data(), fieldNameEnd); |
+ if (fieldName == "event") { |
+ m_eventType = AtomicString(fromUTF8(m_line.data() + fieldValueStart, fieldValueSize)); |
+ return; |
+ } |
+ if (fieldName == "data") { |
+ m_data.append(m_line.data() + fieldValueStart, fieldValueSize); |
+ m_data.append('\n'); |
+ return; |
+ } |
+ if (fieldName == "id") { |
+ m_id = AtomicString(fromUTF8(m_line.data() + fieldValueStart, fieldValueSize)); |
+ return; |
+ } |
+ if (fieldName == "retry") { |
+ bool hasOnlyDigits = true; |
+ for (size_t i = fieldValueStart; i < m_line.size() && hasOnlyDigits; ++i) |
+ hasOnlyDigits = isASCIIDigit(m_line[i]); |
+ if (fieldValueStart == m_line.size()) { |
+ m_client->onReconnectionTimeSet(EventSource::defaultReconnectDelay); |
+ } else if (hasOnlyDigits) { |
+ bool ok; |
+ auto reconnectionTime = fromUTF8(m_line.data() + fieldValueStart, fieldValueSize).toUInt64Strict(&ok); |
+ if (ok) |
+ m_client->onReconnectionTimeSet(reconnectionTime); |
+ } |
+ return; |
+ } |
+ // Unrecognized field name. Ignore! |
+} |
+ |
+String EventSourceParser::fromUTF8(const char* bytes, size_t size) |
+{ |
+ return m_codec->decode(bytes, size, WTF::DataEOF); |
+} |
+ |
+DEFINE_TRACE(EventSourceParser) |
+{ |
+ visitor->trace(m_client); |
+} |
+ |
+} // namespace blink |
+ |