123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- package nettools
- import (
- "crypto/tls"
- "fmt"
- "golang.org/x/net/context"
- "io/ioutil"
- "net"
- "net/http"
- "net/url"
- "strings"
- "time"
- )
- type HttpPingResult struct {
- Time int
- Proto string
- Status int
- Length int
- Err error
- IP net.IP
- }
- func (httpR *HttpPingResult) Result() int {
- return httpR.Time
- }
- func (httpR *HttpPingResult) Error() error {
- return httpR.Err
- }
- func (httpR *HttpPingResult) String() string {
- if httpR.Err != nil {
- return fmt.Sprintf("%s", httpR.Err)
- } else {
- return fmt.Sprintf("%s: protocol=%s, status=%d, length=%d, time=%d ms", httpR.IP.String(), httpR.Proto, httpR.Status, httpR.Length, httpR.Time)
- }
- }
- type HttpPing struct {
- Method string
- URL string
- Timeout time.Duration
-
- DisableHttp2 bool
- DisableCompression bool
- Insecure bool
- Referrer string
- UserAgent string
- IP net.IP
- }
- func (httpC *HttpPing) Ping() IPingResult {
- return httpC.PingContext(context.Background())
- }
- func (httpC *HttpPing) PingContext(ctx context.Context) IPingResult {
- u, err := url.Parse(httpC.URL)
- if err != nil {
- return httpC.errorResult(err)
- }
- host := u.Hostname()
- ip := cloneIP(httpC.IP)
- if ip == nil {
- var err error
- ip, err = LookupFunc(host)
- if err != nil {
- return httpC.errorResult(err)
- }
- }
- dialer := &net.Dialer{
- Timeout: httpC.Timeout,
- KeepAlive: -1,
- }
- dialfunc := func(ctx context.Context, network, address string) (net.Conn, error) {
- h, p, err := net.SplitHostPort(address)
- if err != nil {
- return nil, err
- }
- if ip == nil || !strings.EqualFold(h, host) {
- var err error
- ip, err = LookupFunc(h)
- if err != nil {
- return nil, err
- }
- }
- addr := net.JoinHostPort(ip.String(), p)
- return dialer.DialContext(ctx, network, addr)
- }
- trans := http.DefaultTransport.(*http.Transport).Clone()
- trans.DialContext = dialfunc
- trans.DisableKeepAlives = true
- trans.MaxIdleConnsPerHost = -1
- trans.DisableCompression = httpC.DisableCompression
- trans.ForceAttemptHTTP2 = !httpC.DisableHttp2
- trans.TLSClientConfig = &tls.Config{
- InsecureSkipVerify: httpC.Insecure,
- }
- req, err := http.NewRequestWithContext(ctx, httpC.Method, httpC.URL, nil)
- if err != nil {
- return httpC.errorResult(err)
- }
- if httpC.UserAgent == "" {
- httpC.UserAgent = "httping"
- }
- req.Header.Set("User-Agent", httpC.UserAgent)
- if httpC.Referrer != "" {
- req.Header.Set("Referer", httpC.Referrer)
- }
- client := &http.Client{}
- client.Transport = trans
- client.Timeout = httpC.Timeout
- client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
- return http.ErrUseLastResponse
- }
- t0 := time.Now()
- resp, err := client.Do(req)
- if err != nil {
- return httpC.errorResult(err)
- }
- defer resp.Body.Close()
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return httpC.errorResult(err)
- }
- return &HttpPingResult{int(time.Now().Sub(t0).Milliseconds()), resp.Proto, resp.StatusCode, len(body), nil, ip}
- }
- func (httpC *HttpPing) errorResult(err error) *HttpPingResult {
- r := &HttpPingResult{}
- r.Err = err
- return r
- }
- func NewHttpPing(method, url string, timeout time.Duration) *HttpPing {
- return &HttpPing{
- Method: method,
- URL: url,
- Timeout: timeout,
- }
- }
- var (
- _ IPing = (*HttpPing)(nil)
- _ IPingResult = (*HttpPingResult)(nil)
- )
|