|
@@ -1,6 +1,43 @@
|
|
|
-// Defines a wrapper around the system ping binary.
|
|
|
|
|
|
|
+// Pingo defines a number of high level functions and data structures to
|
|
|
|
|
+// interact with and represent a Ping via Go. Primarily, it defines the
|
|
|
|
|
+// Ping() function and Address data structure.
|
|
|
//
|
|
//
|
|
|
-// NOTE(os): may fail with non-unix compliant system ping binaries.
|
|
|
|
|
|
|
+// NOTE this project does not provide a Go implementation of the ping protocol.
|
|
|
|
|
+// Instead, this project merely provides a wrapper to your system's existing
|
|
|
|
|
+// ping.c binary.
|
|
|
|
|
+//
|
|
|
|
|
+// NOTE(os): this project may fail with non-unix compliant system ping binaries,
|
|
|
|
|
+// predominantly Windows platforms. This relates to the way in which the project
|
|
|
|
|
+// interacts with the ping binary. (read: the project uses `ping -c1 ADDRESS`
|
|
|
|
|
+// and will break on systems that implement a different CLI)
|
|
|
|
|
+//
|
|
|
|
|
+// The project defines a simple top level binding for the system's ping.c:
|
|
|
|
|
+// Ping(). It simply reports a delay time as reported by the system ping, and
|
|
|
|
|
+// any errors encountered.
|
|
|
|
|
+//
|
|
|
|
|
+// Additionally, the project defines the "Address" data structure to represent
|
|
|
|
|
+// a series of ping results relating to the given host. Its fields are simple:
|
|
|
|
|
+//
|
|
|
|
|
+// Address (string): the host or IP address
|
|
|
|
|
+//
|
|
|
|
|
+// Results ([]float64): a series of ping results
|
|
|
|
|
+//
|
|
|
|
|
+// MaxResults (int): The maximum number of results to track, as enforced by
|
|
|
|
|
+// Address.Truncate()
|
|
|
|
|
+//
|
|
|
|
|
+// The Address data structure provides a series of higher level access functions
|
|
|
|
|
+// to prevent code repetition:
|
|
|
|
|
+//
|
|
|
|
|
+// Example:
|
|
|
|
|
+// ...
|
|
|
|
|
+// a := Address{Address:"google.ca", MaxResults:20}
|
|
|
|
|
+// latency, err := a.Ping() // get a single result and return the value
|
|
|
|
|
+// ... // handle error
|
|
|
|
|
+// a.Results, err = a.Poll() // append a ping to the Results slice, then return the updated slice
|
|
|
|
|
+// ... // handle error
|
|
|
|
|
+// a.Results = a.Truncate() // return a modified slice of only the most n*a.MaxResults long
|
|
|
|
|
+//
|
|
|
|
|
+// For more, see each function's GoDoc.
|
|
|
package pingo
|
|
package pingo
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
@@ -21,7 +58,7 @@ func runPing(address string) (delay *[]byte, err error) {
|
|
|
return &out, err
|
|
return &out, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// splitBytesToLines splits bytes as returned by [pingstats.runPing] into an
|
|
|
|
|
|
|
+// splitBytesToLines splits bytes as returned by runPing into an
|
|
|
// array of strings by newline
|
|
// array of strings by newline
|
|
|
func splitBytesToLines(bytes *[]byte) (lines []string) {
|
|
func splitBytesToLines(bytes *[]byte) (lines []string) {
|
|
|
return strings.Split(strings.ReplaceAll(string(*bytes), "\r\n", "\n"), "\n")
|
|
return strings.Split(strings.ReplaceAll(string(*bytes), "\r\n", "\n"), "\n")
|
|
@@ -41,17 +78,17 @@ func Ping(address string) (delay float64, err error) {
|
|
|
|
|
|
|
|
lines := splitBytesToLines(out)
|
|
lines := splitBytesToLines(out)
|
|
|
|
|
|
|
|
- for i := 0; i < len(lines); i++ {
|
|
|
|
|
|
|
+ for i := range lines {
|
|
|
if strings.Contains(lines[i], "bytes from") {
|
|
if strings.Contains(lines[i], "bytes from") {
|
|
|
position := strings.Index(lines[i], "time=")
|
|
position := strings.Index(lines[i], "time=")
|
|
|
|
|
|
|
|
before, _, success := strings.Cut(lines[i][position:], " ")
|
|
before, _, success := strings.Cut(lines[i][position:], " ")
|
|
|
if !success {
|
|
if !success {
|
|
|
- return -1, errors.New("Line does not match pattern!")
|
|
|
|
|
|
|
+ return -1, errors.New("line does not match pattern")
|
|
|
}
|
|
}
|
|
|
_, after, success := strings.Cut(before, "=")
|
|
_, after, success := strings.Cut(before, "=")
|
|
|
if !success {
|
|
if !success {
|
|
|
- return -1, errors.New("Line does not match pattern!")
|
|
|
|
|
|
|
+ return -1, errors.New("line does not match pattern")
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
delay, _ := strconv.ParseFloat(after, 64)
|
|
delay, _ := strconv.ParseFloat(after, 64)
|
|
@@ -62,36 +99,39 @@ func Ping(address string) (delay float64, err error) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
type Address struct {
|
|
type Address struct {
|
|
|
- Address string
|
|
|
|
|
- results []float64
|
|
|
|
|
- max_results int
|
|
|
|
|
|
|
+ Address string
|
|
|
|
|
+ Results []float64
|
|
|
|
|
+ MaxResults int
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// Enforces a Address.Result maximum length by dropping the oldest result,
|
|
|
|
|
+// then returns the modified slice.
|
|
|
func (a Address) Truncate() []float64 {
|
|
func (a Address) Truncate() []float64 {
|
|
|
- if len(a.results) > a.max_results {
|
|
|
|
|
- a.results = a.results[len(a.results)-a.max_results : len(a.results)] // return modified slice missing first index
|
|
|
|
|
|
|
+ if len(a.Results) > a.MaxResults {
|
|
|
|
|
+ a.Results = a.Results[len(a.Results)-a.MaxResults : len(a.Results)] // return modified slice missing first index
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return a.results
|
|
|
|
|
|
|
+ return a.Results
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Wraps [Ping]
|
|
|
|
|
|
|
+// Wraps Ping, passing Address.Address as its argument.
|
|
|
func (a Address) Ping() (delay float64, err error) {
|
|
func (a Address) Ping() (delay float64, err error) {
|
|
|
return Ping(a.Address)
|
|
return Ping(a.Address)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Poll the affiliated Address and appends it to a.results
|
|
|
|
|
|
|
+// Poll the affiliated Address, append it to a.Results, and report the modified
|
|
|
|
|
+// slice and any errors.
|
|
|
func (a Address) Poll() (results []float64, err error) {
|
|
func (a Address) Poll() (results []float64, err error) {
|
|
|
delay, err := a.Ping()
|
|
delay, err := a.Ping()
|
|
|
- a.results = append(a.results, delay)
|
|
|
|
|
- a.results = a.Truncate() // enforce max length
|
|
|
|
|
- return a.results, err
|
|
|
|
|
|
|
+ a.Results = append(a.Results, delay)
|
|
|
|
|
+ a.Results = a.Truncate() // enforce max length
|
|
|
|
|
+ return a.Results, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Last returns the last result in [Address.results]. Returns -1 if no previous result
|
|
|
|
|
|
|
+// Last returns the last result in Address.Results. Returns -1 if no previous result
|
|
|
func (a Address) Last() (delay float64) {
|
|
func (a Address) Last() (delay float64) {
|
|
|
- if len(a.results) > 0 {
|
|
|
|
|
- return a.results[len(a.results)-1]
|
|
|
|
|
|
|
+ if len(a.Results) > 0 {
|
|
|
|
|
+ return a.Results[len(a.Results)-1]
|
|
|
} else {
|
|
} else {
|
|
|
return -1
|
|
return -1
|
|
|
}
|
|
}
|