chore(updater): move updater packages to pkg/updaters/<name>

This commit is contained in:
Quentin McGaw
2026-04-23 03:47:57 +00:00
parent 628b0a22e2
commit d96752c734
164 changed files with 732 additions and 343 deletions
+20
View File
@@ -0,0 +1,20 @@
package hidemyass
func getUniqueHosts(tcpHostToURL, udpHostToURL map[string]string) (
hosts []string,
) {
uniqueHosts := make(map[string]struct{}, len(tcpHostToURL))
for host := range tcpHostToURL {
uniqueHosts[host] = struct{}{}
}
for host := range udpHostToURL {
uniqueHosts[host] = struct{}{}
}
hosts = make([]string, 0, len(uniqueHosts))
for host := range uniqueHosts {
hosts = append(hosts, host)
}
return hosts
}
+45
View File
@@ -0,0 +1,45 @@
package hidemyass
import (
"context"
"net/http"
"strings"
"github.com/qdm12/gluetun/internal/updater/openvpn"
)
func getAllHostToURL(ctx context.Context, client *http.Client) (
tcpHostToURL, udpHostToURL map[string]string, err error,
) {
tcpHostToURL, err = getHostToURL(ctx, client, "TCP")
if err != nil {
return nil, nil, err
}
udpHostToURL, err = getHostToURL(ctx, client, "UDP")
if err != nil {
return nil, nil, err
}
return tcpHostToURL, udpHostToURL, nil
}
func getHostToURL(ctx context.Context, client *http.Client, protocol string) (
hostToURL map[string]string, err error,
) {
const baseURL = "https://vpn.hidemyass.com/vpn-config"
indexURL := baseURL + "/" + strings.ToUpper(protocol) + "/"
urls, err := fetchIndex(ctx, client, indexURL)
if err != nil {
return nil, err
}
const failEarly = true
hostToURL, errors := openvpn.FetchMultiFiles(ctx, client, urls, failEarly)
if len(errors) > 0 {
return nil, errors[0]
}
return hostToURL, nil
}
+55
View File
@@ -0,0 +1,55 @@
package hidemyass
import (
"context"
"io"
"net/http"
"regexp"
"strings"
)
var indexOpenvpnLinksRegex = regexp.MustCompile(`<a[ ]+href=".+\.ovpn">.+\.ovpn</a>`)
func fetchIndex(ctx context.Context, client *http.Client, indexURL string) (
openvpnURLs []string, err error,
) {
request, err := http.NewRequestWithContext(ctx, http.MethodGet, indexURL, nil)
if err != nil {
return nil, err
}
response, err := client.Do(request)
if err != nil {
return nil, err
}
defer response.Body.Close()
htmlCode, err := io.ReadAll(response.Body)
if err != nil {
return nil, err
}
if !strings.HasSuffix(indexURL, "/") {
indexURL += "/"
}
lines := strings.Split(string(htmlCode), "\n")
for _, line := range lines {
found := indexOpenvpnLinksRegex.FindString(line)
if len(found) == 0 {
continue
}
const prefix = `.ovpn">`
const suffix = `</a>`
startIndex := strings.Index(found, prefix) + len(prefix)
endIndex := strings.Index(found, suffix)
filename := found[startIndex:endIndex]
openvpnURL := indexURL + filename
if !strings.HasSuffix(openvpnURL, ".ovpn") {
continue
}
openvpnURLs = append(openvpnURLs, openvpnURL)
}
return openvpnURLs, nil
}
+28
View File
@@ -0,0 +1,28 @@
package hidemyass
import (
"time"
"github.com/qdm12/gluetun/internal/updater/resolver"
)
func parallelResolverSettings(hosts []string) (settings resolver.ParallelSettings) {
const (
maxFailRatio = 0.1
maxDuration = 15 * time.Second
betweenDuration = 2 * time.Second
maxNoNew = 2
maxFails = 2
)
return resolver.ParallelSettings{
Hosts: hosts,
MaxFailRatio: maxFailRatio,
Repeat: resolver.RepeatSettings{
MaxDuration: maxDuration,
BetweenDuration: betweenDuration,
MaxNoNew: maxNoNew,
MaxFails: maxFails,
SortIPs: true,
},
}
}
+74
View File
@@ -0,0 +1,74 @@
package hidemyass
import (
"context"
"fmt"
"sort"
"github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/common"
)
func (u *Updater) FetchServers(ctx context.Context, minServers int) (
servers []models.Server, err error,
) {
tcpHostToURL, udpHostToURL, err := getAllHostToURL(ctx, u.client)
if err != nil {
return nil, err
}
hosts := getUniqueHosts(tcpHostToURL, udpHostToURL)
if len(hosts) < minServers {
return nil, fmt.Errorf("%w: %d and expected at least %d",
common.ErrNotEnoughServers, len(hosts), minServers)
}
resolveSettings := parallelResolverSettings(hosts)
hostToIPs, warnings, err := u.parallelResolver.Resolve(ctx, resolveSettings)
for _, warning := range warnings {
u.warner.Warn(warning)
}
if err != nil {
return nil, err
}
if len(hostToIPs) < minServers {
return nil, fmt.Errorf("%w: %d and expected at least %d",
common.ErrNotEnoughServers, len(servers), minServers)
}
servers = make([]models.Server, 0, len(hostToIPs))
for host, IPs := range hostToIPs {
tcpURL, tcp := tcpHostToURL[host]
udpURL, udp := udpHostToURL[host]
// These two are only used to extract the country, region and city.
var url, protocol string
if tcp {
url = tcpURL
protocol = "TCP"
} else if udp {
url = udpURL
protocol = "UDP"
}
country, region, city := parseOpenvpnURL(url, protocol)
server := models.Server{
VPN: vpn.OpenVPN,
Country: country,
Region: region,
City: city,
Hostname: host,
IPs: IPs,
TCP: tcp,
UDP: udp,
}
servers = append(servers, server)
}
sort.Sort(models.SortableServers(servers))
return servers, nil
}
+27
View File
@@ -0,0 +1,27 @@
package hidemyass
import (
"net/http"
"github.com/qdm12/gluetun/internal/provider/common"
)
type Updater struct {
client *http.Client
parallelResolver common.ParallelResolver
warner common.Warner
}
func New(client *http.Client, warner common.Warner,
parallelResolver common.ParallelResolver,
) *Updater {
return &Updater{
client: client,
parallelResolver: parallelResolver,
warner: warner,
}
}
func (u *Updater) Version() uint16 {
return 2 //nolint:mnd
}
+58
View File
@@ -0,0 +1,58 @@
package hidemyass
import (
"strings"
"unicode"
)
func parseOpenvpnURL(url, protocol string) (country, region, city string) {
lastSlashIndex := strings.LastIndex(url, "/")
url = url[lastSlashIndex+1:]
suffix := "." + strings.ToUpper(protocol) + ".ovpn"
url = strings.TrimSuffix(url, suffix)
parts := strings.Split(url, ".")
switch len(parts) {
case 1:
country = parts[0]
return country, "", ""
case 2: //nolint:mnd
country = parts[0]
city = parts[1]
default:
country = parts[0]
region = parts[1]
city = parts[2]
}
country = camelCaseToWords(country)
region = camelCaseToWords(region)
city = camelCaseToWords(city)
country = mutateSpecialCountryCases(country)
return country, region, city
}
func camelCaseToWords(camelCase string) (words string) {
wasLowerCase := false
for _, r := range camelCase {
if wasLowerCase && unicode.IsUpper(r) {
words += " "
}
wasLowerCase = unicode.IsLower(r)
words += string(r)
}
return words
}
func mutateSpecialCountryCases(country string) string {
switch country {
case "Coted`Ivoire":
return "Cote d'Ivoire"
default:
return country
}
}