armbian-router/servers.go

144 lines
3.3 KiB
Go
Raw Permalink Normal View History

2022-01-10 04:47:40 +00:00
package main
import (
2022-01-10 04:53:23 +00:00
log "github.com/sirupsen/logrus"
"math"
"net"
"net/http"
"runtime"
"strings"
"sync"
"time"
2022-01-10 04:47:40 +00:00
)
var (
2022-01-10 04:53:23 +00:00
checkClient = &http.Client{
Timeout: 10 * time.Second,
}
2022-01-10 04:47:40 +00:00
)
type Server struct {
2022-01-10 04:53:23 +00:00
Available bool
Host string
Path string
Latitude float64
Longitude float64
2022-01-10 04:47:40 +00:00
}
type ServerList []*Server
func (s ServerList) checkLoop() {
2022-01-10 04:53:23 +00:00
t := time.NewTicker(60 * time.Second)
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
for {
<-t.C
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
s.Check()
}
2022-01-10 04:47:40 +00:00
}
2022-01-10 04:53:23 +00:00
// Check will request the index from all servers
// If a server does not respond in 10 seconds, it is considered offline.
// This will wait until all checks are complete.
2022-01-10 04:47:40 +00:00
func (s ServerList) Check() {
2022-01-10 04:53:23 +00:00
var wg sync.WaitGroup
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
for _, server := range s {
wg.Add(1)
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
go func(server *Server) {
req, err := http.NewRequest(http.MethodGet, "https://"+server.Host+"/"+strings.TrimLeft(server.Path, "/"), nil)
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
req.Header.Set("User-Agent", "ArmbianRouter/1.0 (Go "+runtime.Version()+")")
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
if err != nil {
// This should never happen.
log.WithError(err).Warning("Invalid request! This should not happen, please check config.")
wg.Done()
2022-01-10 04:53:23 +00:00
return
}
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
res, err := checkClient.Do(req)
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
if err != nil {
if server.Available {
log.WithField("server", server.Host).Info("Server went offline")
server.Available = false
}
wg.Done()
2022-01-10 04:53:23 +00:00
return
}
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
if (res.StatusCode == http.StatusOK || res.StatusCode == http.StatusMovedPermanently || res.StatusCode == http.StatusFound) &&
!server.Available {
server.Available = true
log.WithField("server", server.Host).Info("Server is online")
}
2022-01-10 04:53:23 +00:00
wg.Done()
}(server)
}
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
wg.Wait()
2022-01-10 04:47:40 +00:00
}
2022-01-10 04:53:23 +00:00
// Closest will use GeoIP on the IP provided and find the closest server.
// Return values are the closest server, the distance, and if an error occurred.
2022-01-10 04:47:40 +00:00
func (s ServerList) Closest(ip net.IP) (*Server, float64, error) {
2022-01-10 04:53:23 +00:00
var city City
err := db.Lookup(ip, &city)
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
if err != nil {
return nil, -1, err
}
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
var closest *Server
var closestDistance float64 = -1
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
for _, server := range s {
if !server.Available {
continue
}
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
distance := Distance(city.Location.Latitude, city.Location.Longitude, server.Latitude, server.Longitude)
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
if closestDistance == -1 || distance < closestDistance {
closestDistance = distance
closest = server
}
}
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
return closest, closestDistance, nil
2022-01-10 04:47:40 +00:00
}
// haversin(θ) function
func hsin(theta float64) float64 {
2022-01-10 04:53:23 +00:00
return math.Pow(math.Sin(theta/2), 2)
2022-01-10 04:47:40 +00:00
}
// Distance function returns the distance (in meters) between two points of
// a given longitude and latitude relatively accurately (using a spherical
// approximation of the Earth) through the Haversin Distance Formula for
// great arc distance on a sphere with accuracy for small distances
//
// point coordinates are supplied in degrees and converted into rad. in the func
//
// distance returned is METERS!!!!!!
// http://en.wikipedia.org/wiki/Haversine_formula
func Distance(lat1, lon1, lat2, lon2 float64) float64 {
2022-01-10 04:53:23 +00:00
// convert to radians
// must cast radius as float to multiply later
var la1, lo1, la2, lo2, r float64
la1 = lat1 * math.Pi / 180
lo1 = lon1 * math.Pi / 180
la2 = lat2 * math.Pi / 180
lo2 = lon2 * math.Pi / 180
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
r = 6378100 // Earth radius in METERS
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
// calculate
h := hsin(la2-la1) + math.Cos(la1)*math.Cos(la2)*hsin(lo2-lo1)
2022-01-10 04:47:40 +00:00
2022-01-10 04:53:23 +00:00
return 2 * r * math.Asin(math.Sqrt(h))
}