1
16
17 package http
18
19 import (
20 "crypto/tls"
21 "errors"
22 "fmt"
23 "net/http"
24 "time"
25
26 utilnet "k8s.io/apimachinery/pkg/util/net"
27 "k8s.io/kubernetes/pkg/probe"
28
29 "k8s.io/klog/v2"
30 utilio "k8s.io/utils/io"
31 )
32
33 const (
34 maxRespBodyLength = 10 * 1 << 10
35 )
36
37
38
39
40 func New(followNonLocalRedirects bool) Prober {
41 tlsConfig := &tls.Config{InsecureSkipVerify: true}
42 return NewWithTLSConfig(tlsConfig, followNonLocalRedirects)
43 }
44
45
46
47
48 func NewWithTLSConfig(config *tls.Config, followNonLocalRedirects bool) Prober {
49
50 transport := utilnet.SetTransportDefaults(
51 &http.Transport{
52 TLSClientConfig: config,
53 DisableKeepAlives: true,
54 Proxy: http.ProxyURL(nil),
55 DisableCompression: true,
56
57
58 DialContext: probe.ProbeDialer().DialContext,
59 })
60
61 return httpProber{transport, followNonLocalRedirects}
62 }
63
64
65 type Prober interface {
66 Probe(req *http.Request, timeout time.Duration) (probe.Result, string, error)
67 }
68
69 type httpProber struct {
70 transport *http.Transport
71 followNonLocalRedirects bool
72 }
73
74
75 func (pr httpProber) Probe(req *http.Request, timeout time.Duration) (probe.Result, string, error) {
76 client := &http.Client{
77 Timeout: timeout,
78 Transport: pr.transport,
79 CheckRedirect: RedirectChecker(pr.followNonLocalRedirects),
80 }
81 return DoHTTPProbe(req, client)
82 }
83
84
85 type GetHTTPInterface interface {
86 Do(req *http.Request) (*http.Response, error)
87 }
88
89
90
91
92
93 func DoHTTPProbe(req *http.Request, client GetHTTPInterface) (probe.Result, string, error) {
94 url := req.URL
95 headers := req.Header
96 res, err := client.Do(req)
97 if err != nil {
98
99 return probe.Failure, err.Error(), nil
100 }
101 defer res.Body.Close()
102 b, err := utilio.ReadAtMost(res.Body, maxRespBodyLength)
103 if err != nil {
104 if err == utilio.ErrLimitReached {
105 klog.V(4).Infof("Non fatal body truncation for %s, Response: %v", url.String(), *res)
106 } else {
107 return probe.Failure, "", err
108 }
109 }
110 body := string(b)
111 if res.StatusCode >= http.StatusOK && res.StatusCode < http.StatusBadRequest {
112 if res.StatusCode >= http.StatusMultipleChoices {
113 klog.V(4).Infof("Probe terminated redirects for %s, Response: %v", url.String(), *res)
114 return probe.Warning, fmt.Sprintf("Probe terminated redirects, Response body: %v", body), nil
115 }
116 klog.V(4).Infof("Probe succeeded for %s, Response: %v", url.String(), *res)
117 return probe.Success, body, nil
118 }
119 klog.V(4).Infof("Probe failed for %s with request headers %v, response body: %v", url.String(), headers, body)
120
121 failureMsg := fmt.Sprintf("HTTP probe failed with statuscode: %d", res.StatusCode)
122 return probe.Failure, failureMsg, nil
123 }
124
125
126 func RedirectChecker(followNonLocalRedirects bool) func(*http.Request, []*http.Request) error {
127 if followNonLocalRedirects {
128 return nil
129 }
130
131 return func(req *http.Request, via []*http.Request) error {
132 if req.URL.Hostname() != via[0].URL.Hostname() {
133 return http.ErrUseLastResponse
134 }
135
136 if len(via) >= 10 {
137 return errors.New("stopped after 10 redirects")
138 }
139 return nil
140 }
141 }
142
View as plain text