This commit is contained in:
commit
0415041917
41
.drone.yml
Normal file
41
.drone.yml
Normal file
@ -0,0 +1,41 @@
|
||||
kind: pipeline
|
||||
name: default
|
||||
type: docker
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: tystuyfzand/goc:latest
|
||||
volumes:
|
||||
- name: build
|
||||
path: /build
|
||||
commands:
|
||||
- mkdir -p /build
|
||||
- go mod download
|
||||
- goc -o /build/gogrok
|
||||
environment:
|
||||
GOOS: linux,windows,darwin
|
||||
GOARCH: 386,amd64,arm,arm64
|
||||
- name: release
|
||||
image: plugins/gitea-release
|
||||
volumes:
|
||||
- name: build
|
||||
path: /build
|
||||
settings:
|
||||
api_key:
|
||||
from_secret: gitea_token
|
||||
base_url: https://git.meow.tf
|
||||
title: release
|
||||
files:
|
||||
- /build/dlrouter_*
|
||||
checksum:
|
||||
- md5
|
||||
- sha1
|
||||
- sha256
|
||||
environment:
|
||||
PLUGIN_API_KEY:
|
||||
from_secret: gitea_token
|
||||
when:
|
||||
event: tag
|
||||
volumes:
|
||||
- name: build
|
||||
temp: {}
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.mmdb
|
5
LICENSE
Normal file
5
LICENSE
Normal file
@ -0,0 +1,5 @@
|
||||
Copyright (c) 2022 Tyler Stuyfzand <admin@meow.tf>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
11
dlrouter.yaml
Normal file
11
dlrouter.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
geodb: GeoIP2-City.mmdb
|
||||
|
||||
servers:
|
||||
- mirrors.tuna.tsinghua.edu.cn/armbian/
|
||||
- armbian.tnahosting.net/apt/
|
||||
- us.mirrors.fossho.st/armbian/armbian/
|
||||
- uk.mirrors.fossho.st/armbian/apt/
|
||||
- armbian.systemonachip.net/apt/
|
||||
- mirrors.netix.net/armbian/apt/
|
||||
- mirrors.dotsrc.org/armbian-apt/
|
||||
- armbian.lv.auroradev.org/apt/
|
27
go.mod
Normal file
27
go.mod
Normal file
@ -0,0 +1,27 @@
|
||||
module meow.tf/armbian-router
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/oschwald/maxminddb-golang v1.8.0
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
)
|
||||
|
||||
require github.com/spf13/viper v1.10.1
|
||||
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/spf13/afero v1.6.0 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
gopkg.in/ini.v1 v1.66.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
63
go.sum
Normal file
63
go.sum
Normal file
@ -0,0 +1,63 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
|
||||
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk=
|
||||
github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis=
|
||||
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
|
||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
|
||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk=
|
||||
github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486 h1:5hpz5aRr+W1erYCL5JRhSUBJRph7l9XkNveoExlrKYk=
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
|
||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
51
http.go
Normal file
51
http.go
Normal file
@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
)
|
||||
|
||||
func statusRequest(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func redirectRequest(w http.ResponseWriter, r *http.Request) {
|
||||
ipStr, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
ip := net.ParseIP(ipStr)
|
||||
|
||||
if ip.IsPrivate() {
|
||||
ip = net.ParseIP("1.1.1.1")
|
||||
}
|
||||
|
||||
server, distance, err := settings.Servers.Closest(ip)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
scheme := r.URL.Scheme
|
||||
|
||||
if scheme == "" {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
u := &url.URL{
|
||||
Scheme: scheme,
|
||||
Host: server.Host,
|
||||
Path: path.Join(server.Path, r.URL.Path),
|
||||
}
|
||||
|
||||
w.Header().Set("X-Geo-Distance", fmt.Sprintf("%f", distance))
|
||||
w.Header().Set("Location", u.String())
|
||||
w.WriteHeader(http.StatusFound)
|
||||
}
|
117
main.go
Normal file
117
main.go
Normal file
@ -0,0 +1,117 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/oschwald/maxminddb-golang"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
db *maxminddb.Reader
|
||||
settings = &Settings{}
|
||||
)
|
||||
|
||||
type City struct {
|
||||
Location struct {
|
||||
Latitude float64 `maxminddb:"latitude"`
|
||||
Longitude float64 `maxminddb:"longitude"`
|
||||
} `maxminddb:"location"`
|
||||
}
|
||||
|
||||
type Settings struct {
|
||||
Servers ServerList
|
||||
}
|
||||
|
||||
func main() {
|
||||
viper.SetConfigName("dlrouter") // name of config file (without extension)
|
||||
viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
|
||||
viper.AddConfigPath("/etc/dlrouter/") // path to look for the config file in
|
||||
viper.AddConfigPath("$HOME/.dlrouter") // call multiple times to add many search paths
|
||||
viper.AddConfigPath(".") // optionally look for config in the working directory
|
||||
err := viper.ReadInConfig() // Find and read the config file
|
||||
|
||||
if err != nil { // Handle errors reading the config file
|
||||
log.WithError(err).Fatalln("Unable to load config file")
|
||||
}
|
||||
|
||||
db, err = maxminddb.Open(viper.GetString("geodb"))
|
||||
|
||||
if err != nil {
|
||||
log.WithError(err).Fatalln("Unable to open database")
|
||||
}
|
||||
|
||||
servers := viper.GetStringSlice("servers")
|
||||
|
||||
for _, server := range servers {
|
||||
var prefix string
|
||||
|
||||
if !strings.HasPrefix(server, "http") {
|
||||
prefix = "https://"
|
||||
}
|
||||
|
||||
u, err := url.Parse(prefix + server)
|
||||
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"error": err,
|
||||
"server": server,
|
||||
}).Warning("Server is invalid")
|
||||
continue
|
||||
}
|
||||
|
||||
ips, err := net.LookupIP(u.Host)
|
||||
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"error": err,
|
||||
"server": server,
|
||||
}).Warning("Could not resolve address")
|
||||
continue
|
||||
}
|
||||
|
||||
var city City
|
||||
err = db.Lookup(ips[0], &city)
|
||||
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"error": err,
|
||||
"server": server,
|
||||
}).Warning("Could not geolocate address")
|
||||
continue
|
||||
}
|
||||
|
||||
settings.Servers = append(settings.Servers, &Server{
|
||||
Host: u.Host,
|
||||
Path: u.Path,
|
||||
Latitude: city.Location.Latitude,
|
||||
Longitude: city.Location.Longitude,
|
||||
})
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"server": u.Host,
|
||||
"path": u.Path,
|
||||
"latitude": city.Location.Latitude,
|
||||
"longitude": city.Location.Longitude,
|
||||
}).Info("Added server")
|
||||
}
|
||||
|
||||
log.Info("Servers added, checking statuses")
|
||||
// Force initial check before running
|
||||
settings.Servers.Check()
|
||||
|
||||
// Start check loop
|
||||
go settings.Servers.checkLoop()
|
||||
|
||||
log.Info("Starting")
|
||||
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc("/status", RealIPMiddleware(statusRequest))
|
||||
mux.HandleFunc("/", RealIPMiddleware(redirectRequest))
|
||||
|
||||
http.ListenAndServe(":8080", mux)
|
||||
}
|
86
middleware.go
Normal file
86
middleware.go
Normal file
@ -0,0 +1,86 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For")
|
||||
xForwardedProto = http.CanonicalHeaderKey("X-Forwarded-Proto")
|
||||
xRealIP = http.CanonicalHeaderKey("X-Real-IP")
|
||||
forwardLimit = 5
|
||||
)
|
||||
|
||||
func RealIPMiddleware(f http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// Treat unix socket as 127.0.0.1
|
||||
if r.RemoteAddr == "@" {
|
||||
r.RemoteAddr = "127.0.0.1:0"
|
||||
}
|
||||
|
||||
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
|
||||
if err != nil {
|
||||
f.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if net.ParseIP(host).IsPrivate() {
|
||||
f.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if rip := realIP(r); len(rip) > 0 {
|
||||
r.RemoteAddr = net.JoinHostPort(rip, "0")
|
||||
}
|
||||
|
||||
if rproto := realProto(r); len(rproto) > 0 {
|
||||
r.URL.Scheme = rproto
|
||||
}
|
||||
|
||||
f.ServeHTTP(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func realIP(r *http.Request) string {
|
||||
var ip string
|
||||
|
||||
if xrip := r.Header.Get(xRealIP); xrip != "" {
|
||||
ip = xrip
|
||||
} else if xff := r.Header.Get(xForwardedFor); xff != "" {
|
||||
p := 0
|
||||
for i := forwardLimit; i > 0; i-- {
|
||||
if p > 0 {
|
||||
xff = xff[:p-2]
|
||||
}
|
||||
p = strings.LastIndex(xff, ", ")
|
||||
if p < 0 {
|
||||
p = 0
|
||||
break
|
||||
} else {
|
||||
p += 2
|
||||
}
|
||||
}
|
||||
|
||||
ip = xff[p:]
|
||||
}
|
||||
|
||||
return ip
|
||||
}
|
||||
|
||||
func realProto(r *http.Request) string {
|
||||
proto := "http"
|
||||
|
||||
if r.TLS != nil {
|
||||
proto = "https"
|
||||
}
|
||||
|
||||
if xproto := r.Header.Get(xForwardedProto); xproto != "" {
|
||||
proto = xproto
|
||||
}
|
||||
|
||||
return proto
|
||||
}
|
||||
|
131
servers.go
Normal file
131
servers.go
Normal file
@ -0,0 +1,131 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
checkClient = &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
Available bool
|
||||
Host string
|
||||
Path string
|
||||
Latitude float64
|
||||
Longitude float64
|
||||
}
|
||||
|
||||
type ServerList []*Server
|
||||
|
||||
func (s ServerList) checkLoop() {
|
||||
t := time.NewTicker(60 * time.Second)
|
||||
|
||||
for {
|
||||
<- t.C
|
||||
|
||||
s.Check()
|
||||
}
|
||||
}
|
||||
|
||||
func (s ServerList) Check() {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for _, server := range s {
|
||||
wg.Add(1)
|
||||
|
||||
go func(server *Server) {
|
||||
req, err := http.NewRequest(http.MethodGet, "https://" + server.Host + "/" + strings.TrimLeft(server.Path, "/"), nil)
|
||||
|
||||
req.Header.Set("User-Agent", "ArmbianRouter/1.0 (Go " + runtime.Version() + ")")
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res, err := checkClient.Do(req)
|
||||
|
||||
if err != nil {
|
||||
log.WithField("server", server.Host).Info("Server went offline")
|
||||
server.Available = false
|
||||
return
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
wg.Done()
|
||||
}(server)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (s ServerList) Closest(ip net.IP) (*Server, float64, error) {
|
||||
var city City
|
||||
err := db.Lookup(ip, &city)
|
||||
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
|
||||
var closest *Server
|
||||
var closestDistance float64 = -1
|
||||
|
||||
for _, server := range s {
|
||||
if !server.Available {
|
||||
continue
|
||||
}
|
||||
|
||||
distance := Distance(city.Location.Latitude, city.Location.Longitude, server.Latitude, server.Longitude)
|
||||
|
||||
if closestDistance == -1 || distance < closestDistance {
|
||||
closestDistance = distance
|
||||
closest = server
|
||||
}
|
||||
}
|
||||
|
||||
return closest, closestDistance, nil
|
||||
}
|
||||
|
||||
// haversin(θ) function
|
||||
func hsin(theta float64) float64 {
|
||||
return math.Pow(math.Sin(theta/2), 2)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// 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
|
||||
|
||||
r = 6378100 // Earth radius in METERS
|
||||
|
||||
// calculate
|
||||
h := hsin(la2-la1) + math.Cos(la1)*math.Cos(la2)*hsin(lo2-lo1)
|
||||
|
||||
return 2 * r * math.Asin(math.Sqrt(h))
|
||||
}
|
Loading…
Reference in New Issue
Block a user