diff --git a/internal/provider/vpnsecure/updater/testdata/index.html b/internal/provider/vpnsecure/updater/testdata/index.html index 79858a95..17eae9a6 100644 --- a/internal/provider/vpnsecure/updater/testdata/index.html +++ b/internal/provider/vpnsecure/updater/testdata/index.html @@ -1,7345 +1,8091 @@ - + + + + + + VPN Server Locations — Unblock Geo-Restricted Content in 40+ Countries + | VPN Secure + - - - - + + + - Locations | VPNSecure.me - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - -
-
-
- -

- -

-
- -
-
-
- -
-
-
-
-
-
-
- - - - - -
-
-
- - - - -
-

- Encrypted VPN Access World Wide -

-
- - -
-
- - - - -
-
-
- - - - -
-

Our private VPN access servers are monitored. They’re access controlled, and we are the only - ones that operate them — no one else.  They do not permanently store IP addresses, nor do they - store logs. Each server supports all popular protocols, including: OpenVPN, SSH SOCKS, HTTP - Proxy & Smart DNS.  With the very best server locations and low ping times you will always - find a fast server close to you.

-
- - - - -
-

- Server Locations and Status -

-
- - -
-
- -
-
-
-
- - -
- -

Australia

-
-
- - -
-
-
- - - - - - - -
- au1 - up -
-
-
City: Brisbane
-
Region: Queensland
-
Premium: Yes
- -
-
- - -
-
-
- - - - - - - -
- au2 - up -
-
-
City: Sydney
-
Region: New South Wales
-
Premium: No
- -
-
- - -
-
-
- - - - - - - -
- au3 - up -
-
-
City: Sydney
-
Region: New South Wales
-
Premium: No
- -
-
- - -
-
-
- - - - - - - -
- au4 - up -
-
-
City: Sydney
-
Region: New South Wales
-
Premium: Yes
- -
-
-
-
- - -
- -

Austria

-
-
- - -
-
-
- - - - -
- at1 - up -
-
-
City: Vienna
-
Region: Vienna
-
Premium: Yes
- -
-
- - -
-
-
- - - - -
- at2 - up -
-
-
City: Vienna
-
Region: Vienna
-
Premium: No
- -
-
-
-
- - -
- -

Brazil

-
-
- - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- br1 - up -
-
-
City: Sao Paulo
-
Region: Sao Paulo
-
Premium: Yes
- -
-
-
-
- - -
- -

Belgium

-
-
- - -
-
-
- - - - - -
- be1 - up -
-
-
City: Zaventem
-
Region: Flanders
-
Premium: No
- -
-
- - -
-
-
- - - - - -
- be2 - up -
-
-
City: Brussel
-
Region: Brussels Hoofdstedelijk Gewest
-
Premium: No
- -
-
-
-
- - - - - -
- -

Canada

-
-
- - -
-
-
- - - - - - - - - -
- ca1 - up -
-
-
City: Richmond Hill
-
Region: Ontario
-
Premium: No
- -
-
- - -
-
-
- - - - - - - - - -
- ca2 - up -
-
-
City: Richmond Hill
-
Region: Ontario
-
Premium: No
- -
-
- - -
-
-
- - - - - - - - - -
- ca3 - up -
-
-
City: Montréal
-
Region: Quebec
-
Premium: Yes
- -
-
-
-
- - -
- -

Denmark

-
-
- - -
-
-
- - - - - - - - - - -
- dk1 - up -
-
-
City: Copenhagen
-
Region: Capital Region
-
Premium: Yes
- -
-
- - -
-
-
- - - - - - - - - - -
- dk2 - up -
-
-
City: Copenhagen
-
Region: Capital Region
-
Premium: Yes
- -
-
- - -
-
-
- - - - - - - - - - -
- dk3 - up -
-
-
City: Ballerup
-
Region: Capital Region
-
Premium: No
- -
-
-
-
- - -
- -

France

-
-
- - -
-
-
- - - - - -
- fr1 - up -
-
-
City: Paris
-
Region: Île-de-France
-
Premium: No
- -
-
- - -
-
-
- - - - - -
- fr2 - up -
-
-
City: Paris
-
Region: Île-de-France
-
Premium: No
- -
-
- - -
-
-
- - - - - -
- fr3 - up -
-
-
City: Strasbourg
-
Region: Grand Est
-
Premium: No
- -
-
-
-
- - -
- -

Germany

-
-
- - -
-
-
- - - - - -
- de1 - up -
-
-
City: Frankfurt am Main
-
Region: Hesse
-
Premium: No
- -
-
- - -
-
-
- - - - - -
- de2 - up -
-
-
City: Frankfurt am Main
-
Region: Hesse
-
Premium: No
- -
-
- - -
-
-
- - - - - -
- de3 - up -
-
-
City: Frankfurt am Main
-
Region: Hesse
-
Premium: No
- -
-
- - -
-
-
- - - - - -
- de4 - up -
-
-
City: Frankfurt am Main
-
Region: Hesse
-
Premium: No
- -
-
- - -
-
-
- - - - - -
- de5 - up -
-
-
City: Limburg an der Lahn
-
Region: Hesse
-
Premium: No
- -
-
- - -
-
-
- - - - - -
- de6 - up -
-
-
City: Frankfurt am Main
-
Region: Hesse
-
Premium: No
- -
-
-
-
- - -
- -

Hungary

-
-
- - -
-
-
- - - - - -
- hu1 - up -
-
-
City: Budapest
-
Region: Budapest
-
Premium: Yes
- -
-
-
-
- - -
- -

India

-
-
- - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- in1 - up -
-
-
City: Doddaballapura
-
Region: Karnataka
-
Premium: No
- -
-
-
-
- - -
- -

Indonesia

-
-
- - -
-
-
- - - - -
- id1 - up -
-
-
City: Jakarta
-
Region: Special Capital Region of Jakarta
-
Premium: No
- -
-
-
-
- - -
- -

Ireland

-
-
- - -
-
-
- - - - - -
- ie1 - up -
-
-
City: Dublin
-
Region: Dublin City
-
Premium: No
- -
-
-
-
- - -
- -

Israel

-
-
- - -
-
-
- - - - - - - - - - - - -
- il1 - up -
-
-
City: Tel Aviv
-
Region: Tel Aviv
-
Premium: Yes
- -
-
-
-
- - -
- -

Italy

-
-
- - -
-
-
- - - - - -
- it1 - up -
-
-
City: Milan
-
Region: Lombardy
-
Premium: Yes
- -
-
-
-
- - -
- -

Japan

-
-
- - -
-
-
- - - - - - - - - -
- jp2 - up -
-
-
City: Tokyo
-
Region: Tokyo
-
Premium: Yes
- -
-
-
-
- - -
- -

Mexico

-
-
- - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- mx1 - up -
-
-
City: Ampliación San Mateo (Colonia Solidaridad)
-
Region: México
-
Premium: No
- -
-
-
-
- - -
- -

Netherlands

-
-
- - -
-
-
- - - - - -
- nl1 - up -
-
-
City: Haarlem
-
Region: North Holland
-
Premium: No
- -
-
- - -
-
-
- - - - - -
- nl2 - up -
-
-
City: Naaldwijk
-
Region: South Holland
-
Premium: No
- -
-
-
-
- - -
- -

New Zealand

-
-
- - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
- nz1 - up -
-
-
City: Auckland
-
Region: Auckland
-
Premium: No
- -
-
-
-
- - -
- -

