Remove support for X-Forwarded-Port
Partially reverts change adding support for X-Forwarded-Port. Changes the logic to prefer the standard Forwarded header over X-Forwarded headers. Prefer forwarded "host" over "for" since "for" represents the client and not the client's request. Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)master
							parent
							
								
									d4c3e88426
								
							
						
					
					
						commit
						81a47d9766
					
				| 
						 | 
					@ -2,10 +2,8 @@ package v2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net"
 | 
					 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/docker/distribution/reference"
 | 
						"github.com/docker/distribution/reference"
 | 
				
			||||||
| 
						 | 
					@ -49,27 +47,34 @@ func NewURLBuilderFromString(root string, relative bool) (*URLBuilder, error) {
 | 
				
			||||||
// NewURLBuilderFromRequest uses information from an *http.Request to
 | 
					// NewURLBuilderFromRequest uses information from an *http.Request to
 | 
				
			||||||
// construct the root url.
 | 
					// construct the root url.
 | 
				
			||||||
func NewURLBuilderFromRequest(r *http.Request, relative bool) *URLBuilder {
 | 
					func NewURLBuilderFromRequest(r *http.Request, relative bool) *URLBuilder {
 | 
				
			||||||
	var scheme string
 | 
						var (
 | 
				
			||||||
 | 
					 | 
				
			||||||
	forwardedProto := r.Header.Get("X-Forwarded-Proto")
 | 
					 | 
				
			||||||
	// TODO: log the error
 | 
					 | 
				
			||||||
	forwardedHeader, _, _ := parseForwardedHeader(r.Header.Get("Forwarded"))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch {
 | 
					 | 
				
			||||||
	case len(forwardedProto) > 0:
 | 
					 | 
				
			||||||
		scheme = forwardedProto
 | 
					 | 
				
			||||||
	case len(forwardedHeader["proto"]) > 0:
 | 
					 | 
				
			||||||
		scheme = forwardedHeader["proto"]
 | 
					 | 
				
			||||||
	case r.TLS != nil:
 | 
					 | 
				
			||||||
		scheme = "https"
 | 
					 | 
				
			||||||
	case len(r.URL.Scheme) > 0:
 | 
					 | 
				
			||||||
		scheme = r.URL.Scheme
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		scheme = "http"
 | 
							scheme = "http"
 | 
				
			||||||
 | 
							host   = r.Host
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if r.TLS != nil {
 | 
				
			||||||
 | 
							scheme = "https"
 | 
				
			||||||
 | 
						} else if len(r.URL.Scheme) > 0 {
 | 
				
			||||||
 | 
							scheme = r.URL.Scheme
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	host := r.Host
 | 
						// Handle fowarded headers
 | 
				
			||||||
 | 
						// Prefer "Forwarded" header as defined by rfc7239 if given
 | 
				
			||||||
 | 
						// see https://tools.ietf.org/html/rfc7239
 | 
				
			||||||
 | 
						if forwarded := r.Header.Get("Forwarded"); len(forwarded) > 0 {
 | 
				
			||||||
 | 
							forwardedHeader, _, err := parseForwardedHeader(forwarded)
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								if fproto := forwardedHeader["proto"]; len(fproto) > 0 {
 | 
				
			||||||
 | 
									scheme = fproto
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if fhost := forwardedHeader["host"]; len(fhost) > 0 {
 | 
				
			||||||
 | 
									host = fhost
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if forwardedProto := r.Header.Get("X-Forwarded-Proto"); len(forwardedProto) > 0 {
 | 
				
			||||||
 | 
								scheme = forwardedProto
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if forwardedHost := r.Header.Get("X-Forwarded-Host"); len(forwardedHost) > 0 {
 | 
							if forwardedHost := r.Header.Get("X-Forwarded-Host"); len(forwardedHost) > 0 {
 | 
				
			||||||
			// According to the Apache mod_proxy docs, X-Forwarded-Host can be a
 | 
								// According to the Apache mod_proxy docs, X-Forwarded-Host can be a
 | 
				
			||||||
			// comma-separated list of hosts, to which each proxy appends the
 | 
								// comma-separated list of hosts, to which each proxy appends the
 | 
				
			||||||
| 
						 | 
					@ -77,38 +82,7 @@ func NewURLBuilderFromRequest(r *http.Request, relative bool) *URLBuilder {
 | 
				
			||||||
			// list.
 | 
								// list.
 | 
				
			||||||
			hosts := strings.SplitN(forwardedHost, ",", 2)
 | 
								hosts := strings.SplitN(forwardedHost, ",", 2)
 | 
				
			||||||
			host = strings.TrimSpace(hosts[0])
 | 
								host = strings.TrimSpace(hosts[0])
 | 
				
			||||||
	} else if addr, exists := forwardedHeader["for"]; exists {
 | 
					 | 
				
			||||||
		host = addr
 | 
					 | 
				
			||||||
	} else if h, exists := forwardedHeader["host"]; exists {
 | 
					 | 
				
			||||||
		host = h
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	portLessHost, port := host, ""
 | 
					 | 
				
			||||||
	if !isIPv6Address(portLessHost) {
 | 
					 | 
				
			||||||
		// with go 1.6, this would treat the last part of IPv6 address as a port
 | 
					 | 
				
			||||||
		portLessHost, port, _ = net.SplitHostPort(host)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if forwardedPort := r.Header.Get("X-Forwarded-Port"); len(port) == 0 && len(forwardedPort) > 0 {
 | 
					 | 
				
			||||||
		ports := strings.SplitN(forwardedPort, ",", 2)
 | 
					 | 
				
			||||||
		forwardedPort = strings.TrimSpace(ports[0])
 | 
					 | 
				
			||||||
		if _, err := strconv.ParseInt(forwardedPort, 10, 32); err == nil {
 | 
					 | 
				
			||||||
			port = forwardedPort
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if len(portLessHost) > 0 {
 | 
					 | 
				
			||||||
		host = portLessHost
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(port) > 0 {
 | 
					 | 
				
			||||||
		// remove enclosing brackets of ipv6 address otherwise they will be duplicated
 | 
					 | 
				
			||||||
		if len(host) > 1 && host[0] == '[' && host[len(host)-1] == ']' {
 | 
					 | 
				
			||||||
			host = host[1 : len(host)-1]
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		// JoinHostPort properly encloses ipv6 addresses in square brackets
 | 
					 | 
				
			||||||
		host = net.JoinHostPort(host, port)
 | 
					 | 
				
			||||||
	} else if isIPv6Address(host) && host[0] != '[' {
 | 
					 | 
				
			||||||
		// ipv6 needs to be enclosed in square brackets in urls
 | 
					 | 
				
			||||||
		host = "[" + host + "]"
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	basePath := routeDescriptorsMap[RouteNameBase].Path
 | 
						basePath := routeDescriptorsMap[RouteNameBase].Path
 | 
				
			||||||
| 
						 | 
					@ -290,28 +264,3 @@ func appendValues(u string, values ...url.Values) string {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return appendValuesURL(up, values...).String()
 | 
						return appendValuesURL(up, values...).String()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
// isIPv6Address returns true if given string is a valid IPv6 address. No port is allowed. The address may be
 | 
					 | 
				
			||||||
// enclosed in square brackets.
 | 
					 | 
				
			||||||
func isIPv6Address(host string) bool {
 | 
					 | 
				
			||||||
	if len(host) > 1 && host[0] == '[' && host[len(host)-1] == ']' {
 | 
					 | 
				
			||||||
		host = host[1 : len(host)-1]
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// The IPv6 scoped addressing zone identifier starts after the last percent sign.
 | 
					 | 
				
			||||||
	if i := strings.LastIndexByte(host, '%'); i > 0 {
 | 
					 | 
				
			||||||
		host = host[:i]
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ip := net.ParseIP(host)
 | 
					 | 
				
			||||||
	if ip == nil {
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if ip.To16() == nil {
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if ip.To4() == nil {
 | 
					 | 
				
			||||||
		return true
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// dot can be present in ipv4-mapped address, it needs to come after a colon though
 | 
					 | 
				
			||||||
	i := strings.IndexAny(host, ":.")
 | 
					 | 
				
			||||||
	return i >= 0 && host[i] == ':'
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -207,7 +207,7 @@ func TestBuilderFromRequest(t *testing.T) {
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "https protocol forwarded with a non-standard header",
 | 
								name: "https protocol forwarded with a non-standard header",
 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
								request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
				
			||||||
				"X-Forwarded-Proto": []string{"https"},
 | 
									"X-Custom-Forwarded-Proto": []string{"https"},
 | 
				
			||||||
			}},
 | 
								}},
 | 
				
			||||||
			base: "http://example.com",
 | 
								base: "http://example.com",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
| 
						 | 
					@ -253,6 +253,7 @@ func TestBuilderFromRequest(t *testing.T) {
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "forwarded port with a non-standard header",
 | 
								name: "forwarded port with a non-standard header",
 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
								request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
				
			||||||
 | 
									"X-Forwarded-Host": []string{"example.com:5000"},
 | 
				
			||||||
				"X-Forwarded-Port": []string{"5000"},
 | 
									"X-Forwarded-Port": []string{"5000"},
 | 
				
			||||||
			}},
 | 
								}},
 | 
				
			||||||
			base: "http://example.com:5000",
 | 
								base: "http://example.com:5000",
 | 
				
			||||||
| 
						 | 
					@ -262,12 +263,13 @@ func TestBuilderFromRequest(t *testing.T) {
 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
								request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
				
			||||||
				"X-Forwarded-Port": []string{"443 , 5001"},
 | 
									"X-Forwarded-Port": []string{"443 , 5001"},
 | 
				
			||||||
			}},
 | 
								}},
 | 
				
			||||||
			base: "http://example.com:443",
 | 
								base: "http://example.com",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "forwarded standard port with non-standard headers",
 | 
								name: "forwarded standard port with non-standard headers",
 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
								request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
				
			||||||
				"X-Forwarded-Proto": []string{"https"},
 | 
									"X-Forwarded-Proto": []string{"https"},
 | 
				
			||||||
 | 
									"X-Forwarded-Host":  []string{"example.com"},
 | 
				
			||||||
				"X-Forwarded-Port":  []string{"443"},
 | 
									"X-Forwarded-Port":  []string{"443"},
 | 
				
			||||||
			}},
 | 
								}},
 | 
				
			||||||
			base: "https://example.com",
 | 
								base: "https://example.com",
 | 
				
			||||||
| 
						 | 
					@ -276,6 +278,7 @@ func TestBuilderFromRequest(t *testing.T) {
 | 
				
			||||||
			name: "forwarded standard port with non-standard headers and explicit port",
 | 
								name: "forwarded standard port with non-standard headers and explicit port",
 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host + ":443", Header: http.Header{
 | 
								request: &http.Request{URL: u, Host: u.Host + ":443", Header: http.Header{
 | 
				
			||||||
				"X-Forwarded-Proto": []string{"https"},
 | 
									"X-Forwarded-Proto": []string{"https"},
 | 
				
			||||||
 | 
									"X-Forwarded-Host":  []string{u.Host + ":443"},
 | 
				
			||||||
				"X-Forwarded-Port":  []string{"443"},
 | 
									"X-Forwarded-Port":  []string{"443"},
 | 
				
			||||||
			}},
 | 
								}},
 | 
				
			||||||
			base: "https://example.com:443",
 | 
								base: "https://example.com:443",
 | 
				
			||||||
| 
						 | 
					@ -284,10 +287,9 @@ func TestBuilderFromRequest(t *testing.T) {
 | 
				
			||||||
			name: "several non-standard headers",
 | 
								name: "several non-standard headers",
 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
								request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
				
			||||||
				"X-Forwarded-Proto": []string{"https"},
 | 
									"X-Forwarded-Proto": []string{"https"},
 | 
				
			||||||
				"X-Forwarded-Host":  []string{" first.example.com "},
 | 
									"X-Forwarded-Host":  []string{" first.example.com:12345 "},
 | 
				
			||||||
				"X-Forwarded-Port":  []string{" 12345 \t"},
 | 
					 | 
				
			||||||
			}},
 | 
								}},
 | 
				
			||||||
			base: "http://first.example.com:12345",
 | 
								base: "https://first.example.com:12345",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "forwarded host with port supplied takes priority",
 | 
								name: "forwarded host with port supplied takes priority",
 | 
				
			||||||
| 
						 | 
					@ -308,16 +310,16 @@ func TestBuilderFromRequest(t *testing.T) {
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "forwarded protocol and addr using standard header",
 | 
								name: "forwarded protocol and addr using standard header",
 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
								request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
				
			||||||
				"Forwarded": []string{`proto=https;for="192.168.22.30:80"`},
 | 
									"Forwarded": []string{`proto=https;host="192.168.22.30:80"`},
 | 
				
			||||||
			}},
 | 
								}},
 | 
				
			||||||
			base: "https://192.168.22.30:80",
 | 
								base: "https://192.168.22.30:80",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "forwarded addr takes priority over host",
 | 
								name: "forwarded host takes priority over for",
 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
								request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
				
			||||||
				"Forwarded": []string{`host=reg.example.com;for="192.168.22.30:5000"`},
 | 
									"Forwarded": []string{`host="reg.example.com:5000";for="192.168.22.30"`},
 | 
				
			||||||
			}},
 | 
								}},
 | 
				
			||||||
			base: "http://192.168.22.30:5000",
 | 
								base: "http://reg.example.com:5000",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "forwarded host and protocol using standard header",
 | 
								name: "forwarded host and protocol using standard header",
 | 
				
			||||||
| 
						 | 
					@ -336,73 +338,26 @@ func TestBuilderFromRequest(t *testing.T) {
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "process just the first list element of standard header",
 | 
								name: "process just the first list element of standard header",
 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
								request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
				
			||||||
				"Forwarded": []string{`for="reg.example.com:443";proto=https, for="reg.example.com:80";proto=http`},
 | 
									"Forwarded": []string{`host="reg.example.com:443";proto=https, host="reg.example.com:80";proto=http`},
 | 
				
			||||||
			}},
 | 
								}},
 | 
				
			||||||
			base: "https://reg.example.com:443",
 | 
								base: "https://reg.example.com:443",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "IPv6 address override port",
 | 
								name: "IPv6 address use host",
 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
								request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
				
			||||||
				"Forwarded":        []string{`for="2607:f0d0:1002:51::4"`},
 | 
									"Forwarded":        []string{`for="2607:f0d0:1002:51::4";host="[2607:f0d0:1002:51::4]:5001"`},
 | 
				
			||||||
				"X-Forwarded-Port": []string{"5001"},
 | 
									"X-Forwarded-Port": []string{"5002"},
 | 
				
			||||||
			}},
 | 
								}},
 | 
				
			||||||
			base: "http://[2607:f0d0:1002:51::4]:5001",
 | 
								base: "http://[2607:f0d0:1002:51::4]:5001",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "IPv6 address with port",
 | 
								name: "IPv6 address with port",
 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
								request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
				
			||||||
				"Forwarded":        []string{`for="[2607:f0d0:1002:51::4]:4000"`},
 | 
									"Forwarded":        []string{`host="[2607:f0d0:1002:51::4]:4000"`},
 | 
				
			||||||
				"X-Forwarded-Port": []string{"5001"},
 | 
									"X-Forwarded-Port": []string{"5001"},
 | 
				
			||||||
			}},
 | 
								}},
 | 
				
			||||||
			base: "http://[2607:f0d0:1002:51::4]:4000",
 | 
								base: "http://[2607:f0d0:1002:51::4]:4000",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name: "IPv6 long address override port",
 | 
					 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
					 | 
				
			||||||
				"Forwarded":        []string{`for="2607:f0d0:1002:0051:0000:0000:0000:0004"`},
 | 
					 | 
				
			||||||
				"X-Forwarded-Port": []string{"5001"},
 | 
					 | 
				
			||||||
			}},
 | 
					 | 
				
			||||||
			base: "http://[2607:f0d0:1002:0051:0000:0000:0000:0004]:5001",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name: "IPv6 long address enclosed in brackets - be benevolent",
 | 
					 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
					 | 
				
			||||||
				"Forwarded":        []string{`for="[2607:f0d0:1002:0051:0000:0000:0000:0004]"`},
 | 
					 | 
				
			||||||
				"X-Forwarded-Port": []string{"5001"},
 | 
					 | 
				
			||||||
			}},
 | 
					 | 
				
			||||||
			base: "http://[2607:f0d0:1002:0051:0000:0000:0000:0004]:5001",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name: "IPv6 long address with port",
 | 
					 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
					 | 
				
			||||||
				"Forwarded":        []string{`for="[2607:f0d0:1002:0051:0000:0000:0000:0004]:4321"`},
 | 
					 | 
				
			||||||
				"X-Forwarded-Port": []string{"5001"},
 | 
					 | 
				
			||||||
			}},
 | 
					 | 
				
			||||||
			base: "http://[2607:f0d0:1002:0051:0000:0000:0000:0004]:4321",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name: "IPv6 address with zone ID",
 | 
					 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
					 | 
				
			||||||
				"Forwarded":        []string{`for="fe80::bd0f:a8bc:6480:238b%11"`},
 | 
					 | 
				
			||||||
				"X-Forwarded-Port": []string{"5001"},
 | 
					 | 
				
			||||||
			}},
 | 
					 | 
				
			||||||
			base: "http://[fe80::bd0f:a8bc:6480:238b%2511]:5001",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name: "IPv6 address with zone ID and port",
 | 
					 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
					 | 
				
			||||||
				"Forwarded":        []string{`for="[fe80::bd0f:a8bc:6480:238b%eth0]:12345"`},
 | 
					 | 
				
			||||||
				"X-Forwarded-Port": []string{"5001"},
 | 
					 | 
				
			||||||
			}},
 | 
					 | 
				
			||||||
			base: "http://[fe80::bd0f:a8bc:6480:238b%25eth0]:12345",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name: "IPv6 address without port",
 | 
					 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
					 | 
				
			||||||
				"Forwarded": []string{`for="::FFFF:129.144.52.38"`},
 | 
					 | 
				
			||||||
			}},
 | 
					 | 
				
			||||||
			base: "http://[::FFFF:129.144.52.38]",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "non-standard and standard forward headers",
 | 
								name: "non-standard and standard forward headers",
 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
								request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
				
			||||||
