-
Notifications
You must be signed in to change notification settings - Fork 38
/
Copy pathresponse.go
112 lines (93 loc) · 3.52 KB
/
response.go
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
package hstspreload
import (
"errors"
"net/http"
"net/url"
"github.com/chromium/hstspreload/chromium/preloadlist"
)
func checkSingleHeader(resp *http.Response) (header *string, issues Issues) {
key := http.CanonicalHeaderKey("Strict-Transport-Security")
hstsHeaders := resp.Header[key]
switch {
case len(hstsHeaders) == 0:
return nil, issues.addErrorf(
"response.no_header",
"No HSTS header",
"Response error: No HSTS header is present on the response.")
case len(hstsHeaders) > 1:
// TODO: Give feedback on the first(last?) HSTS header?
return nil, issues.addErrorf(
"response.multiple_headers",
"Multiple HSTS headers",
"Response error: Multiple HSTS headers (number of HSTS headers: %d).", len(hstsHeaders))
}
return &hstsHeaders[0], issues
}
func checkResponse(resp *http.Response, headerCondition func(string, preloadlist.PolicyType) Issues, policy preloadlist.PolicyType) (header *string, issues Issues) {
header, issues = checkSingleHeader(resp)
if len(issues.Errors) > 0 {
return nil, issues
}
return header, combineIssues(issues, headerCondition(*header, policy))
}
// PreloadableResponse checks whether an resp has a single HSTS header that
// passes the preload requirements.
//
// Iff a single HSTS header was received, `header` contains its value, else
// `header` is `nil`.
// To interpret `issues`, see the list of conventions in the
// documentation for Issues.
func PreloadableResponse(resp *http.Response) (header *string, issues Issues) {
return checkResponse(resp, EligibleHeaderString, preloadlist.Bulk1Year)
}
// EligibleResponse checks whether an resp has a single HSTS header that
// passes the preload requirements.
//
// Iff a single HSTS header was received, `header` contains its value, else
// `header` is `nil`.
// To interpret `issues`, see the list of conventions in the
// documentation for Issues.
func EligibleResponse(resp *http.Response, policy preloadlist.PolicyType) (header *string, issues Issues) {
return checkResponse(resp, EligibleHeaderString, policy)
}
// RemovableResponse checks whether an resp has a single HSTS header that
// matches the requirements for removal from the HSTS preload list.
//
// Iff a single HSTS header was received, `header` contains its value, else
// `header` is `nil`.
// To interpret `issues`, see the list of conventions in the
// documentation for Issues.
func RemovableResponse(resp *http.Response) (header *string, issues Issues) {
return checkResponse(resp, func(domain string, policy preloadlist.PolicyType) Issues { return RemovableHeaderString(domain) }, preloadlist.Bulk1Year)
}
// getFirstResponse makes a GET request to `initialURL` without redirecting.
func getFirstResponse(initialURL string) (*http.Response, error) {
return getFirstResponseWithTransport(initialURL, nil)
}
// `transport` can be `nil`.
func getFirstResponseWithTransport(initialURL string, transport *http.Transport) (*http.Response, error) {
redirectPrevented := errors.New("REDIRECT_PREVENTED")
client := http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return redirectPrevented
},
Timeout: dialTimeout,
}
if transport != nil {
client.Transport = transport
}
isRedirectPrevented := func(err error) bool {
urlError, ok := err.(*url.Error)
return ok && urlError.Err == redirectPrevented
}
req, err := http.NewRequest("GET", initialURL, nil)
if err != nil {
return nil, err
}
req.Header.Set("User-Agent", "hstspreload-bot")
resp, err := client.Do(req)
if isRedirectPrevented(err) {
return resp, nil
}
return resp, err
}