OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "extensions/browser/api/cast_channel/keep_alive_delegate.h" |
| 6 |
| 7 #include <string> |
| 8 #include <utility> |
| 9 |
| 10 #include "base/json/json_reader.h" |
| 11 #include "base/json/json_writer.h" |
| 12 #include "extensions/browser/api/cast_channel/cast_socket.h" |
| 13 #include "extensions/browser/api/cast_channel/logger.h" |
| 14 #include "extensions/common/api/cast_channel/cast_channel.pb.h" |
| 15 #include "extensions/common/api/cast_channel/logging.pb.h" |
| 16 #include "net/base/net_errors.h" |
| 17 |
| 18 namespace extensions { |
| 19 namespace api { |
| 20 namespace cast_channel { |
| 21 namespace { |
| 22 |
| 23 const char kHeartbeatNamespace[] = "urn:x-cast:com.google.cast.tp.heartbeat"; |
| 24 const char kPingSenderId[] = "chrome"; |
| 25 const char kPingReceiverId[] = "receiver-0"; |
| 26 const char kTypeNodeId[] = "type"; |
| 27 |
| 28 // Parses the JSON-encoded payload of |message| and returns the value in the |
| 29 // "type" field or the empty string if the parse fails or the field is not |
| 30 // found. |
| 31 std::string ParseForPayloadType(const CastMessage& message) { |
| 32 std::unique_ptr<base::Value> parsed_payload( |
| 33 base::JSONReader::Read(message.payload_utf8())); |
| 34 base::DictionaryValue* payload_as_dict; |
| 35 if (!parsed_payload || !parsed_payload->GetAsDictionary(&payload_as_dict)) |
| 36 return std::string(); |
| 37 std::string type_string; |
| 38 if (!payload_as_dict->GetString(kTypeNodeId, &type_string)) |
| 39 return std::string(); |
| 40 return type_string; |
| 41 } |
| 42 |
| 43 } // namespace |
| 44 |
| 45 // static |
| 46 const char KeepAliveDelegate::kHeartbeatPingType[] = "PING"; |
| 47 |
| 48 // static |
| 49 const char KeepAliveDelegate::kHeartbeatPongType[] = "PONG"; |
| 50 |
| 51 using ::cast_channel::ChannelError; |
| 52 |
| 53 // static |
| 54 CastMessage KeepAliveDelegate::CreateKeepAliveMessage( |
| 55 const char* message_type) { |
| 56 CastMessage output; |
| 57 output.set_protocol_version(CastMessage::CASTV2_1_0); |
| 58 output.set_source_id(kPingSenderId); |
| 59 output.set_destination_id(kPingReceiverId); |
| 60 output.set_namespace_(kHeartbeatNamespace); |
| 61 base::DictionaryValue type_dict; |
| 62 type_dict.SetString(kTypeNodeId, message_type); |
| 63 if (!base::JSONWriter::Write(type_dict, output.mutable_payload_utf8())) { |
| 64 LOG(ERROR) << "Failed to serialize dictionary."; |
| 65 return output; |
| 66 } |
| 67 output.set_payload_type( |
| 68 CastMessage::PayloadType::CastMessage_PayloadType_STRING); |
| 69 return output; |
| 70 } |
| 71 |
| 72 KeepAliveDelegate::KeepAliveDelegate( |
| 73 CastSocket* socket, |
| 74 scoped_refptr<Logger> logger, |
| 75 std::unique_ptr<CastTransport::Delegate> inner_delegate, |
| 76 base::TimeDelta ping_interval, |
| 77 base::TimeDelta liveness_timeout) |
| 78 : started_(false), |
| 79 socket_(socket), |
| 80 logger_(logger), |
| 81 inner_delegate_(std::move(inner_delegate)), |
| 82 liveness_timeout_(liveness_timeout), |
| 83 ping_interval_(ping_interval) { |
| 84 DCHECK(ping_interval_ < liveness_timeout_); |
| 85 DCHECK(inner_delegate_); |
| 86 DCHECK(socket_); |
| 87 ping_message_ = CreateKeepAliveMessage(kHeartbeatPingType); |
| 88 pong_message_ = CreateKeepAliveMessage(kHeartbeatPongType); |
| 89 } |
| 90 |
| 91 KeepAliveDelegate::~KeepAliveDelegate() { |
| 92 } |
| 93 |
| 94 void KeepAliveDelegate::SetTimersForTest( |
| 95 std::unique_ptr<base::Timer> injected_ping_timer, |
| 96 std::unique_ptr<base::Timer> injected_liveness_timer) { |
| 97 ping_timer_ = std::move(injected_ping_timer); |
| 98 liveness_timer_ = std::move(injected_liveness_timer); |
| 99 } |
| 100 |
| 101 void KeepAliveDelegate::Start() { |
| 102 DCHECK(thread_checker_.CalledOnValidThread()); |
| 103 DCHECK(!started_); |
| 104 |
| 105 VLOG(1) << "Starting keep-alive timers."; |
| 106 VLOG(1) << "Ping timeout: " << ping_interval_; |
| 107 VLOG(1) << "Liveness timeout: " << liveness_timeout_; |
| 108 |
| 109 // Use injected mock timers, if provided. |
| 110 if (!ping_timer_) { |
| 111 ping_timer_.reset(new base::Timer(true, false)); |
| 112 } |
| 113 if (!liveness_timer_) { |
| 114 liveness_timer_.reset(new base::Timer(true, false)); |
| 115 } |
| 116 |
| 117 ping_timer_->Start( |
| 118 FROM_HERE, ping_interval_, |
| 119 base::Bind(&KeepAliveDelegate::SendKeepAliveMessage, |
| 120 base::Unretained(this), ping_message_, kHeartbeatPingType)); |
| 121 liveness_timer_->Start( |
| 122 FROM_HERE, liveness_timeout_, |
| 123 base::Bind(&KeepAliveDelegate::LivenessTimeout, base::Unretained(this))); |
| 124 |
| 125 started_ = true; |
| 126 inner_delegate_->Start(); |
| 127 } |
| 128 |
| 129 void KeepAliveDelegate::ResetTimers() { |
| 130 DCHECK(started_); |
| 131 ping_timer_->Reset(); |
| 132 liveness_timer_->Reset(); |
| 133 } |
| 134 |
| 135 void KeepAliveDelegate::SendKeepAliveMessage(const CastMessage& message, |
| 136 const char* message_type) { |
| 137 DCHECK(thread_checker_.CalledOnValidThread()); |
| 138 VLOG(2) << "Sending " << message_type; |
| 139 socket_->transport()->SendMessage( |
| 140 message, base::Bind(&KeepAliveDelegate::SendKeepAliveMessageComplete, |
| 141 base::Unretained(this), message_type)); |
| 142 } |
| 143 |
| 144 void KeepAliveDelegate::SendKeepAliveMessageComplete(const char* message_type, |
| 145 int rv) { |
| 146 VLOG(2) << "Sending " << message_type << " complete, rv=" << rv; |
| 147 if (rv != net::OK) { |
| 148 // An error occurred while sending the ping response. |
| 149 VLOG(1) << "Error sending " << message_type; |
| 150 logger_->LogSocketEventWithRv(socket_->id(), proto::PING_WRITE_ERROR, rv); |
| 151 OnError(ChannelError::CAST_SOCKET_ERROR); |
| 152 } |
| 153 } |
| 154 |
| 155 void KeepAliveDelegate::LivenessTimeout() { |
| 156 OnError(ChannelError::PING_TIMEOUT); |
| 157 Stop(); |
| 158 } |
| 159 |
| 160 // CastTransport::Delegate interface. |
| 161 void KeepAliveDelegate::OnError(ChannelError error_state) { |
| 162 DCHECK(thread_checker_.CalledOnValidThread()); |
| 163 VLOG(1) << "KeepAlive::OnError: " |
| 164 << ::cast_channel::ChannelErrorToString(error_state); |
| 165 inner_delegate_->OnError(error_state); |
| 166 Stop(); |
| 167 } |
| 168 |
| 169 void KeepAliveDelegate::OnMessage(const CastMessage& message) { |
| 170 DCHECK(thread_checker_.CalledOnValidThread()); |
| 171 VLOG(2) << "KeepAlive::OnMessage : " << message.payload_utf8(); |
| 172 |
| 173 if (started_) |
| 174 ResetTimers(); |
| 175 |
| 176 // PING and PONG messages are intercepted and handled by KeepAliveDelegate |
| 177 // here. All other messages are passed through to |inner_delegate_|. |
| 178 const std::string payload_type = ParseForPayloadType(message); |
| 179 if (payload_type == kHeartbeatPingType) { |
| 180 VLOG(2) << "Received PING."; |
| 181 if (started_) |
| 182 SendKeepAliveMessage(pong_message_, kHeartbeatPongType); |
| 183 } else if (payload_type == kHeartbeatPongType) { |
| 184 VLOG(2) << "Received PONG."; |
| 185 } else { |
| 186 inner_delegate_->OnMessage(message); |
| 187 } |
| 188 } |
| 189 |
| 190 void KeepAliveDelegate::Stop() { |
| 191 if (started_) { |
| 192 started_ = false; |
| 193 ping_timer_->Stop(); |
| 194 liveness_timer_->Stop(); |
| 195 } |
| 196 } |
| 197 |
| 198 } // namespace cast_channel |
| 199 } // namespace api |
| 200 } // namespace extensions |
OLD | NEW |