chore(all): move sub-packages to internal/provider
This commit is contained in:
76
internal/provider/privateinternetaccess/updater/api.go
Normal file
76
internal/provider/privateinternetaccess/updater/api.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package privateinternetaccess
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK")
|
||||
)
|
||||
|
||||
type apiData struct {
|
||||
Regions []regionData `json:"regions"`
|
||||
}
|
||||
|
||||
type regionData struct {
|
||||
Name string `json:"name"`
|
||||
DNS string `json:"dns"`
|
||||
PortForward bool `json:"port_forward"`
|
||||
Offline bool `json:"offline"`
|
||||
Servers struct {
|
||||
UDP []serverData `json:"ovpnudp"`
|
||||
TCP []serverData `json:"ovpntcp"`
|
||||
} `json:"servers"`
|
||||
}
|
||||
|
||||
type serverData struct {
|
||||
IP net.IP `json:"ip"`
|
||||
CN string `json:"cn"`
|
||||
}
|
||||
|
||||
func fetchAPI(ctx context.Context, client *http.Client) (
|
||||
data apiData, err error) {
|
||||
const url = "https://serverlist.piaservers.net/vpninfo/servers/v5"
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return data, fmt.Errorf("%w: %d %s", ErrHTTPStatusCodeNotOK,
|
||||
response.StatusCode, response.Status)
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
if err := response.Body.Close(); err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
// remove key/signature at the bottom
|
||||
i := bytes.IndexRune(b, '\n')
|
||||
b = b[:i]
|
||||
|
||||
if err := json.Unmarshal(b, &data); err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package privateinternetaccess
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
type nameToServer map[string]models.Server
|
||||
|
||||
func (nts nameToServer) add(name, hostname, region string,
|
||||
tcp, udp, portForward bool, ip net.IP) (change bool) {
|
||||
server, ok := nts[name]
|
||||
if !ok {
|
||||
change = true
|
||||
server.VPN = vpn.OpenVPN
|
||||
server.ServerName = name
|
||||
server.Hostname = hostname
|
||||
server.Region = region
|
||||
server.PortForward = portForward
|
||||
}
|
||||
|
||||
if !server.TCP && tcp {
|
||||
change = true
|
||||
server.TCP = tcp
|
||||
}
|
||||
if !server.UDP && udp {
|
||||
change = true
|
||||
server.UDP = udp
|
||||
}
|
||||
|
||||
ipFound := false
|
||||
for _, existingIP := range server.IPs {
|
||||
if ip.Equal(existingIP) {
|
||||
ipFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !ipFound {
|
||||
change = true
|
||||
server.IPs = append(server.IPs, ip)
|
||||
}
|
||||
|
||||
nts[name] = server
|
||||
|
||||
return change
|
||||
}
|
||||
|
||||
func (nts nameToServer) toServersSlice() (servers []models.Server) {
|
||||
servers = make([]models.Server, 0, len(nts))
|
||||
for _, server := range nts {
|
||||
servers = append(servers, server)
|
||||
}
|
||||
return servers
|
||||
}
|
||||
99
internal/provider/privateinternetaccess/updater/servers.go
Normal file
99
internal/provider/privateinternetaccess/updater/servers.go
Normal file
@@ -0,0 +1,99 @@
|
||||
// Package pia contains code to obtain the server information
|
||||
// for the Private Internet Access provider.
|
||||
package privateinternetaccess
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
var ErrNotEnoughServers = errors.New("not enough servers found")
|
||||
|
||||
func GetServers(ctx context.Context, client *http.Client, minServers int) (
|
||||
servers []models.Server, err error) {
|
||||
nts := make(nameToServer)
|
||||
|
||||
noChangeCounter := 0
|
||||
const maxNoChange = 10
|
||||
const betweenDuration = 200 * time.Millisecond
|
||||
const maxDuration = time.Minute
|
||||
|
||||
maxTimer := time.NewTimer(maxDuration)
|
||||
|
||||
for {
|
||||
data, err := fetchAPI(ctx, client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
change := addData(data.Regions, nts)
|
||||
|
||||
if !change {
|
||||
noChangeCounter++
|
||||
if noChangeCounter == maxNoChange {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
noChangeCounter = 0
|
||||
}
|
||||
|
||||
timer := time.NewTimer(betweenDuration)
|
||||
maxTimeout := false
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
if !maxTimer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
return nil, ctx.Err()
|
||||
case <-timer.C:
|
||||
case <-maxTimer.C:
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
maxTimeout = true
|
||||
}
|
||||
|
||||
if maxTimeout {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
servers = nts.toServersSlice()
|
||||
|
||||
if len(servers) < minServers {
|
||||
return nil, fmt.Errorf("%w: %d and expected at least %d",
|
||||
ErrNotEnoughServers, len(servers), minServers)
|
||||
}
|
||||
|
||||
sortServers(servers)
|
||||
|
||||
return servers, nil
|
||||
}
|
||||
|
||||
func addData(regions []regionData, nts nameToServer) (change bool) {
|
||||
for _, region := range regions {
|
||||
for _, server := range region.Servers.UDP {
|
||||
const tcp, udp = false, true
|
||||
if nts.add(server.CN, region.DNS, region.Name, tcp, udp, region.PortForward, server.IP) {
|
||||
change = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, server := range region.Servers.TCP {
|
||||
const tcp, udp = true, false
|
||||
if nts.add(server.CN, region.DNS, region.Name, tcp, udp, region.PortForward, server.IP) {
|
||||
change = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return change
|
||||
}
|
||||
19
internal/provider/privateinternetaccess/updater/sort.go
Normal file
19
internal/provider/privateinternetaccess/updater/sort.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package privateinternetaccess
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
func sortServers(servers []models.Server) {
|
||||
sort.Slice(servers, func(i, j int) bool {
|
||||
if servers[i].Region == servers[j].Region {
|
||||
if servers[i].Hostname == servers[j].Hostname {
|
||||
return servers[i].ServerName < servers[j].ServerName
|
||||
}
|
||||
return servers[i].Hostname < servers[j].Hostname
|
||||
}
|
||||
return servers[i].Region < servers[j].Region
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user