tcping/httping/httping.go
2020-05-11 22:18:16 +08:00

106 lines
2.0 KiB
Go

package httping
import (
"crypto/tls"
"log"
"net/http"
"net/http/httptrace"
"regexp"
"strings"
"time"
"golang.org/x/net/http/httpproxy"
)
type Stats struct {
Proxy bool
Scheme string
DNS int64
TCP int64
TLS int64
Process int64
Transfer int64
Total int64
}
func New(address string) (Stats, error) {
var err error
var stats Stats
var t0, t1, t2, t3, t4, t5, t6, t7 int64
req, _ := http.NewRequest("GET", address, nil)
trace := &httptrace.ClientTrace{
DNSStart: func(info httptrace.DNSStartInfo) {
t0 = time.Now().UnixNano()
},
DNSDone: func(info httptrace.DNSDoneInfo) {
t1 = time.Now().UnixNano()
if info.Err != nil {
err = info.Err
log.Fatal(info.Err)
}
},
ConnectStart: func(net, addr string) {
},
ConnectDone: func(net, addr string, err error) {
if err != nil {
log.Fatalf("unable to connect to host %v: %v", addr, err)
}
t2 = time.Now().UnixNano()
},
GotConn: func(info httptrace.GotConnInfo) {
t3 = time.Now().UnixNano()
},
GotFirstResponseByte: func() {
t4 = time.Now().UnixNano()
},
TLSHandshakeStart: func() {
t5 = time.Now().UnixNano()
},
TLSHandshakeDone: func(_ tls.ConnectionState, _ error) {
t6 = time.Now().UnixNano()
},
}
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
c := &http.Client{
Timeout: 5 * time.Second,
}
_, err = c.Do(req)
if err != nil {
match, _ := regexp.MatchString("Client.Timeout exceeded", err.Error())
if match {
log.Fatal("Connection timeout")
} else {
log.Fatal(err)
}
}
t7 = time.Now().UnixNano()
if strings.HasPrefix(address, "http://") {
stats.Scheme = "http"
} else if strings.HasPrefix(address, "https://") {
stats.Scheme = "https"
}
if t0 == 0 {
t0 = t2
t1 = t2
}
stats.DNS = t1 - t0
stats.TCP = t2 - t1
stats.Process = t4 - t3
stats.Transfer = t7 - t4
stats.TLS = t6 - t5
stats.Total = t7 - t0
// Detect proxies
pc := httpproxy.FromEnvironment()
if pc.HTTPProxy != "" {
stats.Proxy = true
}
return stats, err
}