Skip to content

Commit

Permalink
Merge pull request #63 from redradrat/fix-server-list
Browse files Browse the repository at this point in the history
use /api/js endpoint as new primary endpoint for server list fetching
  • Loading branch information
showwin authored Feb 20, 2022
2 parents 4e10a07 + 62ea24d commit 338c9e0
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 40 deletions.
10 changes: 5 additions & 5 deletions speedtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ func main() {
showUser(user)
}

serverList, err := speedtest.FetchServerList(user)
servers, err := speedtest.FetchServers(user)
checkError(err)
if *showList {
showServerList(serverList)
showServerList(servers)
return
}

targets, err := serverList.FindServer(*serverIds)
targets, err := servers.FindServer(*serverIds)
checkError(err)

startTest(targets, *savingMode, *jsonOutput)
Expand Down Expand Up @@ -140,8 +140,8 @@ func showUser(user *speedtest.User) {
}
}

func showServerList(serverList speedtest.ServerList) {
for _, s := range serverList.Servers {
func showServerList(servers speedtest.Servers) {
for _, s := range servers {
fmt.Printf("[%4s] %8.2fkm ", s.ID, s.Distance)
fmt.Printf(s.Name + " (" + s.Country + ") by " + s.Sponsor + "\n")
}
Expand Down
88 changes: 62 additions & 26 deletions speedtest/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package speedtest

import (
"context"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
Expand All @@ -12,9 +13,17 @@ import (
"time"
)

const speedTestServersUrl = "https://www.speedtest.net/speedtest-servers-static.php"
const speedTestServersUrl = "https://www.speedtest.net/api/js/servers?engine=js&limit=10"
const speedTestServersAlternativeUrl = "https://www.speedtest.net/speedtest-servers-static.php"

type PayloadType int

const (
JSONPayload PayloadType = iota
XMLPayload
)


// Server information
type Server struct {
URL string `xml:"url,attr" json:"url"`
Expand Down Expand Up @@ -62,50 +71,69 @@ func (b ByDistance) Less(i, j int) bool {
return b.Servers[i].Distance < b.Servers[j].Distance
}

// FetchServerList retrieves a list of available servers
func (client *Speedtest) FetchServerList(user *User) (ServerList, error) {
// FetchServers retrieves a list of available servers
func (client *Speedtest) FetchServers(user *User) (Servers, error) {
return client.FetchServerListContext(context.Background(), user)
}

// FetchServerList retrieves a list of available servers
func FetchServerList(user *User) (ServerList, error) {
return defaultClient.FetchServerList(user)
func FetchServers(user *User) (Servers, error) {
return defaultClient.FetchServers(user)
}

// FetchServerListContext retrieves a list of available servers, observing the given context.
func (client *Speedtest) FetchServerListContext(ctx context.Context, user *User) (ServerList, error) {
func (client *Speedtest) FetchServerListContext(ctx context.Context, user *User) (Servers, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, speedTestServersUrl, nil)
if err != nil {
return ServerList{}, err
return Servers{}, err
}

resp, err := client.doer.Do(req)
if err != nil {
return ServerList{}, err
return Servers{}, err
}

payloadType := JSONPayload

if resp.ContentLength == 0 {
resp.Body.Close()

req, err = http.NewRequestWithContext(ctx, http.MethodGet, speedTestServersAlternativeUrl, nil)
if err != nil {
return ServerList{}, err
return Servers{}, err
}

resp, err = client.doer.Do(req)
if err != nil {
return ServerList{}, err
return Servers{}, err
}

payloadType = XMLPayload
}

defer resp.Body.Close()

// Decode xml
decoder := xml.NewDecoder(resp.Body)
var servers Servers

switch payloadType {
case JSONPayload:
// Decode xml
decoder := json.NewDecoder(resp.Body)

if err := decoder.Decode(&servers); err != nil {
return servers, err
}
case XMLPayload:
var list ServerList
// Decode xml
decoder := xml.NewDecoder(resp.Body)

var list ServerList
if err := decoder.Decode(&list); err != nil {
return list, err
if err := decoder.Decode(&list); err != nil {
return servers, err
}
servers = list.Servers
default:
return servers, fmt.Errorf("response payload decoding not implemented")
}

// set doer of server
Expand All @@ -114,8 +142,7 @@ func (client *Speedtest) FetchServerListContext(ctx context.Context, user *User)
}

// Calculate distance
for i := range list.Servers {
server := list.Servers[i]
for _, server := range servers {
sLat, _ := strconv.ParseFloat(server.Lat, 64)
sLon, _ := strconv.ParseFloat(server.Lon, 64)
uLat, _ := strconv.ParseFloat(user.Lat, 64)
Expand All @@ -124,13 +151,13 @@ func (client *Speedtest) FetchServerListContext(ctx context.Context, user *User)
}

// Sort by distance
sort.Sort(ByDistance{list.Servers})
sort.Sort(ByDistance{servers})

if len(list.Servers) <= 0 {
return list, errors.New("unable to retrieve server list")
if len(servers) <= 0 {
return servers, errors.New("unable to retrieve server list")
}

return list, nil
return servers, nil
}

// FetchServerListContext retrieves a list of available servers, observing the given context.
Expand All @@ -151,15 +178,15 @@ func distance(lat1 float64, lon1 float64, lat2 float64, lon2 float64) float64 {
}

// FindServer finds server by serverID
func (l *ServerList) FindServer(serverID []int) (Servers, error) {
func (l Servers) FindServer(serverID []int) (Servers, error) {
servers := Servers{}

if len(l.Servers) <= 0 {
if len(l) <= 0 {
return servers, errors.New("no servers available")
}

for _, sid := range serverID {
for _, s := range l.Servers {
for _, s := range l {
id, _ := strconv.Atoi(s.ID)
if sid == id {
servers = append(servers, s)
Expand All @@ -168,21 +195,30 @@ func (l *ServerList) FindServer(serverID []int) (Servers, error) {
}

if len(servers) == 0 {
servers = append(servers, l.Servers[0])
servers = append(servers, l[0])
}

return servers, nil
}

// String representation of ServerList
func (l *ServerList) String() string {
func (l ServerList) String() string {
slr := ""
for _, s := range l.Servers {
slr += s.String()
}
return slr
}

// String representation of Servers
func (l Servers) String() string {
slr := ""
for _, s := range l {
slr += s.String()
}
return slr
}

// String representation of Server
func (s *Server) String() string {
return fmt.Sprintf("[%4s] %8.2fkm \n%s (%s) by %s\n", s.ID, s.Distance, s.Name, s.Country, s.Sponsor)
Expand Down
17 changes: 8 additions & 9 deletions speedtest/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ func TestFetchServerList(t *testing.T) {

client := New()

serverList, err := client.FetchServerList(&user)
servers, err := client.FetchServers(&user)
if err != nil {
t.Errorf(err.Error())
}
if len(serverList.Servers) == 0 {
if len(servers) == 0 {
t.Errorf("Failed to fetch server list.")
}
if len(serverList.Servers[0].Country) == 0 {
t.Errorf("got unexpected country name '%v'", serverList.Servers[0].Country)
if len(servers[0].Country) == 0 {
t.Errorf("got unexpected country name '%v'", servers[0].Country)
}
}

Expand Down Expand Up @@ -48,7 +48,7 @@ func TestDistance(t *testing.T) {
}

func TestFindServer(t *testing.T) {
servers := []*Server{
servers := Servers{
&Server{
ID: "1",
},
Expand All @@ -59,10 +59,9 @@ func TestFindServer(t *testing.T) {
ID: "3",
},
}
serverList := ServerList{Servers: servers}

serverID := []int{}
s, err := serverList.FindServer(serverID)
s, err := servers.FindServer(serverID)
if err != nil {
t.Errorf(err.Error())
}
Expand All @@ -74,7 +73,7 @@ func TestFindServer(t *testing.T) {
}

serverID = []int{2}
s, err = serverList.FindServer(serverID)
s, err = servers.FindServer(serverID)
if err != nil {
t.Errorf(err.Error())
}
Expand All @@ -86,7 +85,7 @@ func TestFindServer(t *testing.T) {
}

serverID = []int{3, 1}
s, err = serverList.FindServer(serverID)
s, err = servers.FindServer(serverID)
if err != nil {
t.Errorf(err.Error())
}
Expand Down

0 comments on commit 338c9e0

Please sign in to comment.