Maintenance: use native HTTP client for updater

This commit is contained in:
Quentin McGaw
2021-02-26 00:42:55 +00:00
parent c54ee71e1d
commit b446aa6590
12 changed files with 144 additions and 57 deletions

View File

@@ -0,0 +1,8 @@
package updater
import "errors"
var (
ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK")
ErrUnmarshalResponseBody = errors.New("cannot unmarshal response body")
)

View File

@@ -10,7 +10,6 @@ import (
"strings" "strings"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/network"
) )
func (u *updater) updateMullvad(ctx context.Context) (err error) { func (u *updater) updateMullvad(ctx context.Context) (err error) {
@@ -26,15 +25,25 @@ func (u *updater) updateMullvad(ctx context.Context) (err error) {
return nil return nil
} }
func findMullvadServers(ctx context.Context, client network.Client) (servers []models.MullvadServer, err error) { func findMullvadServers(ctx context.Context, client *http.Client) (servers []models.MullvadServer, err error) {
const url = "https://api.mullvad.net/www/relays/openvpn/" const url = "https://api.mullvad.net/www/relays/openvpn/"
bytes, status, err := client.Get(ctx, url)
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if status != http.StatusOK {
return nil, fmt.Errorf("HTTP status code %d", status) response, err := client.Do(request)
if err != nil {
return nil, err
} }
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%w: %s for %s", ErrHTTPStatusCodeNotOK, response.Status, url)
}
decoder := json.NewDecoder(response.Body)
var data []struct { var data []struct {
Country string `json:"country_name"` Country string `json:"country_name"`
City string `json:"city_name"` City string `json:"city_name"`
@@ -44,9 +53,14 @@ func findMullvadServers(ctx context.Context, client network.Client) (servers []m
IPv4 string `json:"ipv4_addr_in"` IPv4 string `json:"ipv4_addr_in"`
IPv6 string `json:"ipv6_addr_in"` IPv6 string `json:"ipv6_addr_in"`
} }
if err := json.Unmarshal(bytes, &data); err != nil { if err := decoder.Decode(&data); err != nil {
return nil, err return nil, err
} }
if err := response.Body.Close(); err != nil {
return nil, err
}
serversByKey := map[string]models.MullvadServer{} serversByKey := map[string]models.MullvadServer{}
for _, jsonServer := range data { for _, jsonServer := range data {
if !jsonServer.Active { if !jsonServer.Active {

View File

@@ -12,7 +12,6 @@ import (
"strings" "strings"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/network"
) )
func (u *updater) updateNordvpn(ctx context.Context) (err error) { func (u *updater) updateNordvpn(ctx context.Context) (err error) {
@@ -38,16 +37,26 @@ var (
ErrInvalidIDInServerName = errors.New("invalid ID in server name") ErrInvalidIDInServerName = errors.New("invalid ID in server name")
) )
func findNordvpnServers(ctx context.Context, client network.Client) ( func findNordvpnServers(ctx context.Context, client *http.Client) (
servers []models.NordvpnServer, warnings []string, err error) { servers []models.NordvpnServer, warnings []string, err error) {
const url = "https://nordvpn.com/api/server" const url = "https://nordvpn.com/api/server"
bytes, status, err := client.Get(ctx, url)
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
if status != http.StatusOK {
return nil, nil, fmt.Errorf("HTTP status code %d", status) response, err := client.Do(request)
if err != nil {
return nil, nil, err
} }
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return nil, nil, fmt.Errorf("%w: %s for %s", ErrHTTPStatusCodeNotOK, response.Status, url)
}
decoder := json.NewDecoder(response.Body)
var data []struct { var data []struct {
IPAddress string `json:"ip_address"` IPAddress string `json:"ip_address"`
Name string `json:"name"` Name string `json:"name"`
@@ -57,9 +66,14 @@ func findNordvpnServers(ctx context.Context, client network.Client) (
TCP bool `json:"openvpn_tcp"` TCP bool `json:"openvpn_tcp"`
} `json:"features"` } `json:"features"`
} }
if err := json.Unmarshal(bytes, &data); err != nil { if err := decoder.Decode(&data); err != nil {
return nil, nil, err return nil, nil, err
} }
if err := response.Body.Close(); err != nil {
return nil, nil, err
}
sort.Slice(data, func(i, j int) bool { sort.Slice(data, func(i, j int) bool {
if data[i].Country == data[j].Country { if data[i].Country == data[j].Country {
return data[i].Name < data[j].Name return data[i].Name < data[j].Name

View File

@@ -5,22 +5,39 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"sort" "sort"
"strings"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
) )
func (u *updater) updatePIA(ctx context.Context) (err error) { func (u *updater) updatePIA(ctx context.Context) (err error) {
const url = "https://serverlist.piaservers.net/vpninfo/servers/v5" const url = "https://serverlist.piaservers.net/vpninfo/servers/v5"
b, status, err := u.client.Get(ctx, url)
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil { if err != nil {
return err return err
} }
if status != http.StatusOK {
return fmt.Errorf("HTTP status code %d: %s", status, strings.ReplaceAll(string(b), "\n", "")) response, err := u.client.Do(request)
if err != nil {
return err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return fmt.Errorf("%w: %s for %s", ErrHTTPStatusCodeNotOK, response.Status, url)
}
b, err := ioutil.ReadAll(response.Body)
if err != nil {
return err
}
if err := response.Body.Close(); err != nil {
return err
} }
// remove key/signature at the bottom // remove key/signature at the bottom

View File

@@ -3,10 +3,10 @@ package updater
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"sort" "sort"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/network"
) )
func (u *updater) updatePrivado(ctx context.Context) (err error) { func (u *updater) updatePrivado(ctx context.Context) (err error) {
@@ -27,7 +27,7 @@ func (u *updater) updatePrivado(ctx context.Context) (err error) {
return nil return nil
} }
func findPrivadoServersFromZip(ctx context.Context, client network.Client, lookupIP lookupIPFunc) ( func findPrivadoServersFromZip(ctx context.Context, client *http.Client, lookupIP lookupIPFunc) (
servers []models.PrivadoServer, warnings []string, err error) { servers []models.PrivadoServer, warnings []string, err error) {
const zipURL = "https://privado.io/apps/ovpn_configs.zip" const zipURL = "https://privado.io/apps/ovpn_configs.zip"
contents, err := fetchAndExtractFiles(ctx, client, zipURL) contents, err := fetchAndExtractFiles(ctx, client, zipURL)

View File

@@ -10,7 +10,6 @@ import (
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/publicip" "github.com/qdm12/gluetun/internal/publicip"
"github.com/qdm12/golibs/network"
) )
func (u *updater) updatePurevpn(ctx context.Context) (err error) { func (u *updater) updatePurevpn(ctx context.Context) (err error) {
@@ -31,7 +30,7 @@ func (u *updater) updatePurevpn(ctx context.Context) (err error) {
return nil return nil
} }
func findPurevpnServers(ctx context.Context, client network.Client, lookupIP lookupIPFunc) ( func findPurevpnServers(ctx context.Context, client *http.Client, lookupIP lookupIPFunc) (
servers []models.PurevpnServer, warnings []string, err error) { servers []models.PurevpnServer, warnings []string, err error) {
const zipURL = "https://s3-us-west-1.amazonaws.com/heartbleed/windows/New+OVPN+Files.zip" const zipURL = "https://s3-us-west-1.amazonaws.com/heartbleed/windows/New+OVPN+Files.zip"
contents, err := fetchAndExtractFiles(ctx, client, zipURL) contents, err := fetchAndExtractFiles(ctx, client, zipURL)
@@ -70,10 +69,7 @@ func findPurevpnServers(ctx context.Context, client network.Client, lookupIP loo
continue continue
} }
// TODO remove once we move away from network.Client country, region, city, err := publicip.Info(ctx, client, IPs[0])
const httpTimeout = 3 * time.Second
httpClient := &http.Client{Timeout: httpTimeout}
country, region, city, err := publicip.Info(ctx, httpClient, IPs[0])
if err != nil { if err != nil {
return nil, warnings, err return nil, warnings, err
} }

View File

@@ -10,7 +10,6 @@ import (
"time" "time"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/network"
) )
func (u *updater) updateSurfshark(ctx context.Context) (err error) { func (u *updater) updateSurfshark(ctx context.Context) (err error) {
@@ -32,21 +31,36 @@ func (u *updater) updateSurfshark(ctx context.Context) (err error) {
} }
//nolint:deadcode,unused //nolint:deadcode,unused
func findSurfsharkServersFromAPI(ctx context.Context, client network.Client, lookupIP lookupIPFunc) ( func findSurfsharkServersFromAPI(ctx context.Context, client *http.Client, lookupIP lookupIPFunc) (
servers []models.SurfsharkServer, warnings []string, err error) { servers []models.SurfsharkServer, warnings []string, err error) {
const url = "https://my.surfshark.com/vpn/api/v4/server/clusters" const url = "https://my.surfshark.com/vpn/api/v4/server/clusters"
b, status, err := client.Get(ctx, url)
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} else if status != http.StatusOK {
return nil, nil, fmt.Errorf("HTTP status code %d", status)
} }
response, err := client.Do(request)
if err != nil {
return nil, nil, err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return nil, nil, fmt.Errorf("%w: %s for %s", ErrHTTPStatusCodeNotOK, response.Status, url)
}
decoder := json.NewDecoder(response.Body)
var jsonServers []struct { var jsonServers []struct {
Host string `json:"connectionName"` Host string `json:"connectionName"`
Country string `json:"country"` Country string `json:"country"`
Location string `json:"location"` Location string `json:"location"`
} }
if err := json.Unmarshal(b, &jsonServers); err != nil { if err := decoder.Decode(&jsonServers); err != nil {
return nil, nil, err
}
if err := response.Body.Close(); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -80,7 +94,7 @@ func findSurfsharkServersFromAPI(ctx context.Context, client network.Client, loo
return servers, warnings, nil return servers, warnings, nil
} }
func findSurfsharkServersFromZip(ctx context.Context, client network.Client, lookupIP lookupIPFunc) ( func findSurfsharkServersFromZip(ctx context.Context, client *http.Client, lookupIP lookupIPFunc) (
servers []models.SurfsharkServer, warnings []string, err error) { servers []models.SurfsharkServer, warnings []string, err error) {
const zipURL = "https://my.surfshark.com/vpn/api/v1/server/configurations" const zipURL = "https://my.surfshark.com/vpn/api/v1/server/configurations"
contents, err := fetchAndExtractFiles(ctx, client, zipURL) contents, err := fetchAndExtractFiles(ctx, client, zipURL)

View File

@@ -4,12 +4,12 @@ import (
"context" "context"
"fmt" "fmt"
"net" "net"
"net/http"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/network"
) )
func (u *updater) updateTorguard(ctx context.Context) (err error) { func (u *updater) updateTorguard(ctx context.Context) (err error) {
@@ -30,7 +30,7 @@ func (u *updater) updateTorguard(ctx context.Context) (err error) {
return nil return nil
} }
func findTorguardServersFromZip(ctx context.Context, client network.Client) ( func findTorguardServersFromZip(ctx context.Context, client *http.Client) (
servers []models.TorguardServer, warnings []string, err error) { servers []models.TorguardServer, warnings []string, err error) {
// Note: all servers do both TCP and UDP // Note: all servers do both TCP and UDP
const zipURL = "https://torguard.net/downloads/OpenVPN-TCP-Linux.zip" const zipURL = "https://torguard.net/downloads/OpenVPN-TCP-Linux.zip"

View File

@@ -10,7 +10,6 @@ import (
"github.com/qdm12/gluetun/internal/configuration" "github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/network"
) )
type Updater interface { type Updater interface {
@@ -29,7 +28,7 @@ type updater struct {
timeNow func() time.Time timeNow func() time.Time
println func(s string) println func(s string)
lookupIP lookupIPFunc lookupIP lookupIPFunc
client network.Client client *http.Client
} }
func New(settings configuration.Updater, httpClient *http.Client, func New(settings configuration.Updater, httpClient *http.Client,
@@ -38,13 +37,12 @@ func New(settings configuration.Updater, httpClient *http.Client,
settings.DNSAddress = "1.1.1.1" settings.DNSAddress = "1.1.1.1"
} }
resolver := newResolver(settings.DNSAddress) resolver := newResolver(settings.DNSAddress)
const clientTimeout = 10 * time.Second
return &updater{ return &updater{
logger: logger, logger: logger,
timeNow: time.Now, timeNow: time.Now,
println: func(s string) { fmt.Println(s) }, println: func(s string) { fmt.Println(s) },
lookupIP: newLookupIP(resolver), lookupIP: newLookupIP(resolver),
client: network.NewClient(clientTimeout), client: httpClient,
options: settings, options: settings,
servers: currentServers, servers: currentServers,
} }

View File

@@ -3,11 +3,11 @@ package updater
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"sort" "sort"
"strings" "strings"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/network"
) )
func (u *updater) updateVyprvpn(ctx context.Context) (err error) { func (u *updater) updateVyprvpn(ctx context.Context) (err error) {
@@ -28,7 +28,7 @@ func (u *updater) updateVyprvpn(ctx context.Context) (err error) {
return nil return nil
} }
func findVyprvpnServers(ctx context.Context, client network.Client, lookupIP lookupIPFunc) ( func findVyprvpnServers(ctx context.Context, client *http.Client, lookupIP lookupIPFunc) (
servers []models.VyprvpnServer, warnings []string, err error) { servers []models.VyprvpnServer, warnings []string, err error) {
const zipURL = "https://support.vyprvpn.com/hc/article_attachments/360052617332/Vypr_OpenVPN_20200320.zip" const zipURL = "https://support.vyprvpn.com/hc/article_attachments/360052617332/Vypr_OpenVPN_20200320.zip"
contents, err := fetchAndExtractFiles(ctx, client, zipURL) contents, err := fetchAndExtractFiles(ctx, client, zipURL)

View File

@@ -10,7 +10,6 @@ import (
"time" "time"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/network"
) )
func (u *updater) updateWindscribe(ctx context.Context) (err error) { func (u *updater) updateWindscribe(ctx context.Context) (err error) {
@@ -26,16 +25,27 @@ func (u *updater) updateWindscribe(ctx context.Context) (err error) {
return nil return nil
} }
func findWindscribeServers(ctx context.Context, client network.Client) (servers []models.WindscribeServer, err error) { func findWindscribeServers(ctx context.Context, client *http.Client) (servers []models.WindscribeServer, err error) {
const baseURL = "https://assets.windscribe.com/serverlist/mob-v2/1/" const baseURL = "https://assets.windscribe.com/serverlist/mob-v2/1/"
cacheBreaker := time.Now().Unix() cacheBreaker := time.Now().Unix()
url := fmt.Sprintf("%s%d", baseURL, cacheBreaker) url := fmt.Sprintf("%s%d", baseURL, cacheBreaker)
content, status, err := client.Get(ctx, url)
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} else if status != http.StatusOK {
return nil, fmt.Errorf(http.StatusText(status))
} }
response, err := client.Do(request)
if err != nil {
return nil, err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%w: %s", ErrHTTPStatusCodeNotOK, response.Status)
}
decoder := json.NewDecoder(response.Body)
var jsonData struct { var jsonData struct {
Data []struct { Data []struct {
Region string `json:"name"` Region string `json:"name"`
@@ -48,9 +58,14 @@ func findWindscribeServers(ctx context.Context, client network.Client) (servers
} `json:"groups"` } `json:"groups"`
} `json:"data"` } `json:"data"`
} }
if err := json.Unmarshal(content, &jsonData); err != nil { if err := decoder.Decode(&jsonData); err != nil {
return nil, fmt.Errorf("%w: %s", ErrUnmarshalResponseBody, err)
}
if err := response.Body.Close(); err != nil {
return nil, err return nil, err
} }
for _, regionBlock := range jsonData.Data { for _, regionBlock := range jsonData.Data {
region := regionBlock.Region region := regionBlock.Region
for _, group := range regionBlock.Groups { for _, group := range regionBlock.Groups {

View File

@@ -4,31 +4,42 @@ import (
"archive/zip" "archive/zip"
"bytes" "bytes"
"context" "context"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/qdm12/golibs/network"
) )
var ( func fetchAndExtractFiles(ctx context.Context, client *http.Client, urls ...string) (
ErrBadStatusCode = errors.New("bad HTTP status code")
)
func fetchAndExtractFiles(ctx context.Context, client network.Client, urls ...string) (
contents map[string][]byte, err error) { contents map[string][]byte, err error) {
contents = make(map[string][]byte) contents = make(map[string][]byte)
for _, url := range urls { for _, url := range urls {
zipBytes, status, err := client.Get(ctx, url) request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} else if status != http.StatusOK {
return nil, fmt.Errorf("%w: fetching url %s: %d", ErrBadStatusCode, url, status)
} }
newContents, err := zipExtractAll(zipBytes)
response, err := client.Do(request)
if err != nil {
return nil, err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%w: %s for %s", ErrHTTPStatusCodeNotOK, response.Status, url)
}
b, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, err
}
if err := response.Body.Close(); err != nil {
return nil, err
}
newContents, err := zipExtractAll(b)
if err != nil { if err != nil {
return nil, err return nil, err
} }