save
parent
f89188761d
commit
7835d1fae7
@ -0,0 +1,17 @@
|
|||||||
|
package ping
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
// GetIP ...
|
||||||
|
func GetIP(hostname string) string {
|
||||||
|
addrs, err := net.LookupIP(hostname)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if ipv4 := addr.To4(); ipv4 != nil {
|
||||||
|
return ipv4.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
package ping
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptrace"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPing ...
|
||||||
|
type HTTPing struct {
|
||||||
|
target *Target
|
||||||
|
done chan struct{}
|
||||||
|
result *Result
|
||||||
|
Method string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Pinger = (*HTTPing)(nil)
|
||||||
|
|
||||||
|
// NewHTTPing return new HTTPing
|
||||||
|
func NewHTTPing(method string) *HTTPing {
|
||||||
|
return &HTTPing{
|
||||||
|
done: make(chan struct{}),
|
||||||
|
Method: method,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTarget ...
|
||||||
|
func (ping *HTTPing) SetTarget(target *Target) {
|
||||||
|
ping.target = target
|
||||||
|
if ping.result == nil {
|
||||||
|
ping.result = &Result{Target: target}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start ping
|
||||||
|
func (ping *HTTPing) Start() <-chan struct{} {
|
||||||
|
go func() {
|
||||||
|
t := time.NewTicker(ping.target.Interval)
|
||||||
|
defer t.Stop()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-t.C:
|
||||||
|
if ping.result.Counter >= ping.target.Counter && ping.target.Counter != 0 {
|
||||||
|
ping.Stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
duration, resp, remoteAddr, err := ping.ping()
|
||||||
|
ping.result.Counter++
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Ping %s - failed: %s\n", ping.target, err)
|
||||||
|
} else {
|
||||||
|
defer resp.Body.Close()
|
||||||
|
length, _ := io.Copy(ioutil.Discard, resp.Body)
|
||||||
|
fmt.Printf("Ping %s(%s) - %s is open - time=%s method=%s status=%d bytes=%d\n", ping.target, remoteAddr, ping.target.Protocol, duration, ping.Method, resp.StatusCode, length)
|
||||||
|
if ping.result.MinDuration == 0 {
|
||||||
|
ping.result.MinDuration = duration
|
||||||
|
}
|
||||||
|
if ping.result.MaxDuration == 0 {
|
||||||
|
ping.result.MaxDuration = duration
|
||||||
|
}
|
||||||
|
ping.result.SuccessCounter++
|
||||||
|
if duration > ping.result.MaxDuration {
|
||||||
|
ping.result.MaxDuration = duration
|
||||||
|
} else if duration < ping.result.MinDuration {
|
||||||
|
ping.result.MinDuration = duration
|
||||||
|
}
|
||||||
|
ping.result.TotalDuration += duration
|
||||||
|
}
|
||||||
|
case <-ping.done:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return ping.done
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result return ping result
|
||||||
|
func (ping *HTTPing) Result() *Result {
|
||||||
|
return ping.result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the tcping
|
||||||
|
func (ping *HTTPing) Stop() {
|
||||||
|
ping.done <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ping HTTPing) ping() (time.Duration, *http.Response, string, error) {
|
||||||
|
var resp *http.Response
|
||||||
|
var body io.Reader
|
||||||
|
if ping.Method == "POST" {
|
||||||
|
body = bytes.NewBufferString("{}")
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(ping.Method, ping.target.String(), body)
|
||||||
|
req.Header.Set(http.CanonicalHeaderKey("User-Agent"), "tcping")
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, "", err
|
||||||
|
}
|
||||||
|
var remoteAddr string
|
||||||
|
trace := &httptrace.ClientTrace{
|
||||||
|
ConnectStart: func(network, addr string) {
|
||||||
|
remoteAddr = addr
|
||||||
|
},
|
||||||
|
}
|
||||||
|
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
|
||||||
|
duration, errIfce := timeIt(func() interface{} {
|
||||||
|
client := http.Client{Timeout: ping.target.Timeout}
|
||||||
|
resp, err = client.Do(req)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if errIfce != nil {
|
||||||
|
err := errIfce.(error)
|
||||||
|
return 0, nil, "", err
|
||||||
|
}
|
||||||
|
return time.Duration(duration), resp, remoteAddr, nil
|
||||||
|
}
|
@ -0,0 +1,149 @@
|
|||||||
|
package ping
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Protocol ...
|
||||||
|
type Protocol int
|
||||||
|
|
||||||
|
func (protocol Protocol) String() string {
|
||||||
|
switch protocol {
|
||||||
|
case TCP:
|
||||||
|
return "tcp"
|
||||||
|
case HTTP:
|
||||||
|
return "http"
|
||||||
|
case HTTPS:
|
||||||
|
return "https"
|
||||||
|
}
|
||||||
|
return "unkown"
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TCP is tcp protocol
|
||||||
|
TCP Protocol = iota
|
||||||
|
// HTTP is http protocol
|
||||||
|
HTTP
|
||||||
|
// HTTPS is https protocol
|
||||||
|
HTTPS
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewProtocol convert protocol stirng to Protocol
|
||||||
|
func NewProtocol(protocol string) (Protocol, error) {
|
||||||
|
switch strings.ToLower(protocol) {
|
||||||
|
case TCP.String():
|
||||||
|
return TCP, nil
|
||||||
|
case HTTP.String():
|
||||||
|
return HTTP, nil
|
||||||
|
case HTTPS.String():
|
||||||
|
return HTTPS, nil
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("protocol %s not support", protocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Target is a ping
|
||||||
|
type Target struct {
|
||||||
|
Protocol Protocol
|
||||||
|
Host string
|
||||||
|
Port int
|
||||||
|
|
||||||
|
Counter int
|
||||||
|
Interval time.Duration
|
||||||
|
Timeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (target Target) String() string {
|
||||||
|
return fmt.Sprintf("%s://%s:%d", target.Protocol, target.Host, target.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pinger is a ping interface
|
||||||
|
type Pinger interface {
|
||||||
|
Start() <-chan struct{}
|
||||||
|
Stop()
|
||||||
|
Result() *Result
|
||||||
|
SetTarget(target *Target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ping is a ping interface
|
||||||
|
type Ping interface {
|
||||||
|
Start() <-chan struct{}
|
||||||
|
|
||||||
|
Host() string
|
||||||
|
Port() int
|
||||||
|
Protocol() Protocol
|
||||||
|
Counter() int
|
||||||
|
|
||||||
|
Stop()
|
||||||
|
|
||||||
|
Result() Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result ...
|
||||||
|
type Result struct {
|
||||||
|
Counter int
|
||||||
|
SuccessCounter int
|
||||||
|
Target *Target
|
||||||
|
|
||||||
|
MinDuration time.Duration
|
||||||
|
MaxDuration time.Duration
|
||||||
|
TotalDuration time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avg return the average time of ping
|
||||||
|
func (result Result) Avg() time.Duration {
|
||||||
|
if result.SuccessCounter == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return result.TotalDuration / time.Duration(result.SuccessCounter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failed return failed counter
|
||||||
|
func (result Result) Failed() int {
|
||||||
|
return result.Counter - result.SuccessCounter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (result Result) String() string {
|
||||||
|
const resultTpl = `
|
||||||
|
Ping statistics {{.Target}}
|
||||||
|
{{.Counter}} probes sent.
|
||||||
|
{{.SuccessCounter}} successful, {{.Failed}} failed.
|
||||||
|
Approximate trip times:
|
||||||
|
Minimum = {{.MinDuration}}, Maximum = {{.MaxDuration}}, Average = {{.Avg}}`
|
||||||
|
t := template.Must(template.New("result").Parse(resultTpl))
|
||||||
|
res := bytes.NewBufferString("")
|
||||||
|
t.Execute(res, result)
|
||||||
|
return res.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckURI check uri
|
||||||
|
func CheckURI(uri string) (schema, host string, port int, matched bool) {
|
||||||
|
const reExp = `^((?P<schema>((ht|f)tp(s?))|tcp)\://)?((([a-zA-Z0-9_\-]+\.)+[a-zA-Z]{2,})|((?:(?:25[0-5]|2[0-4]\d|[01]\d\d|\d?\d)((\.?\d)\.)){4})|(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9]))(:([0-9]+))?(/[a-zA-Z0-9\-\._\?\,\'/\\\+&%\$#\=~]*)?$`
|
||||||
|
pattern := regexp.MustCompile(reExp)
|
||||||
|
res := pattern.FindStringSubmatch(uri)
|
||||||
|
if len(res) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
matched = true
|
||||||
|
schema = res[2]
|
||||||
|
if schema == "" {
|
||||||
|
schema = "tcp"
|
||||||
|
}
|
||||||
|
host = res[6]
|
||||||
|
if res[17] == "" {
|
||||||
|
if schema == HTTPS.String() {
|
||||||
|
port = 443
|
||||||
|
} else {
|
||||||
|
port = 80
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
port, _ = strconv.Atoi(res[17])
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
@ -0,0 +1,102 @@
|
|||||||
|
package ping
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TCPing ...
|
||||||
|
type TCPing struct {
|
||||||
|
target *Target
|
||||||
|
done chan struct{}
|
||||||
|
result *Result
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Pinger = (*TCPing)(nil)
|
||||||
|
|
||||||
|
// NewTCPing return a new TCPing
|
||||||
|
func NewTCPing() *TCPing {
|
||||||
|
tcping := TCPing{
|
||||||
|
done: make(chan struct{}),
|
||||||
|
}
|
||||||
|
return &tcping
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTarget set target for TCPing
|
||||||
|
func (tcping *TCPing) SetTarget(target *Target) {
|
||||||
|
tcping.target = target
|
||||||
|
if tcping.result == nil {
|
||||||
|
tcping.result = &Result{Target: target}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result return the result
|
||||||
|
func (tcping TCPing) Result() *Result {
|
||||||
|
return tcping.result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a tcping
|
||||||
|
func (tcping TCPing) Start() <-chan struct{} {
|
||||||
|
go func() {
|
||||||
|
t := time.NewTicker(tcping.target.Interval)
|
||||||
|
defer t.Stop()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-t.C:
|
||||||
|
if tcping.result.Counter >= tcping.target.Counter && tcping.target.Counter != 0 {
|
||||||
|
tcping.Stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
duration, remoteAddr, err := tcping.ping()
|
||||||
|
tcping.result.Counter++
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Ping %s - failed: %s\n", tcping.target, err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Ping %s(%s) - Connected - time=%s\n", tcping.target, remoteAddr, duration)
|
||||||
|
|
||||||
|
if tcping.result.MinDuration == 0 {
|
||||||
|
tcping.result.MinDuration = duration
|
||||||
|
}
|
||||||
|
if tcping.result.MaxDuration == 0 {
|
||||||
|
tcping.result.MaxDuration = duration
|
||||||
|
}
|
||||||
|
tcping.result.SuccessCounter++
|
||||||
|
if duration > tcping.result.MaxDuration {
|
||||||
|
tcping.result.MaxDuration = duration
|
||||||
|
} else if duration < tcping.result.MinDuration {
|
||||||
|
tcping.result.MinDuration = duration
|
||||||
|
}
|
||||||
|
tcping.result.TotalDuration += duration
|
||||||
|
}
|
||||||
|
case <-tcping.done:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return tcping.done
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the tcping
|
||||||
|
func (tcping *TCPing) Stop() {
|
||||||
|
tcping.done <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tcping TCPing) ping() (time.Duration, net.Addr, error) {
|
||||||
|
var remoteAddr net.Addr
|
||||||
|
duration, errIfce := timeIt(func() interface{} {
|
||||||
|
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", tcping.target.Host, tcping.target.Port), tcping.target.Timeout)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
remoteAddr = conn.RemoteAddr()
|
||||||
|
conn.Close()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if errIfce != nil {
|
||||||
|
err := errIfce.(error)
|
||||||
|
return 0, remoteAddr, err
|
||||||
|
}
|
||||||
|
return time.Duration(duration), remoteAddr, nil
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package ping
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func timeIt(f func() interface{}) (int64, interface{}) {
|
||||||
|
startAt := time.Now()
|
||||||
|
res := f()
|
||||||
|
endAt := time.Now()
|
||||||
|
return endAt.UnixNano() - startAt.UnixNano(), res
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseCustomeDNS will set the dns to default DNS resolver for global
|
||||||
|
func UseCustomeDNS(dns []string) {
|
||||||
|
resolver := net.Resolver{
|
||||||
|
PreferGo: true,
|
||||||
|
Dial: func(ctx context.Context, network, address string) (conn net.Conn, err error) {
|
||||||
|
for _, addr := range dns {
|
||||||
|
if conn, err = net.Dial("udp", addr+":53"); err != nil {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
}
|
||||||
|
net.DefaultResolver = &resolver
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatIP - trim spaces and format IP
|
||||||
|
//
|
||||||
|
// IP - the provided IP
|
||||||
|
//
|
||||||
|
// string - return "" if the input is neither valid IPv4 nor valid IPv6
|
||||||
|
// return IPv4 in format like "192.168.9.1"
|
||||||
|
// return IPv6 in format like "[2002:ac1f:91c5:1::bd59]"
|
||||||
|
func FormatIP(IP string) string {
|
||||||
|
|
||||||
|
host := strings.Trim(IP, "[ ]")
|
||||||
|
if parseIP := net.ParseIP(host); parseIP != nil && parseIP.To4() == nil {
|
||||||
|
host = fmt.Sprintf("[%s]", host)
|
||||||
|
}
|
||||||
|
|
||||||
|
return host
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package ping
|
||||||
|
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFormatIP(t *testing.T) {
|
||||||
|
|
||||||
|
Convey("IP", t, func() {
|
||||||
|
Convey("for v4 success", func() {
|
||||||
|
rc := FormatIP("192.168.0.1")
|
||||||
|
So(rc, ShouldEqual, "192.168.0.1")
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("for v4 failure", func() {
|
||||||
|
rc := FormatIP("192.0.1")
|
||||||
|
So(rc, ShouldEqual, "")
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("for v4 format", func() {
|
||||||
|
rc := FormatIP("[192.0.1.1] ")
|
||||||
|
So(rc, ShouldEqual, "192.0.1.1")
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("for v6 success", func() {
|
||||||
|
rc := FormatIP("[2002:ac1f:91c5:1::bd59]")
|
||||||
|
So(rc, ShouldEqual, "[2002:ac1f:91c5:1::bd59]")
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("for v6 failure", func() {
|
||||||
|
rc := FormatIP("2002:ac1f:91c5:1:")
|
||||||
|
So(rc, ShouldEqual, "")
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("for v6 format", func() {
|
||||||
|
rc := FormatIP("2002:ac1f:91c5:1::bd59 ")
|
||||||
|
So(rc, ShouldEqual, "[2002:ac1f:91c5:1::bd59]")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue