summaryrefslogtreecommitdiffstats
path: root/remoting/client/log_to_server.cc
diff options
context:
space:
mode:
Diffstat (limited to 'remoting/client/log_to_server.cc')
-rw-r--r--remoting/client/log_to_server.cc194
1 files changed, 194 insertions, 0 deletions
diff --git a/remoting/client/log_to_server.cc b/remoting/client/log_to_server.cc
new file mode 100644
index 0000000..c45ad38
--- /dev/null
+++ b/remoting/client/log_to_server.cc
@@ -0,0 +1,194 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/client/log_to_server.h"
+
+#include "base/macros.h"
+#include "base/rand_util.h"
+#include "remoting/base/constants.h"
+#include "remoting/client/chromoting_stats.h"
+#include "remoting/jingle_glue/iq_sender.h"
+#include "remoting/jingle_glue/signal_strategy.h"
+#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
+#include "third_party/libjingle/source/talk/xmpp/constants.h"
+
+using buzz::QName;
+using buzz::XmlElement;
+using remoting::protocol::ConnectionToHost;
+
+namespace {
+
+const char kSessionIdAlphabet[] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
+const int kSessionIdLength = 20;
+
+const int kMaxSessionIdAgeDays = 1;
+
+bool IsStartOfSession(ConnectionToHost::State state) {
+ return state == ConnectionToHost::INITIALIZING ||
+ state == ConnectionToHost::CONNECTING ||
+ state == ConnectionToHost::AUTHENTICATED ||
+ state == ConnectionToHost::CONNECTED;
+}
+
+bool IsEndOfSession(ConnectionToHost::State state) {
+ return state == ConnectionToHost::FAILED ||
+ state == ConnectionToHost::CLOSED;
+}
+
+bool ShouldAddDuration(ConnectionToHost::State state) {
+ // Duration is added to log entries at the end of the session, as well as at
+ // some intermediate states where it is relevant (e.g. to determine how long
+ // it took for a session to become CONNECTED).
+ return IsEndOfSession(state) || state == ConnectionToHost::CONNECTED;
+}
+
+} // namespace
+
+namespace remoting {
+
+namespace client {
+
+LogToServer::LogToServer(ServerLogEntry::Mode mode,
+ SignalStrategy* signal_strategy,
+ const std::string& directory_bot_jid)
+ : mode_(mode),
+ signal_strategy_(signal_strategy),
+ directory_bot_jid_(directory_bot_jid) {
+ signal_strategy_->AddListener(this);
+}
+
+LogToServer::~LogToServer() {
+ signal_strategy_->RemoveListener(this);
+}
+
+void LogToServer::LogSessionStateChange(
+ protocol::ConnectionToHost::State state,
+ protocol::ErrorCode error) {
+ DCHECK(CalledOnValidThread());
+
+ scoped_ptr<ServerLogEntry> entry(
+ ServerLogEntry::MakeForSessionStateChange(state, error));
+ entry->AddClientFields();
+ entry->AddModeField(mode_);
+
+ MaybeExpireSessionId();
+ if (IsStartOfSession(state)) {
+ // Maybe set the session ID and start time.
+ if (session_id_.empty()) {
+ GenerateSessionId();
+ }
+ if (session_start_time_.is_null()) {
+ session_start_time_ = base::TimeTicks::Now();
+ }
+ }
+
+ if (!session_id_.empty()) {
+ entry->AddSessionId(session_id_);
+ }
+
+ // Maybe clear the session start time and log the session duration.
+ if (ShouldAddDuration(state) && !session_start_time_.is_null()) {
+ entry->AddSessionDuration(base::TimeTicks::Now() - session_start_time_);
+ }
+
+ if (IsEndOfSession(state)) {
+ session_start_time_ = base::TimeTicks();
+ session_id_.clear();
+ }
+
+ Log(*entry.get());
+}
+
+void LogToServer::LogStatistics(ChromotingStats* statistics) {
+ DCHECK(CalledOnValidThread());
+
+ MaybeExpireSessionId();
+
+ scoped_ptr<ServerLogEntry> entry(
+ ServerLogEntry::MakeForStatistics(statistics));
+ entry->AddClientFields();
+ entry->AddModeField(mode_);
+ entry->AddSessionId(session_id_);
+ Log(*entry.get());
+}
+
+void LogToServer::OnSignalStrategyStateChange(SignalStrategy::State state) {
+ DCHECK(CalledOnValidThread());
+
+ if (state == SignalStrategy::CONNECTED) {
+ iq_sender_.reset(new IqSender(signal_strategy_));
+ SendPendingEntries();
+ } else if (state == SignalStrategy::DISCONNECTED) {
+ iq_sender_.reset();
+ }
+}
+
+bool LogToServer::OnSignalStrategyIncomingStanza(
+ const buzz::XmlElement* stanza) {
+ return false;
+}
+
+void LogToServer::Log(const ServerLogEntry& entry) {
+ pending_entries_.push_back(entry);
+ SendPendingEntries();
+}
+
+void LogToServer::SendPendingEntries() {
+ if (iq_sender_ == NULL) {
+ return;
+ }
+ if (pending_entries_.empty()) {
+ return;
+ }
+ // Make one stanza containing all the pending entries.
+ scoped_ptr<XmlElement> stanza(ServerLogEntry::MakeStanza());
+ while (!pending_entries_.empty()) {
+ ServerLogEntry& entry = pending_entries_.front();
+ stanza->AddElement(entry.ToStanza().release());
+ pending_entries_.pop_front();
+ }
+ // Send the stanza to the server.
+ scoped_ptr<IqRequest> req = iq_sender_->SendIq(
+ buzz::STR_SET, directory_bot_jid_, stanza.Pass(),
+ IqSender::ReplyCallback());
+ // We ignore any response, so let the IqRequest be destroyed.
+ return;
+}
+
+void LogToServer::GenerateSessionId() {
+ session_id_.resize(kSessionIdLength);
+ for (int i = 0; i < kSessionIdLength; i++) {
+ const int alphabet_size = arraysize(kSessionIdAlphabet) - 1;
+ session_id_[i] = kSessionIdAlphabet[base::RandGenerator(alphabet_size)];
+ }
+ session_id_generation_time_ = base::TimeTicks::Now();
+}
+
+void LogToServer::MaybeExpireSessionId() {
+ if (session_id_.empty()) {
+ return;
+ }
+
+ base::TimeDelta max_age = base::TimeDelta::FromDays(kMaxSessionIdAgeDays);
+ if (base::TimeTicks::Now() - session_id_generation_time_ > max_age) {
+ // Log the old session ID.
+ scoped_ptr<ServerLogEntry> entry(
+ ServerLogEntry::MakeForSessionIdOld(session_id_));
+ entry->AddModeField(mode_);
+ Log(*entry.get());
+
+ // Generate a new session ID.
+ GenerateSessionId();
+
+ // Log the new session ID.
+ entry = ServerLogEntry::MakeForSessionIdNew(session_id_);
+ entry->AddModeField(mode_);
+ Log(*entry.get());
+ }
+}
+
+} // namespace client
+
+} // namespace remoting