115 lines
3.0 KiB
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())
|
|
}
|
|
}
|
|
}
|