From 28d19e43f82ea59556c9a4246fbdc12321adce24 Mon Sep 17 00:00:00 2001 From: stephen mcquay Date: Tue, 29 Sep 2015 01:14:15 -0700 Subject: [PATCH] initial implementation --- license | 27 +++++++++++ main.go | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ readme.md | 3 ++ 3 files changed, 161 insertions(+) create mode 100644 license create mode 100644 main.go create mode 100644 readme.md diff --git a/license b/license new file mode 100644 index 0000000..72659c2 --- /dev/null +++ b/license @@ -0,0 +1,27 @@ +Copyright (c) 2015, stephen mcquay + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of st nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/main.go b/main.go new file mode 100644 index 0000000..3b8ee60 --- /dev/null +++ b/main.go @@ -0,0 +1,131 @@ +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") + 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 +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..cc98bd7 --- /dev/null +++ b/readme.md @@ -0,0 +1,3 @@ +# mcquay.me/st + +my fork of a speedtest tool