package main import ( "flag" "fmt" "io" "os" stdn "github.com/traetox/speedtest/speedtestdotnet" ) const ( maxFailureCount = 3 initialTestCount = 5 basePingCount = 5 fullTestCount = 50 ) var ( speedtestDuration = flag.Int("t", 3, "Target duration for speedtests (in seconds)") ) func init() { flag.Parse() if *speedtestDuration <= 0 { fmt.Fprintf(os.Stderr, "Invalid test duration\n") os.Exit(-1) } } func main() { cfg, err := stdn.GetConfig() if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } if len(cfg.Servers) <= 0 { fmt.Fprintf(os.Stderr, "No acceptable servers found\n") os.Exit(1) } var testServers []stdn.Testserver if testServers, err = autoGetTestServers(cfg); err != nil { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(-1) } if len(testServers) == 0 { fmt.Fprintf(os.Stderr, "insufficient test servers found\n") os.Exit(1) } server := testServers[0] if err = fullTest(server); err != nil { if err == io.EOF { fmt.Fprintf(os.Stderr, "Error, the remote server kicked us.\n") fmt.Fprintf(os.Stderr, "Maximum request size may have changed\n") } else { fmt.Fprintf(os.Stderr, "Test failed with unknown error: %v\n", err) } os.Exit(-1) } } func latency(server stdn.Testserver) (string, error) { //perform a full latency test durs, err := server.Ping(fullTestCount) if err != nil { return "", err } var avg, max, min uint64 var latencies []float64 for i := range durs { ms := uint64(durs[i].Nanoseconds() / 1000000) latencies = append(latencies, float64(ms)) avg += ms if ms > max { max = ms } if ms < min || min == 0 { min = ms } } avg = avg / uint64(len(durs)) return fmt.Sprintf("min/avg/max = %d/%d/%d (ms)", min, avg, max), nil } func fullTest(server stdn.Testserver) error { var err error var lat string if lat, err = latency(server); err != nil { return err } fmt.Printf("latency: %s\n", lat) up, err := server.Upstream(*speedtestDuration) if err != nil { return err } down, err := server.Downstream(*speedtestDuration) if err != nil { return err } fmt.Printf("bandwidth: up/down = %s/%s\n", stdn.HumanSpeed(up), stdn.HumanSpeed(down)) return nil } func autoGetTestServers(cfg *stdn.Config) ([]stdn.Testserver, error) { //get the first 5 closest servers testServers := []stdn.Testserver{} failures := 0 for i := range cfg.Servers { if failures >= maxFailureCount { if len(testServers) > 0 { return testServers, nil } return nil, fmt.Errorf("Failed to perform latency test on closest servers\n") } if len(testServers) >= initialTestCount { return testServers, nil } //get a latency from the server, the last latency will also be store in the //server structure if _, err := cfg.Servers[i].MedianPing(basePingCount); err != nil { failures++ continue } testServers = append(testServers, cfg.Servers[i]) } return testServers, nil }