Norway

-
-
- - -
-
-
- - - - - - - - - - - - -
- no1 - up -
-
-
City: Oslo
-
Region: Oslo
-
Premium: Yes
- -
-
- - -
-
-
- - - - - - - - - - - - -
- no2 - up -
-
-
City: Stockholm
-
Region: Stockholm
-
Premium: Yes
- -
-
-
-
- - -
- -

Poland

-
-
- - -
-
-
- - - - -
- pl1 - up -
-
-
City: Warsaw
-
Region: Mazovia
-
Premium: Yes
- -
-
-
-
- - -
- -

Romania

-
-
- - -
-
-
- - - - - -
- ro1 - up -
-
-
City: Bucharest
-
Region: Bucure?ti
-
Premium: Yes
- -
-
-
-
- - -
- -

Russia

-
-
- - -
-
-
- - - - - -
- ru1 - up -
-
-
City: Moscow
-
Region: Moscow
-
Premium: Yes
- -
-
-
-
- - -
- -

Singapore

-
-
- - -
-
-
- - - - - - - - - - - -
- sg1 - up -
-
-
City: Singapore
-
Region: Singapore
-
Premium: Yes
- -
-
-
-
- - -
- -

South Africa

-
-
- - -
-
-
- - - - - - - - - - - - - -
- za1 - up -
-
-
City: Cape Town
-
Region: Western Cape
-
Premium: Yes
- -
-
-
-
- - -
- -

Spain

-
-
- - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- es2 - up -
-
-
City: Madrid
-
Region: Madrid
-
Premium: No
- -
-
- - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- se1 - up -
-
-
City: Valencia
-
Region: Valencia
-
Premium: No
- -
-
-
-
- - -
- -

Sweden

-
-
- - -
-
-
- - - - - - - - - - - - - -
- se2 - up -
-
-
City: Stockholm
-
Region: Stockholm
-
Premium: Yes
- -
-
- - -
-
-
- - - - - - - - - - - - - -
- se3 - up -
-
-
City: Stockholm
-
Region: Stockholm
-
Premium: No
- -
-
-
-
- - -
- -

Switzerland

-
-
- - -
-
-
- - - - - - - -
- ch1 - up -
-
-
City: Lausanne
-
Region: Vaud
-
Premium: No
- -
-
- - -
-
-
- - - - - - - -
- ch1 - up -
-
-
City: Geneva
-
Region: Geneva
-
Premium: Yes
- -
-
- - -
-
-
- - - - - - - -
- ch2 - up -
-
-
City: Genève
-
Region: Geneva
-
Premium: Yes
- -
-
-
-
- - -
- -

Ukraine

-
-
- - -
-
-
- - - - -
- ua1 - up -
-
-
City: Kremenchuk
-
Region: Poltavs'ka Oblast'
-
Premium: Yes
- -
-
-
-
- - -
- -

United Arab Emirates

-
-
- - -
-
-
- - - - - - - - - - - -
- ae1 - up -
-
-
City: Mumbai
-
Region: Maharashtra
-
Premium: Yes
- -
-
-
-
- - -
- -

United Kingdom

-
-
- - -
-
-
- - - - - - - - - - - -
- uk2 - up -
-
-
City: London
-
Region: England
-
Premium: No
- -
-
- - -
-
-
- - - - - - - - - - - -
- uk3 - up -
-
-
City: Kent
-
Region: England
-
Premium: No
- -
-
- - -
-
-
- - - - - - - - - - - -
- uk4 - up -
-
-
City: London
-
Region: England
-
Premium: No
- -
-
- - -
-
-
- - - - - - - - - - - -
- uk5 - up -
-
-
City: London
-
Region: England
-
Premium: No
- -
-
- - -
-
-
- - - - - - - - - - - -
- uk6 - up -
-
-
City: Harlesden
-
Region: Brent
-
Premium: No
- -
-
- - -
-
-
- - - - - - - - - - - -
- uk7 - up -
-
-
City: Manchester
-
Region: England
-
Premium: No
- -
-
-
-
- - -
- -

United States

-
-
- - -
-
-
- The United States of America flag, produced by Daniel McRae - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- us1 - up -
-
-
City: Secaucus
-
Region: New Jersey
-
Premium: No
- -
-
- - -
-
-
- The United States of America flag, produced by Daniel McRae - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- us10 - up -
-
-
City: New York City
-
Region: New York
-
Premium: No
- -
-
- - -
-
-
- The United States of America flag, produced by Daniel McRae - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- us11 - up -
-
-
City: Los Angeles
-
Region: California
-
Premium: No
- -
-
- - -
-
-
- The United States of America flag, produced by Daniel McRae - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- us12 - up -
-
-
City: Chicago
-
Region: Illinois
-
Premium: No
- -
-
- - -
-
-
- The United States of America flag, produced by Daniel McRae - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- us13 - up -
-
-
City: Los Angeles
-
Region: California
-
Premium: No
- -
-
- - -
-
-
- The United States of America flag, produced by Daniel McRae - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- us14 - up -
-
-
City: Los Angeles
-
Region: California
-
Premium: No
- -
-
- - -
-
-
- The United States of America flag, produced by Daniel McRae - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- us15 - up -
-
-
City: Los Angeles
-
Region: California
-
Premium: No
- -
-
- - -
-
-
- The United States of America flag, produced by Daniel McRae - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- us16 - up -
-
-
City: Chicago
-
Region: Illinois
-
Premium: No
- -
-
- - -
-
-
- The United States of America flag, produced by Daniel McRae - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- us2 - up -
-
-
City: New York City
-
Region: New York
-
Premium: No
- -
-
- - -
-
-
- The United States of America flag, produced by Daniel McRae - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- us3 - up -
-
-
City: Portland
-
Region: Oregon
-
Premium: Yes
- -
-
- - -
-
-
- The United States of America flag, produced by Daniel McRae - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- us4 - up -
-
-
City: Chicago
-
Region: Illinois
-
Premium: No
- -
-
- - -
-
-
- The United States of America flag, produced by Daniel McRae - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- us5 - up -
-
-
City: Los Angeles
-
Region: California
-
Premium: No
- -
-
- - -
-
-
- The United States of America flag, produced by Daniel McRae - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- us6 - up -
-
-
City: Los Angeles
-
Region: California
-
Premium: No
- -
-
- - -
-
-
- The United States of America flag, produced by Daniel McRae - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- us7 - up -
-
-
City: Chicago
-
Region: Illinois
-
Premium: No
- -
-
- - -
-
-
- The United States of America flag, produced by Daniel McRae - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- us8 - up -
-
-
City: Atlanta
-
Region: Georgia
-
Premium: No
- -
-
- - -
-
-
- The United States of America flag, produced by Daniel McRae - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- us9 - up -
-
-
City: Atlanta
-
Region: Georgia
-
Premium: No
- -
-
-
-
- - -
- -

Hong Kong

-
-
- - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- hk1 - up -
-
-
City: Hong Kong
-
Region: Central and Western
-
Premium: No
- -
-
-
-
- - -
- -

United States West

