1package v4
2
3import (
4	"net/http"
5	"strings"
6)
7
8// SanitizeHostForHeader removes default port from host and updates request.Host
9func SanitizeHostForHeader(r *http.Request) {
10	host := getHost(r)
11	port := portOnly(host)
12	if port != "" && isDefaultPort(r.URL.Scheme, port) {
13		r.Host = stripPort(host)
14	}
15}
16
17// Returns host from request
18func getHost(r *http.Request) string {
19	if r.Host != "" {
20		return r.Host
21	}
22
23	return r.URL.Host
24}
25
26// Hostname returns u.Host, without any port number.
27//
28// If Host is an IPv6 literal with a port number, Hostname returns the
29// IPv6 literal without the square brackets. IPv6 literals may include
30// a zone identifier.
31//
32// Copied from the Go 1.8 standard library (net/url)
33func stripPort(hostport string) string {
34	colon := strings.IndexByte(hostport, ':')
35	if colon == -1 {
36		return hostport
37	}
38	if i := strings.IndexByte(hostport, ']'); i != -1 {
39		return strings.TrimPrefix(hostport[:i], "[")
40	}
41	return hostport[:colon]
42}
43
44// Port returns the port part of u.Host, without the leading colon.
45// If u.Host doesn't contain a port, Port returns an empty string.
46//
47// Copied from the Go 1.8 standard library (net/url)
48func portOnly(hostport string) string {
49	colon := strings.IndexByte(hostport, ':')
50	if colon == -1 {
51		return ""
52	}
53	if i := strings.Index(hostport, "]:"); i != -1 {
54		return hostport[i+len("]:"):]
55	}
56	if strings.Contains(hostport, "]") {
57		return ""
58	}
59	return hostport[colon+len(":"):]
60}
61
62// Returns true if the specified URI is using the standard port
63// (i.e. port 80 for HTTP URIs or 443 for HTTPS URIs)
64func isDefaultPort(scheme, port string) bool {
65	if port == "" {
66		return true
67	}
68
69	lowerCaseScheme := strings.ToLower(scheme)
70	if (lowerCaseScheme == "http" && port == "80") || (lowerCaseScheme == "https" && port == "443") {
71		return true
72	}
73
74	return false
75}
76