blob: b7bcec8ae3a93267e6445ab8fe368ee00970cf44 [file] [log] [blame] [edit]
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package proxy
import (
"context"
"errors"
"fmt"
"net"
"slices"
"testing"
)
type recordingProxy struct {
addrs []string
}
func (r *recordingProxy) Dial(network, addr string) (net.Conn, error) {
r.addrs = append(r.addrs, addr)
return nil, errors.New("recordingProxy")
}
func TestPerHost(t *testing.T) {
for _, test := range []struct {
config string // passed to PerHost.AddFromString
nomatch []string // addrs using the default dialer
match []string // addrs using the bypass dialer
}{{
config: "localhost,*.zone,127.0.0.1,10.0.0.1/8,1000::/16",
nomatch: []string{
"example.com:123",
"1.2.3.4:123",
"[1001::]:123",
},
match: []string{
"localhost:123",
"zone:123",
"foo.zone:123",
"127.0.0.1:123",
"10.1.2.3:123",
"[1000::]:123",
"[1000::%25.example.com]:123",
},
}, {
config: "localhost",
nomatch: []string{
"127.0.0.1:80",
},
match: []string{
"localhost:80",
},
}, {
config: "*.zone",
nomatch: []string{
"foo.com:80",
},
match: []string{
"foo.zone:80",
"foo.bar.zone:80",
},
}, {
config: "1.2.3.4",
nomatch: []string{
"127.0.0.1:80",
"11.2.3.4:80",
},
match: []string{
"1.2.3.4:80",
},
}, {
config: "10.0.0.0/24",
nomatch: []string{
"10.0.1.1:80",
},
match: []string{
"10.0.0.1:80",
"10.0.0.255:80",
},
}, {
config: "fe80::/10",
nomatch: []string{
"[fec0::1]:80",
"[fec0::1%en0]:80",
},
match: []string{
"[fe80::1]:80",
"[fe80::1%en0]:80",
},
}, {
// We don't allow zone IDs in network prefixes,
// so this config matches nothing.
config: "fe80::%en0/10",
nomatch: []string{
"[fec0::1]:80",
"[fec0::1%en0]:80",
"[fe80::1]:80",
"[fe80::1%en0]:80",
"[fe80::1%en1]:80",
},
}} {
for _, addr := range test.match {
testPerHost(t, test.config, addr, true)
}
for _, addr := range test.nomatch {
testPerHost(t, test.config, addr, false)
}
}
}
func testPerHost(t *testing.T, config, addr string, wantMatch bool) {
name := fmt.Sprintf("config %q, dial %q", config, addr)
var def, bypass recordingProxy
perHost := NewPerHost(&def, &bypass)
perHost.AddFromString(config)
perHost.Dial("tcp", addr)
// Dial and DialContext should have the same results.
var defc, bypassc recordingProxy
perHostc := NewPerHost(&defc, &bypassc)
perHostc.AddFromString(config)
perHostc.DialContext(context.Background(), "tcp", addr)
if !slices.Equal(def.addrs, defc.addrs) {
t.Errorf("%v: Dial default=%v, bypass=%v; DialContext default=%v, bypass=%v", name, def.addrs, bypass.addrs, defc.addrs, bypass.addrs)
return
}
if got, want := slices.Concat(def.addrs, bypass.addrs), []string{addr}; !slices.Equal(got, want) {
t.Errorf("%v: dialed %q, want %q", name, got, want)
return
}
gotMatch := len(bypass.addrs) > 0
if gotMatch != wantMatch {
t.Errorf("%v: matched=%v, want %v", name, gotMatch, wantMatch)
return
}
}