-
-
- - -
-
-
- us3 - up -
-
-
City: Los Angeles
-
Region: California
-
Premium: Yes
- -
-
-
-
- -
-
-
-
-
-
-
- -
-
- - - - - - - - +} + + + + + + + + + + + + + + + + + + + +
+
+ VPN Secure + + VPN Secure + +
- - - + + + Home + + + + + How VPN Works? + + + + + Pricing + + + + + Features + + + + + Locations + + + + + + Support + + + + + Download + + + + + Login + + + + + No Logging Ever! + + + Get VPN Secure + +
+
+
+ + 🔓 You’re Exposed +
+ + IP: + 72.218.0.213 + +
+ + Location: + + United States + 🇺🇸 + + + + ISP: + + Cox Communications Inc. + + + + Status: + Unprotected + + + 🔍 Check for Leaks + + + 🛡️ Hide Everything with VPN Secure + +
+ +
+ +
+

+ Our VPN Servers + Locations, Status, and Features +

+ +
+

+ 🇦🇺 + Australia +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Sydney + + AU #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇧🇷 + Brazil +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ São Paulo + + BR #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇨🇦 + Canada +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Montréal + + CA #00 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Check +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Montréal + + CA #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Montréal + + CA #02 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Montréal + + CA #03 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇨🇿 + Czech Republic +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Prague + + CZ #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Check +
+ Adblocker + + Cross +
+
+
+
+

+ 🇫🇷 + France +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Roubaix + + FR #00 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Check +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Roubaix + + FR #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Roubaix + + FR #02 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Strasbourg + + FR #03 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Strasbourg + + FR #04 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇩🇪 + Germany +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Frankfurt + + DE #02 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Check +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Limburg + + DE #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Check +
+ Adblocker + + Cross +
+
+
+
+

+ 🇭🇰 + Hong Kong +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Hong Kong + + HK #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇮🇳 + India +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Mumbai + + IN #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇮🇪 + Ireland +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Dublin + + IE #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Dublin + + IE #02 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Dublin + + IE #03 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇮🇱 + Israel +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Tel Aviv + + IL #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇮🇹 + Italy +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Milan + + IT #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Check +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Milan + + IT #02 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇯🇵 + Japan +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Tokyo + + JP #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇱🇹 + Lithuania +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Vilnius + + LT #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Check +
+ Adblocker + + Cross +
+
+
+
+

+ 🇲🇽 + Mexico +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Mexico City + + MX #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇳🇱 + Netherlands +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Amsterdam + + NL #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Amsterdam + + NL #02 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇵🇱 + Poland +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Warsaw + + PL #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇷🇴 + Romania +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Bucharest + + RO #02 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Voluntari + + RO #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇷🇺 + Russia +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Saint Petersburg + + RU #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇸🇬 + Singapore +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Singapore + + SG #00 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Check +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Singapore + + SG #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇪🇸 + Spain +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Madrid + + ES #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Check +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Madrid + + ES #02 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇸🇪 + Sweden +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Stockholm + + SE #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇨🇭 + Switzerland +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Zurich + + CH #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Check +
+ Streaming + + Cross +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇺🇦 + Ukraine +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Kyiv + + UA #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Kyiv + + UA #02 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇬🇧 + United Kingdom +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Bexleyheath + + GB #03 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Erith + + GB #02 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ London + + GB #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+
+

+ 🇺🇸 + United States +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Missouri + + US #05 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Check +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ New York + + US #01 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ New York + + US #04 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Oregon + + US #06 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Oregon + + US #07 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Oregon + + US #08 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Oregon + + US #09 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Oregon + + US #10 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Texas + + US #02 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Texas + + US #03 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Cross +
+ Fast (1Gbps) + + Cross +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Virginia + + US #11 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Check +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Virginia + + US #12 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Check +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Virginia + + US #13 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Check +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Virginia + + US #14 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Check +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Virginia + + US #15 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Check +
+ Adblocker + + Cross +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Virginia + + US #16 +
+ WireGuard + + Check +
+ OpenVPN + + Check +
+ Stealth Mode + + Cross +
+ Streaming + + Cross +
+ Dedicated IP + + Check +
+ Fast (1Gbps) + + Check +
+ Adblocker + + Cross +
+
+
+ +
+

+ 🇦🇺 + Australia +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ AU #01 + +
+
+ Sydney + + Check + + Check + + Cross + + Cross + + Cross + + Cross + + Cross +
+
+
+

+ 🇧🇷 + Brazil +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ BR #01 + +
+
+ São Paulo + + Check + + Check + + Cross + + Cross + + Cross + + Cross + + Cross +
+
+
+

+ 🇨🇦 + Canada +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ CA #00 + +
+
+ Montréal + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Check +
+ CA #01 + +
+
+ Montréal + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+ CA #02 + +
+
+ Montréal + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+ CA #03 + +
+
+ Montréal + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+
+
+

+ 🇨🇿 + Czech Republic +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ CZ #01 + +
+
+ Prague + + Check + + Check + + Cross + + Cross + + Check + + Check + + Cross +
+
+
+

+ 🇫🇷 + France +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ FR #00 + +
+
+ Roubaix + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Check +
+ FR #01 + +
+
+ Roubaix + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+ FR #02 + +
+
+ Roubaix + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+ FR #03 + +
+
+ Strasbourg + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+ FR #04 + +
+
+ Strasbourg + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+
+
+

+ 🇩🇪 + Germany +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ DE #01 + +
+
+ Limburg + + Check + + Check + + Cross + + Cross + + Check + + Check + + Cross +
+ DE #02 + +
+
+ Frankfurt + + Check + + Check + + Cross + + Cross + + Check + + Check + + Cross +
+
+
+

+ 🇭🇰 + Hong Kong +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ HK #01 + +
+
+ Hong Kong + + Check + + Check + + Cross + + Cross + + Cross + + Cross + + Cross +
+
+
+

+ 🇮🇳 + India +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ IN #01 + +
+
+ Mumbai + + Check + + Check + + Cross + + Cross + + Cross + + Cross + + Cross +
+
+
+

+ 🇮🇪 + Ireland +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ IE #01 + +
+
+ Dublin + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+ IE #02 + +
+
+ Dublin + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+ IE #03 + +
+
+ Dublin + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+
+
+

+ 🇮🇱 + Israel +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ IL #01 + +
+
+ Tel Aviv + + Check + + Check + + Cross + + Cross + + Cross + + Cross + + Cross +
+
+
+

+ 🇮🇹 + Italy +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ IT #01 + +
+
+ Milan + + Check + + Check + + Cross + + Cross + + Check + + Check + + Cross +
+ IT #02 + +
+
+ Milan + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+
+
+

+ 🇯🇵 + Japan +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ JP #01 + +
+
+ Tokyo + + Check + + Check + + Cross + + Cross + + Cross + + Cross + + Cross +
+
+
+

+ 🇱🇹 + Lithuania +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ LT #01 + +
+
+ Vilnius + + Check + + Check + + Cross + + Cross + + Check + + Check + + Cross +
+
+
+

+ 🇲🇽 + Mexico +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ MX #01 + +
+
+ Mexico City + + Check + + Check + + Cross + + Cross + + Cross + + Cross + + Cross +
+
+
+

+ 🇳🇱 + Netherlands +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ NL #01 + +
+
+ Amsterdam + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+ NL #02 + +
+
+ Amsterdam + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+
+
+

+ 🇵🇱 + Poland +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ PL #01 + +
+
+ Warsaw + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+
+
+

+ 🇷🇴 + Romania +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ RO #01 + +
+
+ Voluntari + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+ RO #02 + +
+
+ Bucharest + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+
+
+

+ 🇷🇺 + Russia +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ RU #01 + +
+
+ Saint Petersburg + + Check + + Check + + Cross + + Cross + + Cross + + Cross + + Cross +
+
+
+

