blob: eb65abb67a9f2c0745e89bc175665bf39563d3c3 [file] [log] [blame]
// 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 "third_party/blink/renderer/modules/eventsource/event_source_parser.h"
#include "base/stl_util.h"
#include "third_party/blink/renderer/core/event_type_names.h"
#include "third_party/blink/renderer/modules/eventsource/event_source.h"
#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "third_party/blink/renderer/platform/wtf/not_found.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
#include "third_party/blink/renderer/platform/wtf/text/text_encoding_registry.h"
namespace blink {
EventSourceParser::EventSourceParser(const AtomicString& last_event_id,
Client* client)
: id_(last_event_id),
last_event_id_(last_event_id),
client_(client),
codec_(NewTextCodec(UTF8Encoding())) {}
void EventSourceParser::AddBytes(const char* bytes, uint32_t size) {
// A line consists of |m_line| followed by
// |bytes[start..(next line break)]|.
uint32_t start = 0;
const unsigned char kBOM[] = {0xef, 0xbb, 0xbf};
for (uint32_t i = 0; i < size && !is_stopped_; ++i) {
// As kBOM contains neither CR nor LF, we can think BOM and the line
// break separately.
if (is_recognizing_bom_ && line_.size() + (i - start) == base::size(kBOM)) {
Vector<char> line = line_;
line.Append(&bytes[start], i - start);
DCHECK_EQ(line.size(), base::size(kBOM));
is_recognizing_bom_ = false;
if (memcmp(line.data(), kBOM, sizeof(kBOM)) == 0) {
start = i;
line_.clear();
continue;
}
}
if (is_recognizing_crlf_ && bytes[i] == '\n') {
// This is the latter part of "\r\n".
is_recognizing_crlf_ = false;
++start;
continue;
}
is_recognizing_crlf_ = false;
if (bytes[i] == '\r' || bytes[i] == '\n') {
line_.Append(&bytes[start], i - start);
ParseLine();
line_.clear();
start = i + 1;
is_recognizing_crlf_ = bytes[i] == '\r';
is_recognizing_bom_ = false;
}
}
if (is_stopped_)
return;
line_.Append(&bytes[start], size - start);
}
void EventSourceParser::ParseLine() {
if (line_.size() == 0) {
last_event_id_ = id_;
// We dispatch an event when seeing an empty line.
if (!data_.IsEmpty()) {
DCHECK_EQ(data_[data_.size() - 1], '\n');
String data = FromUTF8(data_.data(), data_.size() - 1);
client_->OnMessageEvent(
event_type_.IsEmpty() ? event_type_names::kMessage : event_type_,
data, last_event_id_);
data_.clear();
}
event_type_ = g_null_atom;
return;
}
wtf_size_t field_name_end = line_.Find(':');
wtf_size_t field_value_start;
if (field_name_end == WTF::kNotFound) {
field_name_end = line_.size();
field_value_start = field_name_end;
} else {
field_value_start = field_name_end + 1;
if (field_value_start < line_.size() && line_[field_value_start] == ' ') {
++field_value_start;
}
}
wtf_size_t field_value_size = line_.size() - field_value_start;
String field_name = FromUTF8(line_.data(), field_name_end);
if (field_name == "event") {
event_type_ = AtomicString(
FromUTF8(line_.data() + field_value_start, field_value_size));
return;
}
if (field_name == "data") {
data_.Append(line_.data() + field_value_start, field_value_size);
data_.push_back('\n');
return;
}
if (field_name == "id") {
if (!memchr(line_.data() + field_value_start, '\0', field_value_size)) {
id_ = AtomicString(
FromUTF8(line_.data() + field_value_start, field_value_size));
}
return;
}
if (field_name == "retry") {
bool has_only_digits = true;
for (wtf_size_t i = field_value_start; i < line_.size() && has_only_digits;
++i)
has_only_digits = IsASCIIDigit(line_[i]);
if (field_value_start == line_.size()) {
client_->OnReconnectionTimeSet(EventSource::kDefaultReconnectDelay);
} else if (has_only_digits) {
bool ok;
auto reconnection_time =
FromUTF8(line_.data() + field_value_start, field_value_size)
.ToUInt64Strict(&ok);
if (ok)
client_->OnReconnectionTimeSet(reconnection_time);
}
return;
}
// Unrecognized field name. Ignore!
}
String EventSourceParser::FromUTF8(const char* bytes, uint32_t size) {
return codec_->Decode(bytes, size, WTF::FlushBehavior::kDataEOF);
}
void EventSourceParser::Trace(blink::Visitor* visitor) {
visitor->Trace(client_);
}
} // namespace blink