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
122
123
124
125
126
127
|
// Probe returns a ProbeRunner capable of running an HTTP check.
func (pr httpProber) Probe(url *url.URL, headers http.Header, timeout time.Duration) (probe.Result, string, error) {
//會從外部帶入要 prob 的 url 物件, header timeout 等參數。
//先組成一個 http client ,Transport 這邊不是很重要先不要管他。
client := &http.Client{
Timeout: timeout,
Transport: pr.transport,
//設定轉發策略
CheckRedirect: redirectChecker(pr.followNonLocalRedirects),
}
//執行 prob 探測,傳入 http client 以及 要探測的 url 物件以及 header。
return DoHTTPProbe(url, headers, client)
}
func redirectChecker(followNonLocalRedirects bool) func(*http.Request, []*http.Request) error {
//使用預設的 Redirects ,預設十次
if followNonLocalRedirects {
return nil
}
return func(req *http.Request, via []*http.Request) error {
// 轉發得目標不等於原來要發送的目標,也是直接噴錯。
if req.URL.Hostname() != via[0].URL.Hostname() {
return http.ErrUseLastResponse
}
// Redirect >=10 就不轉發了,直接噴錯。
if len(via) >= 10 {
return errors.New("stopped after 10 redirects")
}
return nil
}
}
// GetHTTPInterface 用於發出 HTTP 請求的 interface ,回傳 response 跟 error ,這個 interface http client 有實作,可以能是未來可以抽換用的吧?
type GetHTTPInterface interface {
Do(req *http.Request) (*http.Response, error)
}
// DoHTTPProbe checks if a GET request to the url succeeds.
// If the HTTP response code is successful (i.e. 400 > code >= 200), it returns Success.
// If the HTTP response code is unsuccessful or HTTP communication fails, it returns Failure.
// This is exported because some other packages may want to do direct HTTP probes.
func DoHTTPProbe(url *url.URL, headers http.Header, client GetHTTPInterface) (probe.Result, string, error) {
//透過 http NewRequest 建立 get 方法,目標為 url 物件的網址
req, err := http.NewRequest("GET", url.String(), nil)
// 建立 http requeset 失敗,報錯回傳
if err != nil {
// Convert errors into failures to catch timeouts.
return probe.Failure, err.Error(), nil
}
//如果 header 沒有 User-Agent 的話,就主動幫他加入 header 與 User-Agent 的 key 以及 value 為 kube-probe/<Major version>.<Minor version>
if _, ok := headers["User-Agent"]; !ok {
if headers == nil {
headers = http.Header{}
}
// explicitly set User-Agent so it's not set to default Go value
v := version.Get()
headers.Set("User-Agent", fmt.Sprintf("kube-probe/%s.%s", v.Major, v.Minor))
}
//將 Header 加入 request
req.Header = headers
//如果 header 有 Host 的話就把 header 的 host 的數值加入到 requeset 的 host
if headers.Get("Host") != "" {
req.Host = headers.Get("Host")
}
//透過外面注入進來的 http client 執行 requeset 的請求
res, err := client.Do(req)
//請求失敗,報錯回傳
if err != nil {
// Convert errors into failures to catch timeouts.
return probe.Failure, err.Error(), nil
}
// defer 關閉 io 的讀取
defer res.Body.Close()
//讀取 request body ,並且限制 body 長度
b, err := utilio.ReadAtMost(res.Body, maxRespBodyLength)
//如果有錯誤的話就直接報錯,並且判斷是否超過 body 長度限制
if err != nil {
if err == utilio.ErrLimitReached {
klog.V(4).Infof("Non fatal body truncation for %s, Response: %v", url.String(), *res)
} else {
return probe.Failure, "", err
}
}
//讀出來的 body byte 轉成 string
body := string(b)
//判斷 StatusCode ,若是 StatusCode 介於 200 ~ 400 之間就當作成功,但是...StatusCode 為重新導向的話 (300) ,就回報 warring 。
if res.StatusCode >= http.StatusOK && res.StatusCode < http.StatusBadRequest {
//StatusCode 為重新導向的話 (300) ,就回報 warring 。
if res.StatusCode >= http.StatusMultipleChoices { // Redirect
klog.V(4).Infof("Probe terminated redirects for %s, Response: %v", url.String(), *res)
return probe.Warning, body, nil
}
//StatusCode 不為 300 的 200 ~ 400 其他狀況回報成功
klog.V(4).Infof("Probe succeeded for %s, Response: %v", url.String(), *res)
return probe.Success, body, nil
}
//其他不是 200 ~ 400 的狀況登回報錯誤
klog.V(4).Infof("Probe failed for %s with request headers %v, response body: %v", url.String(), headers, body)
return probe.Failure, fmt.Sprintf("HTTP probe failed with statuscode: %d", res.StatusCode), nil
}
// ReadAtMost 可以從 Reader 中讀取 byte 如果 body 大於 limit 的話就報錯~
func ReadAtMost(r io.Reader, limit int64) ([]byte, error) {
limitedReader := &io.LimitedReader{R: r, N: limit}
data, err := ioutil.ReadAll(limitedReader)
if err != nil {
return data, err
}
if limitedReader.N <= 0 {
return data, ErrLimitReached
}
return data, nil
}
|