forked from cloverstd/tcping
-
Notifications
You must be signed in to change notification settings - Fork 0
/
http.go
121 lines (111 loc) · 2.77 KB
/
http.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package ping
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net"
"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, net.Addr, 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, nil, err
}
var remoteAddr net.Addr
trace := &httptrace.ClientTrace{
GotConn: func(connInfo httptrace.GotConnInfo) {
remoteAddr = connInfo.Conn.RemoteAddr()
},
}
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, nil, err
}
return time.Duration(duration), resp, remoteAddr, nil
}