+ 🇸🇬 + Singapore +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ SG #00 + +
+
+ Singapore + + Check + + Check + + Check + + Cross + + Check + + Cross + + Cross +
+ SG #01 + +
+
+ Singapore + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+
+
+

+ 🇪🇸 + Spain +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ ES #01 + +
+
+ Madrid + + Check + + Check + + Cross + + Cross + + Check + + Check + + Cross +
+ ES #02 + +
+
+ Madrid + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+
+
+

+ 🇸🇪 + Sweden +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ SE #01 + +
+
+ Stockholm + + Check + + Check + + Cross + + Cross + + Cross + + Cross + + Cross +
+
+
+

+ 🇨🇭 + Switzerland +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ CH #01 + +
+
+ Zurich + + Check + + Check + + Check + + Cross + + Cross + + Cross + + Cross +
+
+
+

+ 🇺🇦 + Ukraine +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ UA #01 + +
+
+ Kyiv + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+ UA #02 + +
+
+ Kyiv + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+
+
+

+ 🇬🇧 + United Kingdom +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ GB #01 + +
+
+ London + + Check + + Check + + Cross + + Cross + + Cross + + Cross + + Cross +
+ GB #02 + +
+
+ Erith + + Check + + Check + + Cross + + Cross + + Cross + + Cross + + Cross +
+ GB #03 + +
+
+ Bexleyheath + + Check + + Check + + Cross + + Cross + + Cross + + Cross + + Cross +
+
+
+

