| Index: extensions/browser/api/cast_channel/keep_alive_delegate.cc
|
| diff --git a/extensions/browser/api/cast_channel/keep_alive_delegate.cc b/extensions/browser/api/cast_channel/keep_alive_delegate.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c6803ebc9919cd1a9ba21586acfec881e408b95b
|
| --- /dev/null
|
| +++ b/extensions/browser/api/cast_channel/keep_alive_delegate.cc
|
| @@ -0,0 +1,200 @@
|
| +// Copyright 2015 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 "extensions/browser/api/cast_channel/keep_alive_delegate.h"
|
| +
|
| +#include <string>
|
| +#include <utility>
|
| +
|
| +#include "base/json/json_reader.h"
|
| +#include "base/json/json_writer.h"
|
| +#include "extensions/browser/api/cast_channel/cast_socket.h"
|
| +#include "extensions/browser/api/cast_channel/logger.h"
|
| +#include "extensions/common/api/cast_channel/cast_channel.pb.h"
|
| +#include "extensions/common/api/cast_channel/logging.pb.h"
|
| +#include "net/base/net_errors.h"
|
| +
|
| +namespace extensions {
|
| +namespace api {
|
| +namespace cast_channel {
|
| +namespace {
|
| +
|
| +const char kHeartbeatNamespace[] = "urn:x-cast:com.google.cast.tp.heartbeat";
|
| +const char kPingSenderId[] = "chrome";
|
| +const char kPingReceiverId[] = "receiver-0";
|
| +const char kTypeNodeId[] = "type";
|
| +
|
| +// Parses the JSON-encoded payload of |message| and returns the value in the
|
| +// "type" field or the empty string if the parse fails or the field is not
|
| +// found.
|
| +std::string ParseForPayloadType(const CastMessage& message) {
|
| + std::unique_ptr<base::Value> parsed_payload(
|
| + base::JSONReader::Read(message.payload_utf8()));
|
| + base::DictionaryValue* payload_as_dict;
|
| + if (!parsed_payload || !parsed_payload->GetAsDictionary(&payload_as_dict))
|
| + return std::string();
|
| + std::string type_string;
|
| + if (!payload_as_dict->GetString(kTypeNodeId, &type_string))
|
| + return std::string();
|
| + return type_string;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// static
|
| +const char KeepAliveDelegate::kHeartbeatPingType[] = "PING";
|
| +
|
| +// static
|
| +const char KeepAliveDelegate::kHeartbeatPongType[] = "PONG";
|
| +
|
| +using ::cast_channel::ChannelError;
|
| +
|
| +// static
|
| +CastMessage KeepAliveDelegate::CreateKeepAliveMessage(
|
| + const char* message_type) {
|
| + CastMessage output;
|
| + output.set_protocol_version(CastMessage::CASTV2_1_0);
|
| + output.set_source_id(kPingSenderId);
|
| + output.set_destination_id(kPingReceiverId);
|
| + output.set_namespace_(kHeartbeatNamespace);
|
| + base::DictionaryValue type_dict;
|
| + type_dict.SetString(kTypeNodeId, message_type);
|
| + if (!base::JSONWriter::Write(type_dict, output.mutable_payload_utf8())) {
|
| + LOG(ERROR) << "Failed to serialize dictionary.";
|
| + return output;
|
| + }
|
| + output.set_payload_type(
|
| + CastMessage::PayloadType::CastMessage_PayloadType_STRING);
|
| + return output;
|
| +}
|
| +
|
| +KeepAliveDelegate::KeepAliveDelegate(
|
| + CastSocket* socket,
|
| + scoped_refptr<Logger> logger,
|
| + std::unique_ptr<CastTransport::Delegate> inner_delegate,
|
| + base::TimeDelta ping_interval,
|
| + base::TimeDelta liveness_timeout)
|
| + : started_(false),
|
| + socket_(socket),
|
| + logger_(logger),
|
| + inner_delegate_(std::move(inner_delegate)),
|
| + liveness_timeout_(liveness_timeout),
|
| + ping_interval_(ping_interval) {
|
| + DCHECK(ping_interval_ < liveness_timeout_);
|
| + DCHECK(inner_delegate_);
|
| + DCHECK(socket_);
|
| + ping_message_ = CreateKeepAliveMessage(kHeartbeatPingType);
|
| + pong_message_ = CreateKeepAliveMessage(kHeartbeatPongType);
|
| +}
|
| +
|
| +KeepAliveDelegate::~KeepAliveDelegate() {
|
| +}
|
| +
|
| +void KeepAliveDelegate::SetTimersForTest(
|
| + std::unique_ptr<base::Timer> injected_ping_timer,
|
| + std::unique_ptr<base::Timer> injected_liveness_timer) {
|
| + ping_timer_ = std::move(injected_ping_timer);
|
| + liveness_timer_ = std::move(injected_liveness_timer);
|
| +}
|
| +
|
| +void KeepAliveDelegate::Start() {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + DCHECK(!started_);
|
| +
|
| + VLOG(1) << "Starting keep-alive timers.";
|
| + VLOG(1) << "Ping timeout: " << ping_interval_;
|
| + VLOG(1) << "Liveness timeout: " << liveness_timeout_;
|
| +
|
| + // Use injected mock timers, if provided.
|
| + if (!ping_timer_) {
|
| + ping_timer_.reset(new base::Timer(true, false));
|
| + }
|
| + if (!liveness_timer_) {
|
| + liveness_timer_.reset(new base::Timer(true, false));
|
| + }
|
| +
|
| + ping_timer_->Start(
|
| + FROM_HERE, ping_interval_,
|
| + base::Bind(&KeepAliveDelegate::SendKeepAliveMessage,
|
| + base::Unretained(this), ping_message_, kHeartbeatPingType));
|
| + liveness_timer_->Start(
|
| + FROM_HERE, liveness_timeout_,
|
| + base::Bind(&KeepAliveDelegate::LivenessTimeout, base::Unretained(this)));
|
| +
|
| + started_ = true;
|
| + inner_delegate_->Start();
|
| +}
|
| +
|
| +void KeepAliveDelegate::ResetTimers() {
|
| + DCHECK(started_);
|
| + ping_timer_->Reset();
|
| + liveness_timer_->Reset();
|
| +}
|
| +
|
| +void KeepAliveDelegate::SendKeepAliveMessage(const CastMessage& message,
|
| + const char* message_type) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + VLOG(2) << "Sending " << message_type;
|
| + socket_->transport()->SendMessage(
|
| + message, base::Bind(&KeepAliveDelegate::SendKeepAliveMessageComplete,
|
| + base::Unretained(this), message_type));
|
| +}
|
| +
|
| +void KeepAliveDelegate::SendKeepAliveMessageComplete(const char* message_type,
|
| + int rv) {
|
| + VLOG(2) << "Sending " << message_type << " complete, rv=" << rv;
|
| + if (rv != net::OK) {
|
| + // An error occurred while sending the ping response.
|
| + VLOG(1) << "Error sending " << message_type;
|
| + logger_->LogSocketEventWithRv(socket_->id(), proto::PING_WRITE_ERROR, rv);
|
| + OnError(ChannelError::CAST_SOCKET_ERROR);
|
| + }
|
| +}
|
| +
|
| +void KeepAliveDelegate::LivenessTimeout() {
|
| + OnError(ChannelError::PING_TIMEOUT);
|
| + Stop();
|
| +}
|
| +
|
| +// CastTransport::Delegate interface.
|
| +void KeepAliveDelegate::OnError(ChannelError error_state) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + VLOG(1) << "KeepAlive::OnError: "
|
| + << ::cast_channel::ChannelErrorToString(error_state);
|
| + inner_delegate_->OnError(error_state);
|
| + Stop();
|
| +}
|
| +
|
| +void KeepAliveDelegate::OnMessage(const CastMessage& message) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + VLOG(2) << "KeepAlive::OnMessage : " << message.payload_utf8();
|
| +
|
| + if (started_)
|
| + ResetTimers();
|
| +
|
| + // PING and PONG messages are intercepted and handled by KeepAliveDelegate
|
| + // here. All other messages are passed through to |inner_delegate_|.
|
| + const std::string payload_type = ParseForPayloadType(message);
|
| + if (payload_type == kHeartbeatPingType) {
|
| + VLOG(2) << "Received PING.";
|
| + if (started_)
|
| + SendKeepAliveMessage(pong_message_, kHeartbeatPongType);
|
| + } else if (payload_type == kHeartbeatPongType) {
|
| + VLOG(2) << "Received PONG.";
|
| + } else {
|
| + inner_delegate_->OnMessage(message);
|
| + }
|
| +}
|
| +
|
| +void KeepAliveDelegate::Stop() {
|
| + if (started_) {
|
| + started_ = false;
|
| + ping_timer_->Stop();
|
| + liveness_timer_->Stop();
|
| + }
|
| +}
|
| +
|
| +} // namespace cast_channel
|
| +} // namespace api
|
| +} // namespace extensions
|
|
|