1 // Copyright 2019 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 #ifndef REMOTING_HOST_HEARTBEAT_SENDER_H_
6 #define REMOTING_HOST_HEARTBEAT_SENDER_H_
7 
8 #include <memory>
9 #include <string>
10 
11 #include "base/callback.h"
12 #include "base/gtest_prod_util.h"
13 #include "base/macros.h"
14 #include "base/sequence_checker.h"
15 #include "base/timer/timer.h"
16 #include "net/base/backoff_entry.h"
17 #include "remoting/proto/remoting/v1/directory_messages.pb.h"
18 #include "remoting/signaling/signal_strategy.h"
19 
20 namespace base {
21 class TimeDelta;
22 }  // namespace base
23 
24 namespace network {
25 class SharedURLLoaderFactory;
26 }  // namespace network
27 
28 namespace remoting {
29 
30 class OAuthTokenGetter;
31 class ProtobufHttpStatus;
32 
33 // HeartbeatSender periodically sends heartbeat to the directory service. See
34 // the HeartbeatRequest message in directory_messages.proto for more details.
35 //
36 // Normally the heartbeat indicates that the host is healthy and ready to
37 // accept new connections from a client, but the message can optionally include
38 // a host_offline_reason field, which indicates that the host cannot accept
39 // connections from the client (and might possibly be shutting down).  The value
40 // of the host_offline_reason field can be either a string from
41 // host_exit_codes.cc (i.e. "INVALID_HOST_CONFIGURATION" string) or one of
42 // kHostOfflineReasonXxx constants (i.e. "POLICY_READ_ERROR" string).
43 //
44 // The heartbeat sender will verify that the channel is in fact active before
45 // sending out the heartbeat. If not, it will disconnect the signaling strategy
46 // so that the signaling connector will try to reconnect signaling.
47 //
48 // The server sends a HeartbeatResponse in response to each successful
49 // heartbeat, which may contain a remote command to be executed on the host,
50 // e.g. restarting the host process upon reception of the response.
51 class HeartbeatSender final : public SignalStrategy::Listener {
52  public:
53   class Delegate {
54    public:
55     virtual ~Delegate() = default;
56 
57     // Invoked after the first successful heartbeat.
58     virtual void OnFirstHeartbeatSuccessful() = 0;
59 
60     // Invoked when the host is not found in the directory.
61     virtual void OnHostNotFound() = 0;
62 
63     // Invoked when the heartbeat sender permanently fails to authenticate the
64     // requests.
65     virtual void OnAuthFailed() = 0;
66 
67     // Invoked when the host has been asked to restart.
68     virtual void OnRemoteRestartHost() = 0;
69 
70    protected:
71     Delegate() = default;
72   };
73 
74   // Interface to track heartbeat events for diagnosis purpose.
75   class Observer {
76    public:
77     virtual ~Observer() = default;
78 
79     // Invoked when the heartbeat sender has sent a heartbeat.
80     virtual void OnHeartbeatSent() = 0;
81 
82    protected:
83     Observer() = default;
84   };
85 
86   // All raw pointers must be non-null and outlive this object.
87   HeartbeatSender(
88       Delegate* delegate,
89       const std::string& host_id,
90       SignalStrategy* signal_strategy,
91       OAuthTokenGetter* oauth_token_getter,
92       Observer* observer,
93       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
94       bool is_googler);
95   ~HeartbeatSender() override;
96 
97   // Sets host offline reason for future heartbeat, and initiates sending a
98   // heartbeat right away.
99   //
100   // For discussion of allowed values for |host_offline_reason| argument,
101   // please see the description in the class-level comments above.
102   //
103   // |ack_callback| will be called when the server acks receiving the
104   // |host_offline_reason| or when |timeout| is reached.
105   void SetHostOfflineReason(
106       const std::string& host_offline_reason,
107       const base::TimeDelta& timeout,
108       base::OnceCallback<void(bool success)> ack_callback);
109 
110  private:
111   class HeartbeatClient {
112    public:
113     using HeartbeatResponseCallback =
114         base::OnceCallback<void(const ProtobufHttpStatus&,
115                                 std::unique_ptr<apis::v1::HeartbeatResponse>)>;
116 
117     virtual ~HeartbeatClient() = default;
118 
119     virtual void Heartbeat(std::unique_ptr<apis::v1::HeartbeatRequest> request,
120                            HeartbeatResponseCallback callback) = 0;
121     virtual void CancelPendingRequests() = 0;
122   };
123 
124   class HeartbeatClientImpl;
125 
126   friend class HeartbeatSenderTest;
127 
128   // SignalStrategy::Listener interface.
129   void OnSignalStrategyStateChange(SignalStrategy::State state) override;
130   bool OnSignalStrategyIncomingStanza(
131       const jingle_xmpp::XmlElement* stanza) override;
132 
133   void SendHeartbeat();
134   void OnResponse(const ProtobufHttpStatus& status,
135                   std::unique_ptr<apis::v1::HeartbeatResponse> response);
136 
137   // Handlers for host-offline-reason completion and timeout.
138   void OnHostOfflineReasonTimeout();
139   void OnHostOfflineReasonAck();
140 
141   void OnRemoteCommand(
142       apis::v1::HeartbeatResponse::RemoteCommand remote_command);
143 
144   // Helper methods used by DoSendStanza() to generate heartbeat stanzas.
145   std::unique_ptr<apis::v1::HeartbeatRequest> CreateHeartbeatRequest();
146 
147   Delegate* delegate_;
148   std::string host_id_;
149   SignalStrategy* const signal_strategy_;
150   std::unique_ptr<HeartbeatClient> client_;
151   OAuthTokenGetter* const oauth_token_getter_;
152   Observer* observer_;
153 
154   base::OneShotTimer heartbeat_timer_;
155 
156   net::BackoffEntry backoff_;
157 
158   bool initial_heartbeat_sent_ = false;
159 
160   bool is_googler_ = false;
161 
162   // Fields to send and indicate completion of sending host-offline-reason.
163   std::string host_offline_reason_;
164   base::OnceCallback<void(bool success)> host_offline_reason_ack_callback_;
165   base::OneShotTimer host_offline_reason_timeout_timer_;
166 
167   SEQUENCE_CHECKER(sequence_checker_);
168 
169   DISALLOW_COPY_AND_ASSIGN(HeartbeatSender);
170 };
171 
172 }  // namespace remoting
173 
174 #endif  // REMOTING_HOST_HEARTBEAT_SENDER_H_
175