Chromium Code Reviews| 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..6867f1e7fa5df21bd5961eb53b972188802dcc12 |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/core/page/EventSourceParser.cpp |
| @@ -0,0 +1,129 @@ |
| +// 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}; |
|
pfeldman
2016/02/11 18:54:41
Do you want to run this by security team since it
Tom Sepez
2016/02/12 17:14:31
The array indexing in here looks ok, what you do w
|
| + 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)); |
| + } else if (fieldName == "data") { |
|
kinuko
2016/02/18 09:15:00
nit: maybe return at the end of each if and make "
yhirano
2016/02/18 23:06:43
Done.
|
| + m_data.append(m_line.data() + fieldValueStart, fieldValueSize); |
| + m_data.append('\n'); |
| + } else if (fieldName == "id") { |
| + m_id = AtomicString(fromUTF8(m_line.data() + fieldValueStart, fieldValueSize)); |
| + } else 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); |
| + } |
| + } else { |
| + // 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 |
| + |