| // 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/bufferevent.h> | |
| #include <event2/dns.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 |