caddy/modules/internal/network/networkproxy_test.go

115 lines
3.0 KiB
Go

package network
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"go.uber.org/zap"
"github.com/caddyserver/caddy/v2"
)
// TestProxyFromURLPlaceholderHostValidation checks that a network_proxy URL
// which resolves to a value without a host is rejected after placeholders are
// applied. url.Parse accepts both "http://:80" and "/some/path" without
// returning an error, so ProxyFunc has to reject them itself.
func TestProxyFromURLPlaceholderHostValidation(t *testing.T) {
for i, tc := range []struct {
name string
url string
hostRepl string
wantHost string
wantErr bool
}{
{
name: "port only, no host",
url: "http://{proxy.host}:80",
hostRepl: "",
wantErr: true,
},
{
name: "no scheme or host, path only",
url: "{proxy.host}/some/path",
hostRepl: "",
wantErr: true,
},
{
// url.Parse puts the userinfo elsewhere, so Host is still just
// ":8080" here. The comment above the check doesn't name this
// form, but it is equally host-less and equally unusable.
name: "userinfo but no host",
url: "http://user:pass@{proxy.host}:8080",
hostRepl: "",
wantErr: true,
},
{
name: "host and port",
url: "http://{proxy.host}:8080",
hostRepl: "proxy.example.com",
wantHost: "proxy.example.com",
wantErr: false,
},
{
name: "host without port",
url: "http://{proxy.host}",
hostRepl: "proxy.example.com",
wantHost: "proxy.example.com",
wantErr: false,
},
{
// Guards against a repair that splits Host on ":" instead of
// using Hostname(): an IPv6 literal is full of colons and must
// not be mistaken for a missing host.
name: "IPv6 literal with port",
url: "http://[{proxy.host}]:8080",
hostRepl: "::1",
wantHost: "::1",
wantErr: false,
},
{
name: "IPv6 literal without port",
url: "http://[{proxy.host}]",
hostRepl: "2001:db8::1",
wantHost: "2001:db8::1",
wantErr: false,
},
} {
p := ProxyFromURL{URL: tc.url, logger: zap.NewNop()}
repl := caddy.NewReplacer()
repl.Set("proxy.host", tc.hostRepl)
req := httptest.NewRequest(http.MethodGet, "http://example.com/", nil)
req = req.WithContext(context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl))
proxyURL, err := p.ProxyFunc()(req)
if tc.wantErr {
if err == nil {
t.Errorf("Test %d (%s): expected an error for %q but got none (proxy URL: %v)",
i, tc.name, tc.url, proxyURL)
}
if proxyURL != nil {
t.Errorf("Test %d (%s): expected a nil proxy URL for %q, got %v",
i, tc.name, tc.url, proxyURL)
}
continue
}
if err != nil {
t.Errorf("Test %d (%s): unexpected error for %q: %v", i, tc.name, tc.url, err)
continue
}
if proxyURL == nil {
t.Errorf("Test %d (%s): expected a proxy URL for %q, got nil", i, tc.name, tc.url)
continue
}
if proxyURL.Hostname() != tc.wantHost {
t.Errorf("Test %d (%s): expected host %q, got %q",
i, tc.name, tc.wantHost, proxyURL.Hostname())
}
}
}