+ 🇺🇸 + United States +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ WireGuard + + OpenVPN + + Stealth Mode + + Streaming + + Dedicated IP + + Fast (1Gbps) + + Adblocker +
+ US #01 + +
+
+ New York + + Check + + Check + + Cross + + Cross + + Cross + + Cross + + Cross +
+ US #02 + +
+
+ Texas + + Check + + Check + + Cross + + Cross + + Cross + + Cross + + Cross +
+ US #03 + +
+
+ Texas + + Check + + Check + + Cross + + Cross + + Cross + + Cross + + Cross +
+ US #04 + +
+
+ New York + + Check + + Check + + Cross + + Cross + + Cross + + Cross + + Cross +
+ US #05 + +
+
+ Missouri + + Check + + Check + + Cross + + Check + + Cross + + Cross + + Cross +
+ US #06 + +
+
+ Oregon + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+ US #07 + +
+
+ Oregon + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+ US #08 + +
+
+ Oregon + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+ US #09 + +
+
+ Oregon + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+ US #10 + +
+
+ Oregon + + Check + + Check + + Cross + + Cross + + Check + + Cross + + Cross +
+ US #11 + +
+
+ Virginia + + Check + + Check + + Cross + + Cross + + Check + + Check + + Cross +
+ US #12 + +
+
+ Virginia + + Check + + Check + + Cross + + Cross + + Check + + Check + + Cross +
+ US #13 + +
+
+ Virginia + + Check + + Check + + Cross + + Cross + + Check + + Check + + Cross +
+ US #14 + +
+
+ Virginia + + Check + + Check + + Cross + + Cross + + Check + + Check + + Cross +
+ US #15 + +
+
+ Virginia + + Check + + Check + + Cross + + Cross + + Check + + Check + + Cross +
+ US #16 + +
+
+ Virginia + + Check + + Check + + Cross + + Cross + + Check + + Check + + Cross +
+
+
+ + + diff --git a/internal/provider/vpnsecure/updater/website.go b/internal/provider/vpnsecure/updater/website.go index 652306bb..22b28fa2 100644 --- a/internal/provider/vpnsecure/updater/website.go +++ b/internal/provider/vpnsecure/updater/website.go @@ -5,7 +5,10 @@ import ( "errors" "fmt" "net/http" + "regexp" "strings" + "unicode" + "unicode/utf8" "github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/provider/common" @@ -16,7 +19,7 @@ import ( func fetchServers(ctx context.Context, client *http.Client, warner common.Warner, ) (servers []models.Server, err error) { - const url = "https://www.vpnsecure.me/vpn-locations/" + const url = "https://www.vpnsecure.me/locations/" rootNode, err := htmlutils.Fetch(ctx, client, url) if err != nil { return nil, fmt.Errorf("fetching HTML code: %w", err) @@ -39,14 +42,14 @@ func parseHTML(rootNode *html.Node) (servers []models.Server, warnings []string, err error, ) { // Find div container for all servers, searching with BFS. - serversDiv := findServersDiv(rootNode) + serversDiv := htmlutils.BFS(rootNode, htmlutils.MatchID("servers")) if serversDiv == nil { return nil, nil, htmlutils.WrapError(errors.New("HTML servers container div not found"), rootNode) } for countryNode := serversDiv.FirstChild; countryNode != nil; countryNode = countryNode.NextSibling { - if countryNode.Data != divString { - // empty line(s) and tab(s) + if countryNode.Data != divString || + !htmlutils.HasClassStrings(countryNode, "box", "dark-gray") { continue } @@ -56,26 +59,20 @@ func parseHTML(rootNode *html.Node) (servers []models.Server, continue } - grid := htmlutils.BFS(countryNode, matchGridDiv) - if grid == nil { - warnings = append(warnings, htmlutils.WrapWarning("grid div not found", countryNode)) - continue - } + // Find all server tables within this country container + for serverNode := countryNode.FirstChild; serverNode != nil; serverNode = serverNode.NextSibling { + if serverNode.Data != divString { + continue + } + if !htmlutils.HasClassStrings(serverNode, "box", "white") { + continue + } - gridItems := htmlutils.DirectChildren(grid, matchGridItem) - if len(gridItems) == 0 { - warnings = append(warnings, htmlutils.WrapWarning("no grid item found", grid)) - continue - } - - for _, gridItem := range gridItems { - server, warning := parseHTMLGridItem(gridItem) + server, warning := parseServerNode(serverNode, country) if warning != "" { warnings = append(warnings, warning) continue } - - server.Country = country servers = append(servers, server) } } @@ -83,168 +80,231 @@ func parseHTML(rootNode *html.Node) (servers []models.Server, return servers, warnings, nil } -func parseHTMLGridItem(gridItem *html.Node) ( +func parseServerNode(node *html.Node, country string) ( server models.Server, warning string, ) { - gridItemDT := htmlutils.DirectChild(gridItem, matchDT) - if gridItemDT == nil { - return server, htmlutils.WrapWarning("grid item
not found", gridItem) + // Find the table within this server box + tableNode := htmlutils.DirectChild(node, func(n *html.Node) bool { + return n != nil && n.Data == "table" + }) + if tableNode == nil { + return server, htmlutils.WrapWarning("server table not found", node) } - host := findHost(gridItemDT) - host = naToEmpty(host) - if host == "" { - return server, htmlutils.WrapWarning("host not found", gridItemDT) + // Check status from green-circle in thead + isUp := hasGreenCircle(tableNode) + if !isUp { + warning := "skipping server which is not up" + return server, htmlutils.WrapWarning(warning, tableNode) } - status := findStatus(gridItemDT) - if !strings.EqualFold(status, "up") { - warning := fmt.Sprintf("skipping server with host %s which has status %q", host, status) - warning = htmlutils.WrapWarning(warning, gridItemDT) - return server, warning - } - - gridItemDD := htmlutils.DirectChild(gridItem, matchDD) - if gridItemDD == nil { - return server, htmlutils.WrapWarning("grid item dd not found", gridItem) - } - - region := findSpanStrong(gridItemDD, "Region:") - if region == "" { - warning := fmt.Sprintf("region for host %s not found", host) - return server, htmlutils.WrapWarning(warning, gridItemDD) - } - region = naToEmpty(region) - - city := findSpanStrong(gridItemDD, "City:") + // Extract city from thead + city := findCity(tableNode) if city == "" { - warning := fmt.Sprintf("region for host %s not found", host) - return server, htmlutils.WrapWarning(warning, gridItemDD) + return server, htmlutils.WrapWarning("city not found", tableNode) } - city = naToEmpty(city) - premiumString := findSpanStrong(gridItemDD, "Premium:") - premiumString = naToEmpty(premiumString) - if premiumString == "" { - warning := fmt.Sprintf("premium for host %s not found", host) - return server, htmlutils.WrapWarning(warning, gridItemDD) + // Extract server identifier (e.g., "AU #01") from thead + serverID := findServerID(tableNode) + if serverID == "" { + return server, htmlutils.WrapWarning("server ID not found", tableNode) } + hostname, err := buildHostname(serverID) + if err != nil { + return server, htmlutils.WrapWarning(fmt.Sprintf("invalid server ID %q: %v", serverID, err), tableNode) + } + + // Check for Dedicated IP feature (maps to Premium) + premium := hasFeature(tableNode, "Dedicated IP") + return models.Server{ - Region: region, + Country: country, City: city, - Hostname: host + ".isponeder.com", - Premium: strings.EqualFold(premiumString, "yes"), + Hostname: hostname, + Premium: premium, }, "" } -func naToEmpty(current string) (output string) { - if current == "N / A" { - return "" +var serverIDPattern = regexp.MustCompile(`^([A-Z]{2})\s*#\s*(\d+)$`) + +func buildHostname(serverID string) (string, error) { + const expectedSubmatches = 3 + matches := serverIDPattern.FindStringSubmatch(serverID) + if len(matches) != expectedSubmatches { + return "", errors.New("unexpected format") } - return current + countryCode := strings.ToLower(matches[1]) + serverNum := strings.TrimLeft(matches[2], "0") + return fmt.Sprintf("%s%s.isponeder.com", countryCode, serverNum), nil } func findCountry(countryNode *html.Node) (country string) { - for node := countryNode.FirstChild; node != nil; node = node.NextSibling { - if node.Data != "a" { - continue - } - for subNode := node.FirstChild; subNode != nil; subNode = subNode.NextSibling { - if subNode.Data != "h4" { - continue - } - return subNode.FirstChild.Data + h3Node := htmlutils.DirectChild(countryNode, func(n *html.Node) bool { + return n != nil && n.Data == "h3" + }) + if h3Node == nil { + return "" + } + + // Extract text content from h3, trimming the flag emoji and whitespace + var sb strings.Builder + for child := h3Node.FirstChild; child != nil; child = child.NextSibling { + if child.Type == html.TextNode { + sb.WriteString(child.Data) } } + country = strings.TrimSpace(sb.String()) + // Strip leading emoji (flag) - flags are typically composed emoji characters + for len(country) > 0 { + r, size := utf8.DecodeRuneInString(country) + if !isEmojiRune(r) { + break + } + country = country[size:] + } + country = strings.TrimSpace(country) + return country +} + +func findCity(tableNode *html.Node) (city string) { + const minThCount = 2 + theadNode := htmlutils.DirectChild(tableNode, func(n *html.Node) bool { + return n != nil && n.Data == "thead" + }) + if theadNode == nil { + return "" + } + + // Find the second which contains the city name + thNodes := htmlutils.BFS(theadNode, func(n *html.Node) bool { + return n != nil && n.Data == "th" + }) + if thNodes == nil { + return "" + } + + // Collect th nodes in order + var ths []*html.Node + for node := thNodes; node != nil; node = node.NextSibling { + if node.Data == "th" { + ths = append(ths, node) + } + } + if len(ths) < minThCount { + return "" + } + + // Second th contains the city + return strings.TrimSpace(getTextContent(ths[1])) +} + +func findServerID(tableNode *html.Node) (serverID string) { + theadNode := htmlutils.DirectChild(tableNode, func(n *html.Node) bool { + return n != nil && n.Data == "thead" + }) + if theadNode == nil { + return "" + } + + // Find the right-aligned th which contains the server ID + for node := htmlutils.BFS(theadNode, func(n *html.Node) bool { + return n != nil && n.Data == "th" + }); node != nil; node = node.NextSibling { + if node.Data == "th" && htmlutils.HasClassStrings(node, "right") { + return strings.TrimSpace(getTextContent(node)) + } + } + return "" } -func findServersDiv(rootNode *html.Node) (serversDiv *html.Node) { - locationsDiv := htmlutils.BFS(rootNode, matchLocationsListDiv) - if locationsDiv == nil { - return nil - } - - return htmlutils.BFS(locationsDiv, matchServersDiv) +func hasGreenCircle(tableNode *html.Node) bool { + return htmlutils.BFS(tableNode, func(n *html.Node) bool { + return n != nil && n.Data == "div" && + htmlutils.HasClassStrings(n, "green-circle") + }) != nil } -func findHost(gridItemDT *html.Node) (host string) { - hostNode := htmlutils.DirectChild(gridItemDT, matchText) - return strings.TrimSpace(hostNode.Data) -} - -func matchText(node *html.Node) (match bool) { - if node.Type != html.TextNode { +func hasFeature(tableNode *html.Node, featureName string) bool { + tbodyNode := htmlutils.DirectChild(tableNode, func(n *html.Node) bool { + return n != nil && n.Data == "tbody" + }) + if tbodyNode == nil { return false } - data := strings.TrimSpace(node.Data) - return data != "" -} -func findStatus(gridItemDT *html.Node) (status string) { - statusNode := htmlutils.DirectChild(gridItemDT, matchStatusSpan) - return strings.TrimSpace(statusNode.FirstChild.Data) -} - -func matchServersDiv(node *html.Node) (match bool) { - return node != nil && node.Data == divString && - htmlutils.HasClassStrings(node, "blk__i") -} - -func matchLocationsListDiv(node *html.Node) (match bool) { - return node != nil && node.Data == divString && - htmlutils.HasClassStrings(node, "locations-list") -} - -func matchGridDiv(node *html.Node) (match bool) { - return node != nil && node.Data == divString && - htmlutils.HasClassStrings(node, "grid--locations") -} - -func matchGridItem(node *html.Node) (match bool) { - return node != nil && node.Data == "dl" && - htmlutils.HasClassStrings(node, "grid__i") -} - -func matchDT(node *html.Node) (match bool) { - return node != nil && node.Data == "dt" -} - -func matchDD(node *html.Node) (match bool) { - return node != nil && node.Data == "dd" -} - -func matchStatusSpan(node *html.Node) (match bool) { - return node.Data == "span" && htmlutils.HasClassStrings(node, "status") -} - -func findSpanStrong(gridItemDD *html.Node, spanData string) ( - strongValue string, -) { - spanFound := false - for child := gridItemDD.FirstChild; child != nil; child = child.NextSibling { - if !htmlutils.MatchData("div")(child) { + for trNode := tbodyNode.FirstChild; trNode != nil; trNode = trNode.NextSibling { + if trNode.Data != "tr" { continue } - for subchild := child.FirstChild; subchild != nil; subchild = subchild.NextSibling { - if htmlutils.MatchData("span")(subchild) && subchild.FirstChild.Data == spanData { - spanFound = true - break + // Collect td cells in this row + var tds []*html.Node + for tdNode := trNode.FirstChild; tdNode != nil; tdNode = tdNode.NextSibling { + if tdNode.Data == "td" { + tds = append(tds, tdNode) } } - - if !spanFound { + if len(tds) == 0 { continue } - for subchild := child.FirstChild; subchild != nil; subchild = subchild.NextSibling { - if htmlutils.MatchData("strong")(subchild) { - return subchild.FirstChild.Data + feature := strings.TrimSpace(getTextContent(tds[0])) + if feature != featureName { + continue + } + + // If only one td with colspan spanning columns, feature exists + if len(tds) == 1 && htmlutils.Attribute(tds[0], "colspan") != "" { + return true + } + + // Otherwise check for pink-check icon in subsequent td cells + for _, td := range tds[1:] { + if htmlutils.BFS(td, func(n *html.Node) bool { + return n != nil && n.Data == "img" && + strings.Contains(htmlutils.Attribute(n, "src"), "pink-check") + }) != nil { + return true } } } - return "" + return false +} + +func getTextContent(node *html.Node) string { + var sb strings.Builder + for child := node.FirstChild; child != nil; child = child.NextSibling { + if child.Type == html.TextNode { + sb.WriteString(child.Data) + } + } + return sb.String() +} + +func isEmojiRune(r rune) bool { + for _, rangeStart := range []struct{ lo, hi rune }{ + // Emoticons + {0x1F600, 0x1F64F}, + // Misc Symbols and Pictographs + {0x1F300, 0x1F5FF}, + // Transport and Map Symbols + {0x1F680, 0x1F6FF}, + // Flags (regional indicator symbols + flag emojis) + {0x1F1E0, 0x1F1FF}, + {0x1F100, 0x1F10A}, + // Supplemental Symbols + {0x1F900, 0x1F9FF}, + } { + if r >= rangeStart.lo && r <= rangeStart.hi { + return true + } + } + // Catch-all for non-ASCII emoji using Unicode category + if r > 0x7E && unicode.In(r, unicode.So) { + return true + } + return false } diff --git a/internal/provider/vpnsecure/updater/website_test.go b/internal/provider/vpnsecure/updater/website_test.go index ea3c620e..fa5699f6 100644 --- a/internal/provider/vpnsecure/updater/website_test.go +++ b/internal/provider/vpnsecure/updater/website_test.go @@ -33,42 +33,77 @@ func Test_fetchServers(t *testing.T) { servers []models.Server errMessage string }{ - "context canceled": { + "context_canceled": { ctx: canceledCtx, - errMessage: `fetching HTML code: Get "https://www.vpnsecure.me/vpn-locations/": context canceled`, + errMessage: `fetching HTML code: Get "https://www.vpnsecure.me/locations/": context canceled`, }, - "success": { + "success_with_testdata": { ctx: context.Background(), responseStatus: http.StatusOK, responseBody: io.NopCloser(strings.NewReader(` -
-
-
- -

Australia

-
-
-
-
- au1 - up -
-
-
City: City
-
Region: Region
-
Premium: YES
- -
-
-
-
+ +
+
+

+ 🇦🇺 + Australia +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CityAU #01
+ WireGuard +
+ OpenVPN +
+ Stealth Mode +
+ Streaming +
+ Dedicated IP +
+ Fast (1Gbps) +
+ Adblocker +
+
+
+ `)), servers: []models.Server{ { Country: "Australia", City: "City", - Region: "Region", Hostname: "au1.isponeder.com", Premium: true, }, @@ -84,7 +119,7 @@ func Test_fetchServers(t *testing.T) { 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.vpnsecure.me/vpn-locations/") + assert.Equal(t, r.URL.String(), "https://www.vpnsecure.me/locations/") ctxErr := r.Context().Err() if ctxErr != nil { @@ -92,7 +127,7 @@ func Test_fetchServers(t *testing.T) { } return &http.Response{ - StatusCode: http.StatusOK, + StatusCode: testCase.responseStatus, Status: http.StatusText(testCase.responseStatus), Body: testCase.responseBody, }, nil @@ -122,92 +157,211 @@ func Test_parseHTML(t *testing.T) { warnings []string errMessage string }{ - "empty html": { + "empty_html": { rootNode: parseTestHTML(t, ""), errMessage: `HTML servers container div not found: in HTML code: `, }, - "test data": { - rootNode: parseTestDataIndexHTML(t), - warnings: []string{ - "no grid item found: in HTML code:
\n
", - }, - //nolint:lll + "missing_servers_div": { + rootNode: parseTestHTML(t, `
`), + errMessage: "HTML servers container div not found", + }, + "server_without_green_circle_is_skipped": { + rootNode: parseTestHTML(t, `
+
+

Germany

+
+ + + + + + + + +
BerlinDE #01
+
+
+
`), + // servers is nil (all servers skipped) + warnings: []string{"skipping server which is not up"}, + }, + "server_without_Dedicated_IP_is_not_premium": { + rootNode: parseTestHTML(t, `
+
+

Germany

+
+ + + + + + + + + + + + + + +
BerlinDE #01
Dedicated IP
+
+
+
`), servers: []models.Server{ - {Country: "Australia", Region: "Queensland", City: "Brisbane", Hostname: "au1.isponeder.com", Premium: true}, - {Country: "Australia", Region: "New South Wales", City: "Sydney", Hostname: "au2.isponeder.com"}, - {Country: "Australia", Region: "New South Wales", City: "Sydney", Hostname: "au3.isponeder.com"}, - {Country: "Australia", Region: "New South Wales", City: "Sydney", Hostname: "au4.isponeder.com", Premium: true}, - {Country: "Austria", Region: "Vienna", City: "Vienna", Hostname: "at1.isponeder.com", Premium: true}, - {Country: "Austria", Region: "Vienna", City: "Vienna", Hostname: "at2.isponeder.com"}, - {Country: "Brazil", Region: "Sao Paulo", City: "Sao Paulo", Hostname: "br1.isponeder.com", Premium: true}, - {Country: "Belgium", Region: "Flanders", City: "Zaventem", Hostname: "be1.isponeder.com"}, - {Country: "Belgium", Region: "Brussels Hoofdstedelijk Gewest", City: "Brussel", Hostname: "be2.isponeder.com"}, - {Country: "Canada", Region: "Ontario", City: "Richmond Hill", Hostname: "ca1.isponeder.com"}, - {Country: "Canada", Region: "Ontario", City: "Richmond Hill", Hostname: "ca2.isponeder.com"}, - {Country: "Canada", Region: "Quebec", City: "Montréal", Hostname: "ca3.isponeder.com", Premium: true}, - {Country: "Denmark", Region: "Capital Region", City: "Copenhagen", Hostname: "dk1.isponeder.com", Premium: true}, - {Country: "Denmark", Region: "Capital Region", City: "Copenhagen", Hostname: "dk2.isponeder.com", Premium: true}, - {Country: "Denmark", Region: "Capital Region", City: "Ballerup", Hostname: "dk3.isponeder.com"}, - {Country: "France", Region: "Île-de-France", City: "Paris", Hostname: "fr1.isponeder.com"}, - {Country: "France", Region: "Île-de-France", City: "Paris", Hostname: "fr2.isponeder.com"}, - {Country: "France", Region: "Grand Est", City: "Strasbourg", Hostname: "fr3.isponeder.com"}, - {Country: "Germany", Region: "Hesse", City: "Frankfurt am Main", Hostname: "de1.isponeder.com"}, - {Country: "Germany", Region: "Hesse", City: "Frankfurt am Main", Hostname: "de2.isponeder.com"}, - {Country: "Germany", Region: "Hesse", City: "Frankfurt am Main", Hostname: "de3.isponeder.com"}, - {Country: "Germany", Region: "Hesse", City: "Frankfurt am Main", Hostname: "de4.isponeder.com"}, - {Country: "Germany", Region: "Hesse", City: "Limburg an der Lahn", Hostname: "de5.isponeder.com"}, - {Country: "Germany", Region: "Hesse", City: "Frankfurt am Main", Hostname: "de6.isponeder.com"}, - {Country: "Hungary", Region: "Budapest", City: "Budapest", Hostname: "hu1.isponeder.com", Premium: true}, - {Country: "India", Region: "Karnataka", City: "Doddaballapura", Hostname: "in1.isponeder.com"}, - {Country: "Indonesia", Region: "Special Capital Region of Jakarta", City: "Jakarta", Hostname: "id1.isponeder.com"}, - {Country: "Ireland", Region: "Dublin City", City: "Dublin", Hostname: "ie1.isponeder.com"}, - {Country: "Israel", Region: "Tel Aviv", City: "Tel Aviv", Hostname: "il1.isponeder.com", Premium: true}, - {Country: "Italy", Region: "Lombardy", City: "Milan", Hostname: "it1.isponeder.com", Premium: true}, - {Country: "Japan", Region: "Tokyo", City: "Tokyo", Hostname: "jp2.isponeder.com", Premium: true}, - {Country: "Mexico", Region: "México", City: "Ampliación San Mateo (Colonia Solidaridad)", Hostname: "mx1.isponeder.com"}, - {Country: "Netherlands", Region: "North Holland", City: "Haarlem", Hostname: "nl1.isponeder.com"}, - {Country: "Netherlands", Region: "South Holland", City: "Naaldwijk", Hostname: "nl2.isponeder.com"}, - {Country: "New Zealand", Region: "Auckland", City: "Auckland", Hostname: "nz1.isponeder.com"}, - {Country: "Norway", Region: "Oslo", City: "Oslo", Hostname: "no1.isponeder.com", Premium: true}, - {Country: "Norway", Region: "Stockholm", City: "Stockholm", Hostname: "no2.isponeder.com", Premium: true}, - {Country: "Poland", Region: "Mazovia", City: "Warsaw", Hostname: "pl1.isponeder.com", Premium: true}, - {Country: "Romania", Region: "Bucure?ti", City: "Bucharest", Hostname: "ro1.isponeder.com", Premium: true}, - {Country: "Russia", Region: "Moscow", City: "Moscow", Hostname: "ru1.isponeder.com", Premium: true}, - {Country: "Singapore", Region: "Singapore", City: "Singapore", Hostname: "sg1.isponeder.com", Premium: true}, - {Country: "South Africa", Region: "Western Cape", City: "Cape Town", Hostname: "za1.isponeder.com", Premium: true}, - {Country: "Spain", Region: "Madrid", City: "Madrid", Hostname: "es2.isponeder.com"}, - {Country: "Spain", Region: "Valencia", City: "Valencia", Hostname: "se1.isponeder.com"}, - {Country: "Sweden", Region: "Stockholm", City: "Stockholm", Hostname: "se2.isponeder.com", Premium: true}, - {Country: "Sweden", Region: "Stockholm", City: "Stockholm", Hostname: "se3.isponeder.com"}, - {Country: "Switzerland", Region: "Vaud", City: "Lausanne", Hostname: "ch1.isponeder.com"}, - {Country: "Switzerland", Region: "Geneva", City: "Geneva", Hostname: "ch1.isponeder.com", Premium: true}, - {Country: "Switzerland", Region: "Geneva", City: "Genève", Hostname: "ch2.isponeder.com", Premium: true}, - {Country: "Ukraine", Region: "Poltavs'ka Oblast'", City: "Kremenchuk", Hostname: "ua1.isponeder.com", Premium: true}, - {Country: "United Arab Emirates", Region: "Maharashtra", City: "Mumbai", Hostname: "ae1.isponeder.com", Premium: true}, - {Country: "United Kingdom", Region: "England", City: "London", Hostname: "uk2.isponeder.com"}, - {Country: "United Kingdom", Region: "England", City: "Kent", Hostname: "uk3.isponeder.com"}, - {Country: "United Kingdom", Region: "England", City: "London", Hostname: "uk4.isponeder.com"}, - {Country: "United Kingdom", Region: "England", City: "London", Hostname: "uk5.isponeder.com"}, - {Country: "United Kingdom", Region: "Brent", City: "Harlesden", Hostname: "uk6.isponeder.com"}, - {Country: "United Kingdom", Region: "England", City: "Manchester", Hostname: "uk7.isponeder.com"}, - {Country: "United States", Region: "New Jersey", City: "Secaucus", Hostname: "us1.isponeder.com"}, - {Country: "United States", Region: "New York", City: "New York City", Hostname: "us10.isponeder.com"}, - {Country: "United States", Region: "California", City: "Los Angeles", Hostname: "us11.isponeder.com"}, - {Country: "United States", Region: "Illinois", City: "Chicago", Hostname: "us12.isponeder.com"}, - {Country: "United States", Region: "California", City: "Los Angeles", Hostname: "us13.isponeder.com"}, - {Country: "United States", Region: "California", City: "Los Angeles", Hostname: "us14.isponeder.com"}, - {Country: "United States", Region: "California", City: "Los Angeles", Hostname: "us15.isponeder.com"}, - {Country: "United States", Region: "Illinois", City: "Chicago", Hostname: "us16.isponeder.com"}, - {Country: "United States", Region: "New York", City: "New York City", Hostname: "us2.isponeder.com"}, - {Country: "United States", Region: "Oregon", City: "Portland", Hostname: "us3.isponeder.com", Premium: true}, - {Country: "United States", Region: "Illinois", City: "Chicago", Hostname: "us4.isponeder.com"}, - {Country: "United States", Region: "California", City: "Los Angeles", Hostname: "us5.isponeder.com"}, - {Country: "United States", Region: "California", City: "Los Angeles", Hostname: "us6.isponeder.com"}, - {Country: "United States", Region: "Illinois", City: "Chicago", Hostname: "us7.isponeder.com"}, - {Country: "United States", Region: "Georgia", City: "Atlanta", Hostname: "us8.isponeder.com"}, - {Country: "United States", Region: "Georgia", City: "Atlanta", Hostname: "us9.isponeder.com"}, - {Country: "Hong Kong", Region: "Central and Western", City: "Hong Kong", Hostname: "hk1.isponeder.com"}, - {Country: "United States West", Region: "California", City: "Los Angeles", Hostname: "us3.isponeder.com", Premium: true}, + { + Country: "Germany", + City: "Berlin", + Hostname: "de1.isponeder.com", + Premium: false, + }, + }, + }, + "server_with_Dedicated_IP_is_premium": { + rootNode: parseTestHTML(t, `
+
+

Germany

+
+ + + + + + + + + + + + + + +
BerlinDE #01
Dedicated IP
+
+
+
`), + servers: []models.Server{ + { + Country: "Germany", + City: "Berlin", + Hostname: "de1.isponeder.com", + Premium: true, + }, + }, + }, + "country_with_flag_emoji_prefix_is_stripped": { + rootNode: parseTestHTML(t, `
+
+

🇩🇪 Germany

+
+ + + + + + + + + +
BerlinDE #01
+
+
+
`), + servers: []models.Server{ + { + Country: "Germany", + City: "Berlin", + Hostname: "de1.isponeder.com", + }, + }, + }, + "country_name_starting_with_emoji_inline_text": { + rootNode: parseTestHTML(t, `
+
+

🇯🇵 Japan

+
+ + + + + + + + + +
TokyoJP #01
+
+
+
`), + servers: []models.Server{ + { + Country: "Japan", + City: "Tokyo", + Hostname: "jp1.isponeder.com", + }, + }, + }, + "test_data": { + rootNode: parseTestDataIndexHTML(t), + servers: []models.Server{ + {Country: "Australia", City: "Sydney", Hostname: "au1.isponeder.com", Premium: false}, + {Country: "Brazil", City: "São Paulo", Hostname: "br1.isponeder.com", Premium: false}, + {Country: "Canada", City: "Montréal", Hostname: "ca.isponeder.com", Premium: true}, + {Country: "Canada", City: "Montréal", Hostname: "ca1.isponeder.com", Premium: true}, + {Country: "Canada", City: "Montréal", Hostname: "ca2.isponeder.com", Premium: true}, + {Country: "Canada", City: "Montréal", Hostname: "ca3.isponeder.com", Premium: true}, + {Country: "Czech Republic", City: "Prague", Hostname: "cz1.isponeder.com", Premium: true}, + {Country: "France", City: "Roubaix", Hostname: "fr.isponeder.com", Premium: true}, + {Country: "France", City: "Roubaix", Hostname: "fr1.isponeder.com", Premium: true}, + {Country: "France", City: "Roubaix", Hostname: "fr2.isponeder.com", Premium: true}, + {Country: "France", City: "Strasbourg", Hostname: "fr3.isponeder.com", Premium: true}, + {Country: "France", City: "Strasbourg", Hostname: "fr4.isponeder.com", Premium: true}, + {Country: "Germany", City: "Frankfurt", Hostname: "de2.isponeder.com", Premium: true}, + {Country: "Germany", City: "Limburg", Hostname: "de1.isponeder.com", Premium: true}, + {Country: "Hong Kong", City: "Hong Kong", Hostname: "hk1.isponeder.com", Premium: false}, + {Country: "India", City: "Mumbai", Hostname: "in1.isponeder.com", Premium: false}, + {Country: "Ireland", City: "Dublin", Hostname: "ie1.isponeder.com", Premium: true}, + {Country: "Ireland", City: "Dublin", Hostname: "ie2.isponeder.com", Premium: true}, + {Country: "Ireland", City: "Dublin", Hostname: "ie3.isponeder.com", Premium: true}, + {Country: "Israel", City: "Tel Aviv", Hostname: "il1.isponeder.com", Premium: false}, + {Country: "Italy", City: "Milan", Hostname: "it1.isponeder.com", Premium: true}, + {Country: "Italy", City: "Milan", Hostname: "it2.isponeder.com", Premium: true}, + {Country: "Japan", City: "Tokyo", Hostname: "jp1.isponeder.com", Premium: false}, + {Country: "Lithuania", City: "Vilnius", Hostname: "lt1.isponeder.com", Premium: true}, + {Country: "Mexico", City: "Mexico City", Hostname: "mx1.isponeder.com", Premium: false}, + {Country: "Netherlands", City: "Amsterdam", Hostname: "nl1.isponeder.com", Premium: true}, + {Country: "Netherlands", City: "Amsterdam", Hostname: "nl2.isponeder.com", Premium: true}, + {Country: "Poland", City: "Warsaw", Hostname: "pl1.isponeder.com", Premium: true}, + {Country: "Romania", City: "Bucharest", Hostname: "ro2.isponeder.com", Premium: true}, + {Country: "Romania", City: "Voluntari", Hostname: "ro1.isponeder.com", Premium: true}, + {Country: "Russia", City: "Saint Petersburg", Hostname: "ru1.isponeder.com", Premium: false}, + {Country: "Singapore", City: "Singapore", Hostname: "sg.isponeder.com", Premium: true}, + {Country: "Singapore", City: "Singapore", Hostname: "sg1.isponeder.com", Premium: true}, + {Country: "Spain", City: "Madrid", Hostname: "es1.isponeder.com", Premium: true}, + {Country: "Spain", City: "Madrid", Hostname: "es2.isponeder.com", Premium: true}, + {Country: "Sweden", City: "Stockholm", Hostname: "se1.isponeder.com", Premium: false}, + {Country: "Switzerland", City: "Zurich", Hostname: "ch1.isponeder.com", Premium: false}, + {Country: "Ukraine", City: "Kyiv", Hostname: "ua1.isponeder.com", Premium: true}, + {Country: "Ukraine", City: "Kyiv", Hostname: "ua2.isponeder.com", Premium: true}, + {Country: "United Kingdom", City: "Bexleyheath", Hostname: "gb3.isponeder.com", Premium: false}, + {Country: "United Kingdom", City: "Erith", Hostname: "gb2.isponeder.com", Premium: false}, + {Country: "United Kingdom", City: "London", Hostname: "gb1.isponeder.com", Premium: false}, + {Country: "United States", City: "Missouri", Hostname: "us5.isponeder.com", Premium: false}, + {Country: "United States", City: "New York", Hostname: "us1.isponeder.com", Premium: false}, + {Country: "United States", City: "New York", Hostname: "us4.isponeder.com", Premium: false}, + {Country: "United States", City: "Oregon", Hostname: "us6.isponeder.com", Premium: true}, + {Country: "United States", City: "Oregon", Hostname: "us7.isponeder.com", Premium: true}, + {Country: "United States", City: "Oregon", Hostname: "us8.isponeder.com", Premium: true}, + {Country: "United States", City: "Oregon", Hostname: "us9.isponeder.com", Premium: true}, + {Country: "United States", City: "Oregon", Hostname: "us10.isponeder.com", Premium: true}, + {Country: "United States", City: "Texas", Hostname: "us2.isponeder.com", Premium: false}, + {Country: "United States", City: "Texas", Hostname: "us3.isponeder.com", Premium: false}, + {Country: "United States", City: "Virginia", Hostname: "us11.isponeder.com", Premium: true}, + {Country: "United States", City: "Virginia", Hostname: "us12.isponeder.com", Premium: true}, + {Country: "United States", City: "Virginia", Hostname: "us13.isponeder.com", Premium: true}, + {Country: "United States", City: "Virginia", Hostname: "us14.isponeder.com", Premium: true}, + {Country: "United States", City: "Virginia", Hostname: "us15.isponeder.com", Premium: true}, + {Country: "United States", City: "Virginia", Hostname: "us16.isponeder.com", Premium: true}, }, }, } @@ -219,9 +373,18 @@ func Test_parseHTML(t *testing.T) { servers, warnings, err := parseHTML(testCase.rootNode) assert.Equal(t, testCase.servers, servers) - assert.Equal(t, testCase.warnings, warnings) + for _, expected := range testCase.warnings { + found := false + for _, actual := range warnings { + if strings.Contains(actual, expected) { + found = true + break + } + } + assert.True(t, found, "warning %q not found in %v", expected, warnings) + } if testCase.errMessage != "" { - assert.EqualError(t, err, testCase.errMessage) + assert.ErrorContains(t, err, testCase.errMessage) } else { assert.NoError(t, err) }