package httprequest
import (
"crypto/tls"
"net/http"
"net/url"
"testing"
)
func TestSchemeHost(t *testing.T) {
testcases := map[string]struct {
req *http.Request
expectedScheme string
expectedHost string
}{
"X-Forwarded-Host and X-Forwarded-Port combined": {
req: &http.Request{
URL: &url.URL{Path: "/"},
Host: "127.0.0.1",
Header: http.Header{
"X-Forwarded-Host": []string{"example.com"},
"X-Forwarded-Port": []string{"443"},
"X-Forwarded-Proto": []string{"https"},
},
},
expectedScheme: "https",
expectedHost: "example.com:443",
},
"X-Forwarded-Port overwrites X-Forwarded-Host port": {
req: &http.Request{
URL: &url.URL{Path: "/"},
Host: "127.0.0.1",
Header: http.Header{
"X-Forwarded-Host": []string{"example.com:1234"},
"X-Forwarded-Port": []string{"443"},
"X-Forwarded-Proto": []string{"https"},
},
},
expectedScheme: "https",
expectedHost: "example.com:443",
},
"X-Forwarded-* multiple attrs": {
req: &http.Request{
URL: &url.URL{Host: "urlhost", Path: "/"},
Host: "reqhost",
Header: http.Header{
"X-Forwarded-Host": []string{"example.com,foo.com"},
"X-Forwarded-Port": []string{"443,123"},
"X-Forwarded-Proto": []string{"https,http"},
},
},
expectedScheme: "https",
expectedHost: "example.com:443",
},
"req host": {
req: &http.Request{
URL: &url.URL{Host: "urlhost", Path: "/"},
Host: "example.com",
},
expectedScheme: "http",
expectedHost: "example.com",
},
"req host with port": {
req: &http.Request{
URL: &url.URL{Host: "urlhost", Path: "/"},
Host: "example.com:80",
},
expectedScheme: "http",
expectedHost: "example.com:80",
},
"req host with tls port": {
req: &http.Request{
URL: &url.URL{Host: "urlhost", Path: "/"},
Host: "example.com:443",
},
expectedScheme: "https",
expectedHost: "example.com:443",
},
"req tls": {
req: &http.Request{
URL: &url.URL{Path: "/"},
Host: "example.com",
TLS: &tls.ConnectionState{},
},
expectedScheme: "https",
expectedHost: "example.com",
},
"req url": {
req: &http.Request{
URL: &url.URL{Scheme: "https", Host: "example.com", Path: "/"},
},
expectedScheme: "https",
expectedHost: "example.com",
},
"req url with port": {
req: &http.Request{
URL: &url.URL{Scheme: "https", Host: "example.com:123", Path: "/"},
},
expectedScheme: "https",
expectedHost: "example.com:123",
},
// The following scenarios are captured from actual direct requests to pods
"non-tls pod": {
req: &http.Request{
URL: &url.URL{Path: "/"},
Host: "172.17.0.2:9080",
},
expectedScheme: "http",
expectedHost: "172.17.0.2:9080",
},
"tls pod": {
req: &http.Request{
URL: &url.URL{Path: "/"},
Host: "172.17.0.2:9443",
TLS: &tls.ConnectionState{ /* request has non-nil TLS connection state */ },
},
expectedScheme: "https",
expectedHost: "172.17.0.2:9443",
},
// The following scenarios are captured from actual requests to pods via services
"svc -> non-tls pod": {
req: &http.Request{
URL: &url.URL{Path: "/"},
Host: "service.default.svc.cluster.local:10080",
},
expectedScheme: "http",
expectedHost: "service.default.svc.cluster.local:10080",
},
"svc -> tls pod": {
req: &http.Request{
URL: &url.URL{Path: "/"},
Host: "service.default.svc.cluster.local:10443",
TLS: &tls.ConnectionState{ /* request has non-nil TLS connection state */ },
},
expectedScheme: "https",
expectedHost: "service.default.svc.cluster.local:10443",
},
// The following scenarios are captured from actual requests to pods via services via routes serviced by haproxy
"haproxy non-tls route -> svc -> non-tls pod": {
req: &http.Request{
URL: &url.URL{Path: "/"},
Host: "route-namespace.router.default.svc.cluster.local",
Header: http.Header{
"X-Forwarded-Host": []string{"route-namespace.router.default.svc.cluster.local"},
"X-Forwarded-Port": []string{"80"},
"X-Forwarded-Proto": []string{"http"},
"Forwarded": []string{"for=172.18.2.57;host=route-namespace.router.default.svc.cluster.local;proto=http"},
"X-Forwarded-For": []string{"172.18.2.57"},
},
},
expectedScheme: "http",
expectedHost: "route-namespace.router.default.svc.cluster.local:80",
},
"haproxy edge terminated route -> svc -> non-tls pod": {
req: &http.Request{
URL: &url.URL{Path: "/"},
Host: "route-namespace.router.default.svc.cluster.local",
Header: http.Header{
"X-Forwarded-Host": []string{"route-namespace.router.default.svc.cluster.local"},
"X-Forwarded-Port": []string{"443"},
"X-Forwarded-Proto": []string{"https"},
"Forwarded": []string{"for=172.18.2.57;host=route-namespace.router.default.svc.cluster.local;proto=https"},
"X-Forwarded-For": []string{"172.18.2.57"},
},
},
expectedScheme: "https",
expectedHost: "route-namespace.router.default.svc.cluster.local:443",
},
"haproxy passthrough route -> svc -> tls pod": {
req: &http.Request{
URL: &url.URL{Path: "/"},
Host: "route-namespace.router.default.svc.cluster.local",
TLS: &tls.ConnectionState{ /* request has non-nil TLS connection state */ },
},
expectedScheme: "https",
expectedHost: "route-namespace.router.default.svc.cluster.local",
},
}
for k, tc := range testcases {
scheme, host := SchemeHost(tc.req)
if scheme != tc.expectedScheme {
t.Errorf("%s: expected scheme %q, got %q", k, tc.expectedScheme, scheme)
}
if host != tc.expectedHost {
t.Errorf("%s: expected host %q, got %q", k, tc.expectedHost, host)
}
}
}