blob: ba03e8f1ddcaf3a89378a2776a25dd1c842ad2ca [file] [log] [blame]
// Copyright 2015 The Weave 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 "examples/provider/event_network.h"
#include <weave/enum_to_string.h>
#include <base/bind.h>
#include <event2/dns.h>
#include <event2/bufferevent.h>
#include "examples/provider/event_task_runner.h"
#include "examples/provider/ssl_stream.h"
namespace weave {
namespace examples {
namespace {
const char kNetworkProbeHostname[] = "talk.google.com";
const int kNetworkProbePort = 5223;
const int kNetworkProbeTimeoutS = 2;
} // namespace
void EventNetworkImpl::Deleter::operator()(evdns_base* dns_base) {
evdns_base_free(dns_base, 0);
}
void EventNetworkImpl::Deleter::operator()(bufferevent* bev) {
bufferevent_free(bev);
}
EventNetworkImpl::EventNetworkImpl(EventTaskRunner* task_runner)
: task_runner_(task_runner) {
UpdateNetworkState();
}
void EventNetworkImpl::AddConnectionChangedCallback(
const ConnectionChangedCallback& callback) {
callbacks_.push_back(callback);
}
void EventNetworkImpl::UpdateNetworkState() {
if (simulate_offline_) {
LOG(INFO) << "Simulating offline state";
connectivity_probe_.reset();
return UpdateNetworkStateCallback(State::kOffline);
}
connectivity_probe_.reset(
bufferevent_socket_new(task_runner_->GetEventBase(), -1,
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS));
timeval timeout{kNetworkProbeTimeoutS, 0};
bufferevent_set_timeouts(connectivity_probe_.get(), &timeout, &timeout);
bufferevent_setcb(
connectivity_probe_.get(), nullptr, nullptr,
[](struct bufferevent* buf, short events, void* ctx) {
EventNetworkImpl* network = static_cast<EventNetworkImpl*>(ctx);
if (events & BEV_EVENT_CONNECTED) {
network->UpdateNetworkStateCallback(State::kOnline);
return;
}
if (events & (BEV_EVENT_ERROR | BEV_EVENT_EOF | BEV_EVENT_TIMEOUT)) {
int err = bufferevent_socket_get_dns_error(buf);
if (err) {
LOG(ERROR) << "network connect dns error: "
<< evutil_gai_strerror(err);
}
network->UpdateNetworkStateCallback(State::kOffline);
return;
}
},
this);
int err = bufferevent_socket_connect_hostname(
connectivity_probe_.get(), dns_base_.get(), AF_INET,
kNetworkProbeHostname, kNetworkProbePort);
if (err) {
LOG(ERROR) << " network connect socket error: " << evutil_gai_strerror(err);
return UpdateNetworkStateCallback(State::kOffline);
}
}
void EventNetworkImpl::UpdateNetworkStateCallback(
provider::Network::State state) {
if (state != network_state_) {
LOG(INFO) << "network state updated: " << weave::EnumToString(state);
network_state_ = state;
// In general it's better to send false notification than miss one.
// However current implementation can only send them very often on every
// UpdateNetworkStateCallback or just here, guarder with this if condition.
for (const auto& cb : callbacks_)
cb.Run();
}
// Reset current posted task.
weak_ptr_factory_.InvalidateWeakPtrs();
// TODO(proppy): use netlink interface event instead of polling
task_runner_->PostDelayedTask(
FROM_HERE, base::Bind(&EventNetworkImpl::UpdateNetworkState,
weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromSeconds(10));
}
weave::provider::Network::State EventNetworkImpl::GetConnectionState() const {
return network_state_;
}
void EventNetworkImpl::OpenSslSocket(const std::string& host,
uint16_t port,
const OpenSslSocketCallback& callback) {
SSLStream::Connect(task_runner_, host, port, callback);
}
} // namespace examples
} // namespace weave