https.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. package nettools
  2. import (
  3. "crypto/tls"
  4. "fmt"
  5. "golang.org/x/net/context"
  6. "io/ioutil"
  7. "net"
  8. "net/http"
  9. "net/url"
  10. "strings"
  11. "time"
  12. )
  13. type HttpPingResult struct {
  14. Time int
  15. Proto string
  16. Status int
  17. Length int
  18. Err error
  19. IP net.IP
  20. }
  21. func (httpR *HttpPingResult) Result() int {
  22. return httpR.Time
  23. }
  24. func (httpR *HttpPingResult) Error() error {
  25. return httpR.Err
  26. }
  27. func (httpR *HttpPingResult) String() string {
  28. if httpR.Err != nil {
  29. return fmt.Sprintf("%s", httpR.Err)
  30. } else {
  31. return fmt.Sprintf("%s: protocol=%s, status=%d, length=%d, time=%d ms", httpR.IP.String(), httpR.Proto, httpR.Status, httpR.Length, httpR.Time)
  32. }
  33. }
  34. type HttpPing struct {
  35. Method string
  36. URL string
  37. Timeout time.Duration
  38. // 以下参数全部为可选
  39. DisableHttp2 bool
  40. DisableCompression bool
  41. Insecure bool
  42. Referrer string
  43. UserAgent string
  44. IP net.IP
  45. }
  46. func (httpC *HttpPing) Ping() IPingResult {
  47. return httpC.PingContext(context.Background())
  48. }
  49. func (httpC *HttpPing) PingContext(ctx context.Context) IPingResult {
  50. u, err := url.Parse(httpC.URL)
  51. if err != nil {
  52. return httpC.errorResult(err)
  53. }
  54. host := u.Hostname()
  55. ip := cloneIP(httpC.IP)
  56. if ip == nil {
  57. var err error
  58. ip, err = LookupFunc(host)
  59. if err != nil {
  60. return httpC.errorResult(err)
  61. }
  62. }
  63. dialer := &net.Dialer{
  64. Timeout: httpC.Timeout,
  65. KeepAlive: -1,
  66. }
  67. dialfunc := func(ctx context.Context, network, address string) (net.Conn, error) {
  68. h, p, err := net.SplitHostPort(address)
  69. if err != nil {
  70. return nil, err
  71. }
  72. if ip == nil || !strings.EqualFold(h, host) {
  73. var err error
  74. ip, err = LookupFunc(h)
  75. if err != nil {
  76. return nil, err
  77. }
  78. }
  79. addr := net.JoinHostPort(ip.String(), p)
  80. return dialer.DialContext(ctx, network, addr)
  81. }
  82. trans := http.DefaultTransport.(*http.Transport).Clone()
  83. trans.DialContext = dialfunc
  84. trans.DisableKeepAlives = true
  85. trans.MaxIdleConnsPerHost = -1
  86. trans.DisableCompression = httpC.DisableCompression
  87. trans.ForceAttemptHTTP2 = !httpC.DisableHttp2
  88. trans.TLSClientConfig = &tls.Config{
  89. InsecureSkipVerify: httpC.Insecure,
  90. }
  91. req, err := http.NewRequestWithContext(ctx, httpC.Method, httpC.URL, nil)
  92. if err != nil {
  93. return httpC.errorResult(err)
  94. }
  95. if httpC.UserAgent == "" {
  96. httpC.UserAgent = "httping"
  97. }
  98. req.Header.Set("User-Agent", httpC.UserAgent)
  99. if httpC.Referrer != "" {
  100. req.Header.Set("Referer", httpC.Referrer)
  101. }
  102. client := &http.Client{}
  103. client.Transport = trans
  104. client.Timeout = httpC.Timeout
  105. client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
  106. return http.ErrUseLastResponse
  107. }
  108. t0 := time.Now()
  109. resp, err := client.Do(req)
  110. if err != nil {
  111. return httpC.errorResult(err)
  112. }
  113. defer resp.Body.Close()
  114. body, err := ioutil.ReadAll(resp.Body)
  115. if err != nil {
  116. return httpC.errorResult(err)
  117. }
  118. return &HttpPingResult{int(time.Now().Sub(t0).Milliseconds()), resp.Proto, resp.StatusCode, len(body), nil, ip}
  119. }
  120. func (httpC *HttpPing) errorResult(err error) *HttpPingResult {
  121. r := &HttpPingResult{}
  122. r.Err = err
  123. return r
  124. }
  125. func NewHttpPing(method, url string, timeout time.Duration) *HttpPing {
  126. return &HttpPing{
  127. Method: method,
  128. URL: url,
  129. Timeout: timeout,
  130. }
  131. }
  132. var (
  133. _ IPing = (*HttpPing)(nil)
  134. _ IPingResult = (*HttpPingResult)(nil)
  135. )