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