1 // Copyright 2014 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.
5 #include "remoting/client/log_to_server.h"
7 #include "base/macros.h"
8 #include "base/rand_util.h"
9 #include "remoting/base/constants.h"
10 #include "remoting/client/chromoting_stats.h"
11 #include "remoting/client/server_log_entry_client.h"
12 #include "remoting/jingle_glue/iq_sender.h"
13 #include "remoting/jingle_glue/signal_strategy.h"
14 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
15 #include "third_party/libjingle/source/talk/xmpp/constants.h"
18 using buzz::XmlElement
;
19 using remoting::protocol::ConnectionToHost
;
23 const char kSessionIdAlphabet
[] =
24 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
25 const int kSessionIdLength
= 20;
27 const int kMaxSessionIdAgeDays
= 1;
29 bool IsStartOfSession(ConnectionToHost::State state
) {
30 return state
== ConnectionToHost::INITIALIZING
||
31 state
== ConnectionToHost::CONNECTING
||
32 state
== ConnectionToHost::AUTHENTICATED
||
33 state
== ConnectionToHost::CONNECTED
;
36 bool IsEndOfSession(ConnectionToHost::State state
) {
37 return state
== ConnectionToHost::FAILED
||
38 state
== ConnectionToHost::CLOSED
;
41 bool ShouldAddDuration(ConnectionToHost::State state
) {
42 // Duration is added to log entries at the end of the session, as well as at
43 // some intermediate states where it is relevant (e.g. to determine how long
44 // it took for a session to become CONNECTED).
45 return IsEndOfSession(state
) || state
== ConnectionToHost::CONNECTED
;
54 LogToServer::LogToServer(ServerLogEntry::Mode mode
,
55 SignalStrategy
* signal_strategy
,
56 const std::string
& directory_bot_jid
)
58 signal_strategy_(signal_strategy
),
59 directory_bot_jid_(directory_bot_jid
) {
60 signal_strategy_
->AddListener(this);
63 LogToServer::~LogToServer() {
64 signal_strategy_
->RemoveListener(this);
67 void LogToServer::LogSessionStateChange(
68 protocol::ConnectionToHost::State state
,
69 protocol::ErrorCode error
) {
70 DCHECK(CalledOnValidThread());
72 scoped_ptr
<ServerLogEntry
> entry(
73 MakeLogEntryForSessionStateChange(state
, error
));
74 AddClientFieldsToLogEntry(entry
.get());
75 entry
->AddModeField(mode_
);
77 MaybeExpireSessionId();
78 if (IsStartOfSession(state
)) {
79 // Maybe set the session ID and start time.
80 if (session_id_
.empty()) {
83 if (session_start_time_
.is_null()) {
84 session_start_time_
= base::TimeTicks::Now();
88 if (!session_id_
.empty()) {
89 AddSessionIdToLogEntry(entry
.get(), session_id_
);
92 // Maybe clear the session start time and log the session duration.
93 if (ShouldAddDuration(state
) && !session_start_time_
.is_null()) {
94 AddSessionDurationToLogEntry(entry
.get(),
95 base::TimeTicks::Now() - session_start_time_
);
98 if (IsEndOfSession(state
)) {
99 session_start_time_
= base::TimeTicks();
106 void LogToServer::LogStatistics(ChromotingStats
* statistics
) {
107 DCHECK(CalledOnValidThread());
109 MaybeExpireSessionId();
111 scoped_ptr
<ServerLogEntry
> entry(MakeLogEntryForStatistics(statistics
));
112 AddClientFieldsToLogEntry(entry
.get());
113 entry
->AddModeField(mode_
);
114 AddSessionIdToLogEntry(entry
.get(), session_id_
);
118 void LogToServer::OnSignalStrategyStateChange(SignalStrategy::State state
) {
119 DCHECK(CalledOnValidThread());
121 if (state
== SignalStrategy::CONNECTED
) {
122 iq_sender_
.reset(new IqSender(signal_strategy_
));
123 SendPendingEntries();
124 } else if (state
== SignalStrategy::DISCONNECTED
) {
129 bool LogToServer::OnSignalStrategyIncomingStanza(
130 const buzz::XmlElement
* stanza
) {
134 void LogToServer::Log(const ServerLogEntry
& entry
) {
135 pending_entries_
.push_back(entry
);
136 SendPendingEntries();
139 void LogToServer::SendPendingEntries() {
140 if (iq_sender_
== NULL
) {
143 if (pending_entries_
.empty()) {
146 // Make one stanza containing all the pending entries.
147 scoped_ptr
<XmlElement
> stanza(ServerLogEntry::MakeStanza());
148 while (!pending_entries_
.empty()) {
149 ServerLogEntry
& entry
= pending_entries_
.front();
150 stanza
->AddElement(entry
.ToStanza().release());
151 pending_entries_
.pop_front();
153 // Send the stanza to the server.
154 scoped_ptr
<IqRequest
> req
= iq_sender_
->SendIq(
155 buzz::STR_SET
, directory_bot_jid_
, stanza
.Pass(),
156 IqSender::ReplyCallback());
157 // We ignore any response, so let the IqRequest be destroyed.
161 void LogToServer::GenerateSessionId() {
162 session_id_
.resize(kSessionIdLength
);
163 for (int i
= 0; i
< kSessionIdLength
; i
++) {
164 const int alphabet_size
= arraysize(kSessionIdAlphabet
) - 1;
165 session_id_
[i
] = kSessionIdAlphabet
[base::RandGenerator(alphabet_size
)];
167 session_id_generation_time_
= base::TimeTicks::Now();
170 void LogToServer::MaybeExpireSessionId() {
171 if (session_id_
.empty()) {
175 base::TimeDelta max_age
= base::TimeDelta::FromDays(kMaxSessionIdAgeDays
);
176 if (base::TimeTicks::Now() - session_id_generation_time_
> max_age
) {
177 // Log the old session ID.
178 scoped_ptr
<ServerLogEntry
> entry(MakeLogEntryForSessionIdOld(session_id_
));
179 entry
->AddModeField(mode_
);
182 // Generate a new session ID.
185 // Log the new session ID.
186 entry
= MakeLogEntryForSessionIdNew(session_id_
);
187 entry
->AddModeField(mode_
);
192 } // namespace client
194 } // namespace remoting