blob: 4230bcd7ec08b44984063564b0ddb3f750361311 [file]
// Copyright 2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "google/cacheinvalidation/network-manager.h"
#include <algorithm>
#include "google/cacheinvalidation/callback.h"
#include "google/cacheinvalidation/compiler-specific.h"
#include "google/cacheinvalidation/invalidation-client-impl.h"
#include "google/cacheinvalidation/log-macro.h"
#include "google/cacheinvalidation/logging.h"
#include "google/cacheinvalidation/stl-namespace.h"
#include "google/cacheinvalidation/string_util.h"
#include "google/cacheinvalidation/time.h"
namespace invalidation {
using INVALIDATION_STL_NAMESPACE::min;
NetworkManager::NetworkManager(
NetworkEndpoint* endpoint, SystemResources* resources,
const string& client_info, const ClientConfig& config)
: endpoint_(endpoint),
resources_(resources),
// Set the throttler up with rate limits defined by the config.
throttle_(config.rate_limits, resources,
NewPermanentCallback(
ALLOW_THIS_IN_INITIALIZER_LIST(this),
&NetworkManager::DoInformOutboundListener)),
has_outbound_data_(false),
outbound_listener_(NULL),
config_(config),
next_heartbeat_(Time() - TimeDelta::FromHours(1)),
heartbeat_delay_(config.initial_heartbeat_interval),
message_number_(0),
random_(resources->current_time().ToInternalValue()),
version_manager_(client_info) {
}
void NetworkManager::OutboundDataReady() {
if (!has_outbound_data_) {
has_outbound_data_ = true;
if (outbound_listener_ != NULL) {
InformOutboundListener();
}
}
}
void NetworkManager::ScheduleHeartbeat() {
Time now = resources_->current_time();
next_heartbeat_ = now + InvalidationClientImpl::SmearDelay(
heartbeat_delay_, config_.smear_factor, &random_);
TLOG(INFO_LEVEL, "Next heartbeat at %d", next_heartbeat_.ToInternalValue());
}
void NetworkManager::RegisterOutboundListener(
NetworkCallback* outbound_message_ready) {
CHECK(IsCallbackRepeatable(outbound_message_ready));
outbound_listener_ = outbound_message_ready;
if (has_outbound_data_) {
InformOutboundListener();
}
}
void NetworkManager::InformOutboundListener() {
throttle_.Fire();
}
void NetworkManager::DoInformOutboundListener() {
// Explicitness hack here to work around broken callback
// implementations.
void (NetworkCallback::*run_function)(NetworkEndpoint* const&) =
&NetworkCallback::Run;
// This call may have gotten deferred by the throttler, so check again that we
// have outbound data before scheduling the ping (the app could have pulled a
// bundle of its own accord in the mean time).
if (has_outbound_data_) {
TLOG(INFO_LEVEL, "scheduling outbound listener");
resources_->ScheduleOnListenerThread(
NewPermanentCallback(outbound_listener_, run_function, endpoint_));
}
}
void NetworkManager::AddHeartbeat(ClientToServerMessage* message) {
CHECK(message->message_type() ==
ClientToServerMessage_MessageType_TYPE_OBJECT_CONTROL);
Time now = resources_->current_time();
if (NeedsHeartbeat()) {
// Heartbeat required.
message->set_action(ClientToServerMessage_Action_HEARTBEAT);
ScheduleHeartbeat();
}
}
void NetworkManager::FinalizeOutboundMessage(ClientToServerMessage* message) {
++message_number_;
message->set_message_id(StringPrintf("%d", message_number_));
// Set the protocol version that we want to use.
VersionManager::GetLatestProtocolVersion(message->mutable_protocol_version());
// Set the client version.
version_manager_.GetClientVersion(message->mutable_client_version());
// Set a timestamp on the message. Internal time is in microseconds, so
// divide to get milliseconds.
message->set_timestamp(resources_->current_time().ToInternalValue() /
Time::kMicrosecondsPerMillisecond);
has_outbound_data_ = false;
}
void NetworkManager::HandleInboundMessage(const ServerToClientMessage& bundle) {
// Update the heartbeat interval.
if (bundle.has_next_heartbeat_interval_ms()) {
int new_heartbeat_interval_ms = bundle.next_heartbeat_interval_ms();
// Don't accept intervals of 0 or less -- that has to be bad data.
if (new_heartbeat_interval_ms > 0) {
TimeDelta new_heartbeat_interval =
TimeDelta::FromMilliseconds(new_heartbeat_interval_ms);
if (heartbeat_delay_ != new_heartbeat_interval) {
TLOG(INFO_LEVEL, "Accepting new heartbeat interval of %d ms",
new_heartbeat_interval_ms);
heartbeat_delay_ =
TimeDelta::FromMilliseconds(new_heartbeat_interval_ms);
// Schedule the next heartbeat using the new delay.
ScheduleHeartbeat();
} else {
// Heartbeat interval is unchanged: do nothing.
}
} else {
TLOG(INFO_LEVEL, "Ignoring bad server-provided heartbeat delay of %d ms",
new_heartbeat_interval_ms);
}
}
}
} // namespace invalidation