forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 1
/
httprequest.go
129 lines (112 loc) · 3.46 KB
/
httprequest.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package httprequest
import (
"net"
"net/http"
"strings"
"bitbucket.org/ww/goautoneg"
)
// PrefersHTML returns true if the request was made by something that looks like a browser, or can receive HTML
func PrefersHTML(req *http.Request) bool {
accepts := goautoneg.ParseAccept(req.Header.Get("Accept"))
acceptsHTML := false
acceptsJSON := false
for _, accept := range accepts {
if accept.Type == "text" && accept.SubType == "html" {
acceptsHTML = true
} else if accept.Type == "application" && accept.SubType == "json" {
acceptsJSON = true
}
}
// If HTML is accepted, return true
if acceptsHTML {
return true
}
// If JSON was specifically requested, return false
// This gives browsers a way to make requests and add an "Accept" header to request JSON
if acceptsJSON {
return false
}
// In Intranet/Compatibility mode, IE sends an Accept header that does not contain "text/html".
if strings.HasPrefix(req.UserAgent(), "Mozilla") {
return true
}
return false
}
// SchemeHost returns the scheme and host used to make this request.
// Suitable for use to compute scheme/host in returned 302 redirect Location.
// Note the returned host is not normalized, and may or may not contain a port.
// Returned values are based on the following information:
//
// Host:
// * X-Forwarded-Host/X-Forwarded-Port headers
// * Host field on the request (parsed from Host header)
// * Host in the request's URL (parsed from Request-Line)
//
// Scheme:
// * X-Forwarded-Proto header
// * Existence of TLS information on the request implies https
// * Scheme in the request's URL (parsed from Request-Line)
// * Port (if included in calculated Host value, 443 implies https)
// * Otherwise, defaults to "http"
func SchemeHost(req *http.Request) (string /*scheme*/, string /*host*/) {
forwarded := func(attr string) string {
// Get the X-Forwarded-<attr> value
value := req.Header.Get("X-Forwarded-" + attr)
// Take the first comma-separated value, if multiple exist
value = strings.SplitN(value, ",", 2)[0]
// Trim whitespace
return strings.TrimSpace(value)
}
hasExplicitHost := func(h string) bool {
_, _, err := net.SplitHostPort(h)
return err == nil
}
forwardedHost := forwarded("Host")
host := ""
hostHadExplicitPort := false
switch {
case len(forwardedHost) > 0:
host = forwardedHost
hostHadExplicitPort = hasExplicitHost(host)
// If both X-Forwarded-Host and X-Forwarded-Port are sent, use the explicit port info
if forwardedPort := forwarded("Port"); len(forwardedPort) > 0 {
if h, _, err := net.SplitHostPort(forwardedHost); err == nil {
host = net.JoinHostPort(h, forwardedPort)
} else {
host = net.JoinHostPort(forwardedHost, forwardedPort)
}
}
case len(req.Host) > 0:
host = req.Host
hostHadExplicitPort = hasExplicitHost(host)
case len(req.URL.Host) > 0:
host = req.URL.Host
hostHadExplicitPort = hasExplicitHost(host)
}
port := ""
if _, p, err := net.SplitHostPort(host); err == nil {
port = p
}
forwardedProto := forwarded("Proto")
scheme := ""
switch {
case len(forwardedProto) > 0:
scheme = forwardedProto
case req.TLS != nil:
scheme = "https"
case len(req.URL.Scheme) > 0:
scheme = req.URL.Scheme
case port == "443":
scheme = "https"
default:
scheme = "http"
}
if !hostHadExplicitPort {
if (scheme == "https" && port == "443") || (scheme == "http" && port == "80") {
if hostWithoutPort, _, err := net.SplitHostPort(host); err == nil {
host = hostWithoutPort
}
}
}
return scheme, host
}