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 |