func (s *Servers) SortServersByLatency() {
	ps := &serverSorter{
		servers: s.Servers,
		by: func(s1, s2 *Server) bool {
			if s1.Latency == 0 {
				return false
			}
			return s1.Latency < s2.Latency
		},
	}
	sort.Sort(ps)
}
Sort is a method on the function type, By, that sorts the argument slice according to the function.
package main
import (
	"crypto/md5"
	"encoding/csv"
	"encoding/json"
	"encoding/xml"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"path"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"
	"github.com/kellydunn/golang-geo"
)
const (
	version = "0.0.1"
)
func errorf(text string, a ...interface{}) {
	if !strings.HasSuffix(text, "\n") {
		text += "\n"
	}
	fmt.Printf(text, a...)
	os.Exit(1)
}
func dialTimeout(network string, laddr *net.TCPAddr, raddr *net.TCPAddr, timeout time.Duration) (net.Conn, error) {
	dialer := &net.Dialer{
		Timeout:   timeout,
		LocalAddr: laddr,
	}
	conn, err := dialer.Dial(network, raddr.String())
	return conn, err
}
type CliFlags struct {
	List        bool
	Server      int
	Interactive bool  
	Json        bool
	Xml         bool
	Csv         bool
	Simple      bool
	Source      string
	Timeout     int64
	Share       bool
	Version     bool
}
func NewCliFlags() *CliFlags {
	return &CliFlags{
		Interactive: true,
	}
}
type Results struct {
	XMLName   xml.Name  `json:"-" xml:"results"`
	Download  float64   `json:"download" xml:"download"`
	Upload    float64   `json:"upload" xml:"upload"`
	Latency   float64   `json:"latency" xml:"latency"`
	Server    *Server   `json:"server" xml:"server"`
	Timestamp time.Time `json:"timestamp" xml:"timestamp"`
	Share     string    `json:"share" xml:"share"`
}
func NewResults() *Results {
	return &Results{
		Timestamp: time.Now(),
	}
}
func (r *Results) ToJson() {
	out, err := json.MarshalIndent(r, "", "    ")
	if err != nil {
		errorf(err.Error())
	}
	fmt.Println(string(out))
}
func (r *Results) ToXml() {
	out, err := xml.MarshalIndent(r, "", "    ")
	if err != nil {
		errorf(err.Error())
	}
	fmt.Printf("%s%s", xml.Header, string(out))
}
func (r *Results) ToCsv() {
	record := []string{
		strconv.Itoa(r.Server.ID),
		r.Server.Sponsor,
		r.Server.Name,
		r.Timestamp.Format(time.RFC3339),
		strconv.FormatFloat(r.Server.Distance, 'f', -1, 64),
		strconv.FormatFloat(r.Latency, 'f', -1, 64),
		strconv.FormatFloat(r.Download, 'f', -1, 64),
		strconv.FormatFloat(r.Upload, 'f', -1, 64),
	}
	w := csv.NewWriter(os.Stdout)
	w.Write(record)
	w.Flush()
}
func (r *Results) ToSimple() {
	fmt.Printf("Latency: %.02f ms\n", r.Latency)
	fmt.Printf("Download: %.02f Mbit/s\n", r.Download/1000/1000)
	fmt.Printf("Upload: %.02f Mbit/s\n", r.Upload/1000/1000)
}
func (r *Results) ToPng() {
	kDownload := strconv.FormatFloat(r.Download/1000, 'f', 0, 64)
	kUpload := strconv.FormatFloat(r.Upload/1000, 'f', 0, 64)
	latency := strconv.FormatFloat(r.Latency, 'f', 0, 64)
	hashData := []byte(fmt.Sprintf("%s-%s-%s-297aae72", latency, kUpload, kDownload))
	hash := fmt.Sprintf("%x", md5.Sum(hashData))
	form := url.Values{}
	form.Add("download", kDownload)
	form.Add("ping", latency)
	form.Add("upload", kUpload)
	form.Add("promo", "")
	form.Add("startmode", "pingselect")
	form.Add("recommendedserverid", strconv.Itoa(r.Server.ID))
	form.Add("accuracy", "1")
	form.Add("serverid", strconv.Itoa(r.Server.ID))
	form.Add("hash", hash)
	req, _ := http.NewRequest("POST", "https://www.speedtest.net/api/api.php", strings.NewReader(form.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Referer", "http://c.speedtest.net/flash/speedtest.swf")
	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		r.Share = "Could not submit results to: " + err.Error()
	}
	defer res.Body.Close()
	resBody, _ := ioutil.ReadAll(res.Body)
	qsValues, _ := url.ParseQuery(string(resBody))
	r.Share = fmt.Sprintf("http://www.speedtest.net/result/%s.png", qsValues.Get("resultid"))
	r.Server.speedtest.Printf("Share results: %s", r.Share)
}
type Speedtest struct {
	Configuration *Configuration
	Servers       *Servers
	CliFlags      *CliFlags
	Results       *Results
	Source        *net.TCPAddr
	Timeout       time.Duration
}
func NewSpeedtest() *Speedtest {
	return &Speedtest{
		Configuration: &Configuration{},
		Servers:       &Servers{},
		CliFlags:      NewCliFlags(),
		Results:       NewResults(),
	}
}
func (s *Speedtest) Printf(text string, a ...interface{}) {
	if !s.CliFlags.Interactive {
		return
	}
	fmt.Printf(text, a...)
}
func (s *Speedtest) GetConfiguration() (*Configuration, error) {
	res, err := http.Get("https://www.speedtest.net/speedtest-config.php")
	if err != nil {
		return s.Configuration, errors.New("Error retrieving Speedtest.net configuration: " + err.Error())
	}
	defer res.Body.Close()
	settingsBody, _ := ioutil.ReadAll(res.Body)
	xml.Unmarshal(settingsBody, &s.Configuration)
	return s.Configuration, nil
}
func (s *Speedtest) GetServers(serverId int) (*Servers, error) {
	res, err := http.Get("https://www.speedtest.net/speedtest-servers.php")
	if err != nil {
		return s.Servers, errors.New("Error retrieving Speedtest.net servers: " + err.Error())
	}
	defer res.Body.Close()
	serversBody, _ := ioutil.ReadAll(res.Body)
	var allServers Servers
	xml.Unmarshal(serversBody, &allServers)
	for _, server := range allServers.Servers {
		server.speedtest = s
		if serverId == 0 || server.ID == serverId {
			s.Servers.Servers = append(s.Servers.Servers, server)
		}
	}
	return s.Servers, nil
}
type Client struct {
	IP        string  `xml:"ip,attr"`
	ISP       string  `xml:"isp,attr"`
	Latitude  float64 `xml:"lat,attr"`
	Longitude float64 `xml:"lon,attr"`
}
type ServerConfig struct {
	IgnoreIDs   string `xml:"ignoreids,attr"`
	ThreadCount string `xml:"threadcount,attr"`
}
type Times struct {
	DownloadOne   int `xml:"dl1,attr"`
	DownloadTwo   int `xml:"dl2,attr"`
	DownloadThree int `xml:"dl3,attr"`
	UploadOne     int `xml:"ul1,attr"`
	UploadTwo     int `xml:"ul2,attr"`
	UploadThree   int `xml:"ul3,attr"`
}
type Download struct {
	Length       float64 `xml:"testlength,attr"`
	PacketLength int     `xml:"packetlength,attr"`
}
type Upload struct {
	Length       float64 `xml:"testlength,attr"`
	PacketLength int     `xml:"packetlength,attr"`
}
type Latency struct {
	Length float64 `xml:"testlength,attr"`
}
type Configuration struct {
	Client       Client       `xml:"client"`
	ServerConfig ServerConfig `xml:"server-config"`
	Times        Times        `xml:"times"`
	Download     Download     `xml:"socket-download"`
	Upload       Upload       `xml:"socket-upload"`
	Latency      Latency      `xml:"socket-latency"`
}
type Server struct {
	CC        string        `xml:"cc,attr" json:"cc"`
	Country   string        `xml:"country,attr" json:"country"`
	ID        int           `xml:"id,attr" json:"id"`
	Latitude  float64       `xml:"lat,attr" json:"lat"`
	Longitude float64       `xml:"lon,attr" json:"lon"`
	Name      string        `xml:"name,attr" json:"name"`
	Sponsor   string        `xml:"sponsor,attr" json:"sponsor"`
	URL       string        `xml:"url,attr" json:"url"`
	URL2      string        `xml:"url2,attr" json:"url2"`
	Host      string        `xml:"host,attr" json:"host"`
	Distance  float64       `xml:"distance,attr" json:"distance"`
	Latency   time.Duration `xml:"latency,attr" json:"latency"`
	speedtest *Speedtest
	tcpAddr   *net.TCPAddr
}
type Servers struct {
	Servers []Server `xml:"servers>server"`
}
func (s *Servers) SortServersByDistance() {
	ps := &serverSorter{
		servers: s.Servers,
		by: func(s1, s2 *Server) bool {
			return s1.Distance < s2.Distance
		},
	}
	sort.Sort(ps)
}