fix(slickvpn/updater): update existing hostnames from servers.json

- SlickVPN location website is now total garbage which cannot be parsed
This commit is contained in:
Quentin McGaw
2025-12-23 02:50:15 +00:00
parent 92237658c3
commit e806fe02db
12 changed files with 52 additions and 695 deletions
+14
View File
@@ -92,6 +92,20 @@ func (mr *MockStorageMockRecorder) FilterServers(arg0, arg1 interface{}) *gomock
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FilterServers", reflect.TypeOf((*MockStorage)(nil).FilterServers), arg0, arg1)
}
// HardcodedServers mocks base method.
func (m *MockStorage) HardcodedServers() models.AllServers {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "HardcodedServers")
ret0, _ := ret[0].(models.AllServers)
return ret0
}
// HardcodedServers indicates an expected call of HardcodedServers.
func (mr *MockStorageMockRecorder) HardcodedServers() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HardcodedServers", reflect.TypeOf((*MockStorage)(nil).HardcodedServers))
}
// MockUnzipper is a mock of Unzipper interface.
type MockUnzipper struct {
ctrl *gomock.Controller
+1
View File
@@ -8,4 +8,5 @@ import (
type Storage interface {
FilterServers(provider string, selection settings.ServerSelection) (
servers []models.Server, err error)
HardcodedServers() models.AllServers
}
+1
View File
@@ -43,6 +43,7 @@ type Providers struct {
type Storage interface {
FilterServers(provider string, selection settings.ServerSelection) (
servers []models.Server, err error)
HardcodedServers() models.AllServers
}
type Extractor interface {
+1 -1
View File
@@ -22,7 +22,7 @@ func New(storage common.Storage, randSource rand.Source,
return &Provider{
storage: storage,
randSource: randSource,
Fetcher: updater.New(client, updaterWarner, parallelResolver),
Fetcher: updater.New(client, updaterWarner, parallelResolver, storage),
}
}
+19 -41
View File
@@ -5,44 +5,29 @@ import (
"fmt"
"sort"
"github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/constants/providers"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/common"
"github.com/qdm12/gluetun/internal/updater/openvpn"
)
func (u *Updater) FetchServers(ctx context.Context, minServers int) (
servers []models.Server, err error,
) {
hostToData, err := fetchServers(ctx, u.client)
if err != nil {
return nil, fmt.Errorf("fetching and parsing website: %w", err)
// Since SlickVPN website listing VPN servers https://www.slickvpn.com/locations/
// went to become a pile of trash, we now use the servers data from our servers.json
// to check which servers can be resolved. The previous code was dynamically parsing
// their website table of servers and they now list only 11 servers on their website.
hardcodedServersData := u.storage.HardcodedServers()
slickVPNData, ok := hardcodedServersData.ProviderToServers[providers.SlickVPN]
if !ok {
return nil, fmt.Errorf("no hardcoded servers for provider %s", providers.SlickVPN)
}
hardcodedServers := make([]models.Server, len(slickVPNData.Servers))
copy(hardcodedServers, slickVPNData.Servers)
openvpnURLs := make([]string, 0, len(hostToData))
for _, data := range hostToData {
openvpnURLs = append(openvpnURLs, data.ovpnURL)
}
if len(openvpnURLs) < minServers {
return nil, fmt.Errorf("%w: %d and expected at least %d",
common.ErrNotEnoughServers, len(openvpnURLs), minServers)
}
const failEarly = false // some URLs from the website are not valid
hostToURL, errors := openvpn.FetchMultiFiles(ctx, u.client, openvpnURLs, failEarly)
for _, err := range errors {
u.warner.Warn(fmt.Sprintf("fetching OpenVPN files: %s", err))
}
if len(hostToURL) < minServers {
return nil, fmt.Errorf("%w: %d and expected at least %d",
common.ErrNotEnoughServers, len(hostToURL), minServers)
}
hosts := make([]string, 0, len(hostToURL))
for host := range hostToURL {
hosts = append(hosts, host)
hosts := make([]string, len(hardcodedServers))
for i := range hardcodedServers {
hosts[i] = hardcodedServers[i].Hostname
}
resolveSettings := parallelResolverSettings(hosts)
@@ -60,19 +45,12 @@ func (u *Updater) FetchServers(ctx context.Context, minServers int) (
}
servers = make([]models.Server, 0, len(hostToIPs))
for host, IPs := range hostToIPs {
serverData := hostToData[host]
server := models.Server{
VPN: vpn.OpenVPN,
Region: serverData.region,
Country: serverData.country,
City: serverData.city,
Hostname: host,
UDP: true,
TCP: true,
IPs: IPs,
for _, server := range hardcodedServers {
IPs, ok := hostToIPs[server.Hostname]
if !ok || len(IPs) == 0 {
continue
}
server.IPs = IPs
servers = append(servers, server)
}
File diff suppressed because one or more lines are too long
@@ -10,14 +10,16 @@ type Updater struct {
client *http.Client
parallelResolver common.ParallelResolver
warner common.Warner
storage common.Storage
}
func New(client *http.Client, warner common.Warner,
parallelResolver common.ParallelResolver,
parallelResolver common.ParallelResolver, storage common.Storage,
) *Updater {
return &Updater{
client: client,
parallelResolver: parallelResolver,
warner: warner,
storage: storage,
}
}
@@ -1,148 +0,0 @@
package updater
import (
"context"
"errors"
"fmt"
"net/http"
"regexp"
htmlutils "github.com/qdm12/gluetun/internal/updater/html"
"golang.org/x/net/html"
)
func fetchServers(ctx context.Context, client *http.Client) (
hostToData map[string]serverData, err error,
) {
const url = "https://www.slickvpn.com/locations/"
rootNode, err := htmlutils.Fetch(ctx, client, url)
if err != nil {
return nil, fmt.Errorf("fetching HTML code: %w", err)
}
hostToData, err = parseHTML(rootNode)
if err != nil {
return nil, fmt.Errorf("parsing HTML: %w", err)
}
return hostToData, nil
}
type serverData struct {
ovpnURL string
country string
region string
city string
}
var (
ErrLocationTableNotFound = errors.New("HTML location table node not found")
ErrTbodyNotFound = errors.New("HTML tbody node not found")
ErrExtractOpenVPNURL = errors.New("failed extracting OpenVPN URL")
)
func parseHTML(rootNode *html.Node) (hostToData map[string]serverData, err error) {
locationTableNode := htmlutils.BFS(rootNode, matchLocationTable)
if locationTableNode == nil {
return nil, htmlutils.WrapError(ErrLocationTableNotFound, rootNode)
}
tBodyNode := htmlutils.BFS(locationTableNode, matchTbody)
if tBodyNode == nil {
return nil, htmlutils.WrapError(ErrTbodyNotFound, rootNode)
}
rowNodes := htmlutils.DirectChildren(tBodyNode, matchTr)
hostToData = make(map[string]serverData, len(rowNodes))
for _, rowNode := range rowNodes {
hostname, data, err := parseRowNode(rowNode)
if err != nil {
return nil, fmt.Errorf("parsing row node: %w", err)
}
hostToData[hostname] = data
}
return hostToData, nil
}
func parseRowNode(rowNode *html.Node) (hostname string, data serverData, err error) {
columnIndex := 0
const (
columnIndexContinent = 0
columnIndexCountry = 1
columnIndexCity = 2
columnIndexConfig = 3
)
for cellNode := rowNode.FirstChild; cellNode != nil; cellNode = cellNode.NextSibling {
if cellNode.FirstChild == nil {
continue
}
switch columnIndex {
case columnIndexContinent:
data.region = cellNode.FirstChild.Data
case columnIndexCountry:
data.country = cellNode.FirstChild.Data
case columnIndexCity:
data.city = cellNode.FirstChild.Data
case columnIndexConfig:
linkNodes := htmlutils.DirectChildren(cellNode, matchA)
for _, linkNode := range linkNodes {
if linkNode.FirstChild.Data != "OpenVPN" {
continue
}
data.ovpnURL = htmlutils.Attribute(linkNode, "href")
if data.ovpnURL == "" {
return "", data, htmlutils.WrapError(ErrExtractOpenVPNURL, linkNode)
}
hostname, err = extractHostnameFromURL(data.ovpnURL)
if err != nil {
return "", data, fmt.Errorf("extracting hostname from url: %w", err)
}
break
}
}
columnIndex++
if columnIndex == columnIndexConfig+1 {
break
}
}
return hostname, data, nil
}
func matchLocationTable(rootNode *html.Node) (match bool) {
return htmlutils.MatchID("location-table")(rootNode)
}
func matchTbody(locationTableNode *html.Node) (match bool) {
return htmlutils.MatchData("tbody")(locationTableNode)
}
func matchTr(tbodyNode *html.Node) (match bool) {
return htmlutils.MatchData("tr")(tbodyNode)
}
func matchA(cellNode *html.Node) (match bool) {
return htmlutils.MatchData("a")(cellNode)
}
var serverNameRegex = regexp.MustCompile(`^.+\/(?P<serverName>.+)\.ovpn$`)
var ErrExtractHostnameFromURL = errors.New("cannot extract hostname from url")
func extractHostnameFromURL(url string) (hostname string, err error) {
matches := serverNameRegex.FindStringSubmatch(url)
const minMatches = 2
if len(matches) < minMatches {
return "", fmt.Errorf("%w: %s has less than 2 matches for %s",
ErrExtractHostnameFromURL, url, serverNameRegex)
}
hostname = matches[1]
return hostname, nil
}
@@ -1,267 +0,0 @@
package updater
import (
"context"
"io"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"golang.org/x/net/html"
)
type roundTripFunc func(r *http.Request) (*http.Response, error)
func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) {
return f(r)
}
func Test_fetchServers(t *testing.T) {
t.Parallel()
canceledCtx, cancel := context.WithCancel(context.Background())
cancel()
testCases := map[string]struct {
ctx context.Context
responseStatus int
responseBody io.ReadCloser
hostToData map[string]serverData
errWrapped error
errMessage string
}{
"context canceled": {
ctx: canceledCtx,
errWrapped: context.Canceled,
errMessage: `fetching HTML code: Get "https://www.slickvpn.com/locations/": context canceled`,
},
"success": {
ctx: context.Background(),
responseStatus: http.StatusOK,
//nolint:lll
responseBody: io.NopCloser(strings.NewReader(`
<div>
<table id="location-table">
<tbody>
<tr>
<td>South America</td>
<td>Chile</td>
<td>Vina del Mar</td>
<td> <a
href="https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.kna1.slickvpn.com.ovpn">OpenVPN</a>
| <a
href="https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/viscosity/gw1.kna1.slickvpn.com_viscosity.ovpn">Viscosity</a>
</td>
</tr>
</tbody>
</table>
</div>
`)),
hostToData: map[string]serverData{
"gw1.kna1.slickvpn.com": {
ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.kna1.slickvpn.com.ovpn", //nolint:lll
country: "Chile",
region: "South America",
city: "Vina del Mar",
},
},
},
}
for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
t.Parallel()
client := &http.Client{
Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
assert.Equal(t, http.MethodGet, r.Method)
assert.Equal(t, r.URL.String(), "https://www.slickvpn.com/locations/")
ctxErr := r.Context().Err()
if ctxErr != nil {
return nil, ctxErr
}
return &http.Response{
StatusCode: http.StatusOK,
Status: http.StatusText(testCase.responseStatus),
Body: testCase.responseBody,
}, nil
}),
}
hostToData, err := fetchServers(testCase.ctx, client)
assert.ErrorIs(t, err, testCase.errWrapped)
if testCase.errWrapped != nil {
assert.EqualError(t, err, testCase.errMessage)
}
assert.Equal(t, testCase.hostToData, hostToData)
})
}
}
func Test_parseHTML(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
rootNode *html.Node
hostToData map[string]serverData
errWrapped error
errMessage string
}{
"empty html": {
rootNode: parseTestHTML(t, ""),
errWrapped: ErrLocationTableNotFound,
errMessage: `HTML location table node not found: in HTML code: <html><head></head><body></body></html>`,
},
"test data": {
rootNode: parseTestDataIndexHTML(t),
//nolint:lll
hostToData: map[string]serverData{
"gw1.ams1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.ams1.slickvpn.com.ovpn", country: "Netherlands", region: "Europe", city: "Amsterdam"},
"gw1.ams2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.ams2.slickvpn.com.ovpn", country: "Netherlands", region: "Europe", city: "Amsterdam"},
"gw1.ams3.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.ams3.slickvpn.com.ovpn", country: "Netherlands", region: "Europe", city: "Amsterdam"},
"gw1.ams4.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.ams4.slickvpn.com.ovpn", country: "Netherlands", region: "Europe", city: "Amsterdam"},
"gw1.arn1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.arn1.slickvpn.com.ovpn", country: "Sweden", region: "Europe", city: "Stockholm"},
"gw1.arn3.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.arn3.slickvpn.com.ovpn", country: "Sweden", region: "Europe", city: "Stockholm"},
"gw1.ath1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.ath1.slickvpn.com.ovpn", country: "Greece", region: "Europe", city: "Athens"},
"gw1.atl1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.atl1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Atlanta"},
"gw1.atl3.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.atl3.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Atlanta"},
"gw1.beg1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.beg1.slickvpn.com.ovpn", country: "Serbia", region: "Europe", city: "Belgrade"},
"gw1.bkk1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.bkk1.slickvpn.com.ovpn", country: "Thailand", region: "Asia", city: "Bangkok"},
"gw1.blr1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.blr1.slickvpn.com.ovpn", country: "India", region: "Asia", city: "Bangalore"},
"gw1.bne1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.bne1.slickvpn.com.ovpn", country: "Australia", region: "Oceania", city: "Brisbane"},
"gw1.bom1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.bom1.slickvpn.com.ovpn", country: "India", region: "Asia", city: "Mumbai"},
"gw1.bos1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.bos1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Boston"},
"gw1.bud1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.bud1.slickvpn.com.ovpn", country: "Hungary", region: "Europe", city: "Budapest"},
"gw1.buf1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.buf1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Buffalo"},
"gw1.buh2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.buh2.slickvpn.com.ovpn", country: "Romania", region: "Europe", city: "Bucharest"},
"gw1.cdg1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.cdg1.slickvpn.com.ovpn", country: "France", region: "Europe", city: "Paris"},
"gw1.cgk1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.cgk1.slickvpn.com.ovpn", country: "Indonesia", region: "Asia", city: "Jakarta"},
"gw1.cmh1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.cmh1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Columbus"},
"gw1.cph1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.cph1.slickvpn.com.ovpn", country: "Denmark", region: "Europe", city: "Copenhagen"},
"gw1.cvt1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.cvt1.slickvpn.com.ovpn", country: "United Kingdom", region: "Europe", city: "Coventry"},
"gw1.dbq1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.dbq1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Dubuque"},
"gw1.den1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.den1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Denver"},
"gw1.dfw2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.dfw2.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Dallas"},
"gw1.dfw3.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.dfw3.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Dallas"},
"gw1.dub1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.dub1.slickvpn.com.ovpn", country: "Ireland", region: "Europe", city: "Dublin"},
"gw1.ewr1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.ewr1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Newark"},
"gw1.fra1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.fra1.slickvpn.com.ovpn", country: "Germany", region: "Europe", city: "Frankfurt"},
"gw1.fra2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.fra2.slickvpn.com.ovpn", country: "Germany", region: "Europe", city: "Frankfurt"},
"gw1.gru2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.gru2.slickvpn.com.ovpn", country: "Brazil", region: "South America", city: "Sao Paulo"},
"gw1.grz1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.grz1.slickvpn.com.ovpn", country: "Austria", region: "Europe", city: "Graz"},
"gw1.had2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.had2.slickvpn.com.ovpn", country: "Sweden", region: "Europe", city: "Halmstad"},
"gw1.hkg2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.hkg2.slickvpn.com.ovpn", country: "Hong Kong", region: "Asia", city: "Hong Kong"},
"gw1.iad1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.iad1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Washington"},
"gw1.iev1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.iev1.slickvpn.com.ovpn", country: "Ukraine", region: "Europe", city: "Kiev"},
"gw1.iom1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.iom1.slickvpn.com.ovpn", country: "United Kingdom", region: "Europe", city: "Isle Of Man"},
"gw1.kiv1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.kiv1.slickvpn.com.ovpn", country: "Moldova", region: "Europe", city: "Chisinau"},
"gw1.kna1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.kna1.slickvpn.com.ovpn", country: "Chile", region: "South America", city: "Vina del Mar"},
"gw1.kul1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.kul1.slickvpn.com.ovpn", country: "Malaysia", region: "Asia", city: "Kuala Lumpur"},
"gw1.las1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.las1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Las Vegas"},
"gw1.lax1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.lax1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Los Angeles"},
"gw1.lax2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.lax2.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Los Angeles"},
"gw1.led1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.led1.slickvpn.com.ovpn", country: "Russian Federation", region: "Europe", city: "St Petersburg"},
"gw1.lga1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.lga1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "New York"},
"gw1.lga2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.lga2.slickvpn.com.ovpn", country: "United States", region: "North America", city: "New York"},
"gw1.lhr1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.lhr1.slickvpn.com.ovpn", country: "United Kingdom", region: "Europe", city: "London"},
"gw1.lhr2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.lhr2.slickvpn.com.ovpn", country: "United Kingdom", region: "Europe", city: "London"},
"gw1.lil1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.lil1.slickvpn.com.ovpn", country: "France", region: "Europe", city: "Lille"},
"gw1.lju1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.lju1.slickvpn.com.ovpn", country: "Slovenia", region: "Europe", city: "Ljubljana"},
"gw1.mad1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.mad1.slickvpn.com.ovpn", country: "Spain", region: "Europe", city: "Madrid"},
"gw1.man2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.man2.slickvpn.com.ovpn", country: "United Kingdom", region: "Europe", city: "Manchester"},
"gw1.mci2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.mci2.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Kansas City"},
"gw1.mrn1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.mrn1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Morganton"},
"gw1.mxp1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.mxp1.slickvpn.com.ovpn", country: "Italy", region: "Europe", city: "Milan"},
"gw1.mxp2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.mxp2.slickvpn.com.ovpn", country: "Italy", region: "Europe", city: "Milan"},
"gw1.nrt1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.nrt1.slickvpn.com.ovpn", country: "Japan", region: "Asia", city: "Tokyo"},
"gw1.nue1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.nue1.slickvpn.com.ovpn", country: "Germany", region: "Europe", city: "Nürnberg"},
"gw1.ord3.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.ord3.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Chicago"},
"gw1.ord4.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.ord4.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Chicago"},
"gw1.ost2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.ost2.slickvpn.com.ovpn", country: "Belgium", region: "Europe", city: "Ostend"},
"gw1.pao1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.pao1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Palo Alto"},
"gw1.phx2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.phx2.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Phoenix"},
"gw1.prg1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.prg1.slickvpn.com.ovpn", country: "Czech Republic", region: "Europe", city: "Prague"},
"gw1.prg2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.prg2.slickvpn.com.ovpn", country: "Czech Republic", region: "Europe", city: "Prague"},
"gw1.rcs1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.rcs1.slickvpn.com.ovpn", country: "United Kingdom", region: "Europe", city: "Rochester"},
"gw1.rkv1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.rkv1.slickvpn.com.ovpn", country: "Iceland", region: "Europe", city: "Reykjavik"},
"gw1.san1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.san1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "San Diego"},
"gw1.sea1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.sea1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Seattle"},
"gw1.sea2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.sea2.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Seattle"},
"gw1.sin1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.sin1.slickvpn.com.ovpn", country: "Singapore", region: "Asia", city: "Singapore"},
"gw1.sin2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.sin2.slickvpn.com.ovpn", country: "Singapore", region: "Asia", city: "Singapore"},
"gw1.sjc2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.sjc2.slickvpn.com.ovpn", country: "United States", region: "North America", city: "San Jose"},
"gw1.skg1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.skg1.slickvpn.com.ovpn", country: "Greece", region: "Europe", city: "Thessaloniki"},
"gw1.sou1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.sou1.slickvpn.com.ovpn", country: "United Kingdom", region: "Europe", city: "Eastleigh near Southampton"},
"gw1.stl1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.stl1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "St Louis"},
"gw1.svo1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.svo1.slickvpn.com.ovpn", country: "Russian Federation", region: "Europe", city: "Moscow"},
"gw1.svo2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.svo2.slickvpn.com.ovpn", country: "Russian Federation", region: "Europe", city: "Moscow"},
"gw1.syd1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.syd1.slickvpn.com.ovpn", country: "Australia", region: "Oceania", city: "Sydney"},
"gw1.syd2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.syd2.slickvpn.com.ovpn", country: "Australia", region: "Oceania", city: "Sydney"},
"gw1.tll1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.tll1.slickvpn.com.ovpn", country: "Estonia", region: "Europe", city: "Tallinn"},
"gw1.tlv2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.tlv2.slickvpn.com.ovpn", country: "Israel", region: "Asia", city: "Tel Aviv Yafo"},
"gw1.tpa1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.tpa1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Tampa"},
"gw1.trf1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.trf1.slickvpn.com.ovpn", country: "Norway", region: "Europe", city: "Torp"},
"gw1.waw1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.waw1.slickvpn.com.ovpn", country: "Poland", region: "Europe", city: "Warsaw"},
"gw1.yei1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.yei1.slickvpn.com.ovpn", country: "Turkey", region: "Asia", city: "Bursa"},
"gw1.yul1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.yul1.slickvpn.com.ovpn", country: "Canada", region: "North America", city: "Montreal"},
"gw1.yul2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.yul2.slickvpn.com.ovpn", country: "Canada", region: "North America", city: "Montreal"},
"gw1.yvr1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.yvr1.slickvpn.com.ovpn", country: "Canada", region: "North America", city: "Vancouver"},
"gw1.yyz1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.yyz1.slickvpn.com.ovpn", country: "Canada", region: "North America", city: "Toronto"},
"gw1.zrh1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw1.zrh1.slickvpn.com.ovpn", country: "Switzerland", region: "Europe", city: "Zurich"},
"gw2.ams3.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.ams3.slickvpn.com.ovpn", country: "Netherlands", region: "Europe", city: "Amsterdam"},
"gw2.atl3.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.atl3.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Atlanta"},
"gw2.bcn2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.bcn2.slickvpn.com.ovpn", country: "Spain", region: "Europe", city: "Barcelona"},
"gw2.clt1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.clt1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Charlotte"},
"gw2.dfw3.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.dfw3.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Dallas"},
"gw2.ewr1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.ewr1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Newark"},
"gw2.fra1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.fra1.slickvpn.com.ovpn", country: "Germany", region: "Europe", city: "Frankfurt"},
"gw2.hou1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.hou1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Houston"},
"gw2.iad1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.iad1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Washington"},
"gw2.lhr2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.lhr2.slickvpn.com.ovpn", country: "United Kingdom", region: "Europe", city: "London"},
"gw2.mel1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.mel1.slickvpn.com.ovpn", country: "Australia", region: "Oceania", city: "Melbourne"},
"gw2.mia3.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.mia3.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Miami"},
"gw2.mia4.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.mia4.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Miami"},
"gw2.mxp2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.mxp2.slickvpn.com.ovpn", country: "Italy", region: "Europe", city: "Milan"},
"gw2.ord1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.ord1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Chicago"},
"gw2.ost2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.ost2.slickvpn.com.ovpn", country: "Belgium", region: "Europe", city: "Ostend"},
"gw2.pao1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.pao1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Palo Alto"},
"gw2.prg1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.prg1.slickvpn.com.ovpn", country: "Czech Republic", region: "Europe", city: "Prague"},
"gw2.pty1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.pty1.slickvpn.com.ovpn", country: "Panama", region: "North America", city: "Panama City"},
"gw2.sin2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.sin2.slickvpn.com.ovpn", country: "Singapore", region: "Asia", city: "Singapore"},
"gw2.slc1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.slc1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Salt Lake City"},
"gw2.syd2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.syd2.slickvpn.com.ovpn", country: "Australia", region: "Oceania", city: "Sydney"},
"gw2.tpe1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.tpe1.slickvpn.com.ovpn", country: "Taiwan", region: "Asia", city: "Taipei"},
"gw2.yul2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.yul2.slickvpn.com.ovpn", country: "Canada", region: "North America", city: "Montreal"},
"gw2.yyz1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw2.yyz1.slickvpn.com.ovpn", country: "Canada", region: "North America", city: "Toronto"},
"gw3.dfw3.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw3.dfw3.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Dallas"},
"gw3.ewr1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw3.ewr1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Newark"},
"gw3.iad1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw3.iad1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Washington"},
"gw3.lax3.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw3.lax3.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Los Angeles"},
"gw3.lhr1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw3.lhr1.slickvpn.com.ovpn", country: "United Kingdom", region: "Europe", city: "London"},
"gw3.lhr2.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw3.lhr2.slickvpn.com.ovpn", country: "United Kingdom", region: "Europe", city: "London"},
"gw3.pao1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw3.pao1.slickvpn.com.ovpn", country: "United States", region: "North America", city: "Palo Alto"},
"gw3.per1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw3.per1.slickvpn.com.ovpn", country: "Australia", region: "Oceania", city: "Perth"},
"gw3.sou1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw3.sou1.slickvpn.com.ovpn", country: "United Kingdom", region: "Europe", city: "Eastleigh near Southampton"},
"gw4.lhr1.slickvpn.com": {ovpnURL: "https://www.slickvpn.com/wp-content/themes/slickvpn-theme/vpn-configs/ovpn/gw4.lhr1.slickvpn.com.ovpn", country: "United Kingdom", region: "Europe", city: "London"},
},
},
}
for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
t.Parallel()
hostToData, err := parseHTML(testCase.rootNode)
assert.Equal(t, testCase.hostToData, hostToData)
assert.ErrorIs(t, err, testCase.errWrapped)
if testCase.errWrapped != nil {
assert.EqualError(t, err, testCase.errMessage)
}
})
}
}
+6
View File
@@ -19,3 +19,9 @@ func parseHardcodedServers() (allServers models.AllServers, err error) {
err = decoder.Decode(&allServers)
return allServers, err
}
// HardcodedServers returns the hardcoded servers.
// Warning: mutating the returned value will impact the storage's internal state.
func (s *Storage) HardcodedServers() models.AllServers {
return s.hardcodedServers
}
+1 -1
View File
@@ -12,7 +12,7 @@ import (
"golang.org/x/text/language"
)
// readFromFile reads the servers from server.json.
// readFromFile reads the servers from servers.json.
// It only reads servers that have the same version as the hardcoded servers version
// to avoid JSON decoding errors.
func (s *Storage) readFromFile(filepath string, hardcodedVersions map[string]uint16) (
+6
View File
@@ -22,6 +22,12 @@ func MatchData(data string) MatchFunc {
}
}
func MatchClasses(classStrings ...string) MatchFunc {
return func(node *html.Node) (match bool) {
return HasClassStrings(node, classStrings...)
}
}
func DirectChild(parent *html.Node,
matchFunc MatchFunc,
) (child *html.Node) {