| 
						 | 
					@ -414,14 +369,34 @@ func TestBuilderFromRequest(t *testing.T) {
 | 
				
			||||||
			base: "https://first.example.com",
 | 
								base: "https://first.example.com",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "non-standard headers take precedence over standard one",
 | 
								name: "standard header takes precedence over non-standard headers",
 | 
				
			||||||
			request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
								request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
				
			||||||
				"X-Forwarded-Proto": []string{`http`},
 | 
									"X-Forwarded-Proto": []string{`http`},
 | 
				
			||||||
				"Forwarded":         []string{`host=second.example.com; proto=https`},
 | 
									"Forwarded":         []string{`host=second.example.com; proto=https`},
 | 
				
			||||||
				"X-Forwarded-Host":  []string{`first.example.com`},
 | 
									"X-Forwarded-Host":  []string{`first.example.com`},
 | 
				
			||||||
				"X-Forwarded-Port":  []string{`4000`},
 | 
									"X-Forwarded-Port":  []string{`4000`},
 | 
				
			||||||
			}},
 | 
								}},
 | 
				
			||||||
			base: "http://first.example.com:4000",
 | 
								base: "https://second.example.com",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "incomplete standard header uses default",
 | 
				
			||||||
 | 
								request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
				
			||||||
 | 
									"X-Forwarded-Proto": []string{`https`},
 | 
				
			||||||
 | 
									"Forwarded":         []string{`for=127.0.0.1`},
 | 
				
			||||||
 | 
									"X-Forwarded-Host":  []string{`first.example.com`},
 | 
				
			||||||
 | 
									"X-Forwarded-Port":  []string{`4000`},
 | 
				
			||||||
 | 
								}},
 | 
				
			||||||
 | 
								base: "http://" + u.Host,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "standard with just proto",
 | 
				
			||||||
 | 
								request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
 | 
				
			||||||
 | 
									"X-Forwarded-Proto": []string{`https`},
 | 
				
			||||||
 | 
									"Forwarded":         []string{`proto=https`},
 | 
				
			||||||
 | 
									"X-Forwarded-Host":  []string{`first.example.com`},
 | 
				
			||||||
 | 
									"X-Forwarded-Port":  []string{`4000`},
 | 
				
			||||||
 | 
								}},
 | 
				
			||||||
 | 
								base: "https://" + u.Host,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -444,24 +419,10 @@ func TestBuilderFromRequest(t *testing.T) {
 | 
				
			||||||
					continue
 | 
										continue
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				var expectedURL string
 | 
									expectedURL := testCase.expectedPath
 | 
				
			||||||
				proto, ok := tr.request.Header["X-Forwarded-Proto"]
 | 
					 | 
				
			||||||
				if !ok {
 | 
					 | 
				
			||||||
					expectedURL = testCase.expectedPath
 | 
					 | 
				
			||||||
				if !relative {
 | 
									if !relative {
 | 
				
			||||||
					expectedURL = tr.base + expectedURL
 | 
										expectedURL = tr.base + expectedURL
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					urlBase, err := url.Parse(tr.base)
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						t.Fatal(err)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					urlBase.Scheme = proto[0]
 | 
					 | 
				
			||||||
					expectedURL = testCase.expectedPath
 | 
					 | 
				
			||||||
					if !relative {
 | 
					 | 
				
			||||||
						expectedURL = urlBase.String() + expectedURL
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if buildURL != expectedURL {
 | 
									if buildURL != expectedURL {
 | 
				
			||||||
					t.Errorf("[relative=%t, request=%q, case=%q]: %q != %q", relative, tr.name, testCase.description, buildURL, expectedURL)
 | 
										t.Errorf("[relative=%t, request=%q, case=%q]: %q != %q", relative, tr.name, testCase.description, buildURL, expectedURL)
 | 
				
			||||||
| 
						 | 
					@ -557,119 +518,3 @@ func TestBuilderFromRequestWithPrefix(t *testing.T) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestIsIPv6Address(t *testing.T) {
 | 
					 | 
				
			||||||
	for _, tc := range []struct {
 | 
					 | 
				
			||||||
		name    string
 | 
					 | 
				
			||||||
		address string
 | 
					 | 
				
			||||||
		isIPv6  bool
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "IPv6 short address",
 | 
					 | 
				
			||||||
			address: `2607:f0d0:1002:51::4`,
 | 
					 | 
				
			||||||
			isIPv6:  true,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "IPv6 short address enclosed in brackets",
 | 
					 | 
				
			||||||
			address: "[2607:f0d0:1002:51::4]",
 | 
					 | 
				
			||||||
			isIPv6:  true,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "IPv6 address",
 | 
					 | 
				
			||||||
			address: `2607:f0d0:1002:0051:0000:0000:0000:0004`,
 | 
					 | 
				
			||||||
			isIPv6:  true,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "IPv6 address with numeric zone ID",
 | 
					 | 
				
			||||||
			address: `fe80::bd0f:a8bc:6480:238b%11`,
 | 
					 | 
				
			||||||
			isIPv6:  true,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "IPv6 address with device name as zone ID",
 | 
					 | 
				
			||||||
			address: `fe80::bd0f:a8bc:6480:238b%eth0`,
 | 
					 | 
				
			||||||
			isIPv6:  true,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "IPv6 address with device name as zone ID enclosed in brackets",
 | 
					 | 
				
			||||||
			address: `[fe80::bd0f:a8bc:6480:238b%eth0]`,
 | 
					 | 
				
			||||||
			isIPv6:  true,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "IPv4-mapped address",
 | 
					 | 
				
			||||||
			address: "::FFFF:129.144.52.38",
 | 
					 | 
				
			||||||
			isIPv6:  true,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "localhost",
 | 
					 | 
				
			||||||
			address: "::1",
 | 
					 | 
				
			||||||
			isIPv6:  true,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "localhost",
 | 
					 | 
				
			||||||
			address: "::1",
 | 
					 | 
				
			||||||
			isIPv6:  true,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "long localhost address",
 | 
					 | 
				
			||||||
			address: "0:0:0:0:0:0:0:1",
 | 
					 | 
				
			||||||
			isIPv6:  true,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "IPv6 long address with port",
 | 
					 | 
				
			||||||
			address: "[2607:f0d0:1002:0051:0000:0000:0000:0004]:4321",
 | 
					 | 
				
			||||||
			isIPv6:  false,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "too many groups",
 | 
					 | 
				
			||||||
			address: "2607:f0d0:1002:0051:0000:0000:0000:0004:4321",
 | 
					 | 
				
			||||||
			isIPv6:  false,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "square brackets don't make an IPv6 address",
 | 
					 | 
				
			||||||
			address: "[2607:f0d0]",
 | 
					 | 
				
			||||||
			isIPv6:  false,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "require two consecutive colons in localhost",
 | 
					 | 
				
			||||||
			address: ":1",
 | 
					 | 
				
			||||||
			isIPv6:  false,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "more then 4 hexadecimal digits",
 | 
					 | 
				
			||||||
			address: "2607:f0d0b:1002:0051:0000:0000:0000:0004",
 | 
					 | 
				
			||||||
			isIPv6:  false,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "too short address",
 | 
					 | 
				
			||||||
			address: `2607:f0d0:1002:0000:0000:0000:0004`,
 | 
					 | 
				
			||||||
			isIPv6:  false,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "IPv4 address",
 | 
					 | 
				
			||||||
			address: `192.168.100.1`,
 | 
					 | 
				
			||||||
			isIPv6:  false,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "unclosed bracket",
 | 
					 | 
				
			||||||
			address: `[2607:f0d0:1002:0051:0000:0000:0000:0004`,
 | 
					 | 
				
			||||||
			isIPv6:  false,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "trailing bracket",
 | 
					 | 
				
			||||||
			address: `2607:f0d0:1002:0051:0000:0000:0000:0004]`,
 | 
					 | 
				
			||||||
			isIPv6:  false,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "domain name",
 | 
					 | 
				
			||||||
			address: `localhost`,
 | 
					 | 
				
			||||||
			isIPv6:  false,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	} {
 | 
					 | 
				
			||||||
		isIPv6 := isIPv6Address(tc.address)
 | 
					 | 
				
			||||||
		if isIPv6 && !tc.isIPv6 {
 | 
					 | 
				
			||||||
			t.Errorf("[%s] address %q falsely detected as IPv6 address", tc.name, tc.address)
 | 
					 | 
				
			||||||
		} else if !isIPv6 && tc.isIPv6 {
 | 
					 | 
				
			||||||
			t.Errorf("[%s] address %q not recognized as IPv6", tc.name, tc.address)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue