func TestUnmarshal(t *testing.T) { for _, gold := range newGoldenCases() { data, err := hex.DecodeString(gold.serial) if err != nil { t.Fatal(err) } got := gen.O{} if err := got.UnmarshalBinary(data); err != nil { t.Errorf("0x%s: %s", gold.serial, err) continue } verify.Values(t, fmt.Sprintf("0x%s", gold.serial), got, gold.object) } }
func TestFromProperties(t *testing.T) { in := ` proxy.addr = :1234 proxy.localip = 4.4.4.4 proxy.strategy = rr proxy.shutdownwait = 500ms proxy.timeout = 3s proxy.dialtimeout = 60s proxy.readtimeout = 5s proxy.writetimeout = 10s proxy.maxconn = 666 proxy.header.clientip = clientip proxy.header.tls = tls proxy.header.tls.value = tls-true registry.backend = something registry.file.path = /foo/bar registry.static.routes = route add svc / http://127.0.0.1:6666/ registry.consul.addr = 1.2.3.4:5678 registry.consul.token = consul-token registry.consul.kvpath = /some/path registry.consul.tagprefix = p- registry.consul.register.addr = 6.6.6.6:7777 registry.consul.register.name = fab registry.consul.register.checkInterval = 5s registry.consul.register.checkTimeout = 10s metrics.target = graphite metrics.prefix = someprefix metrics.interval = 5s metrics.graphite.addr = 5.6.7.8:9999 runtime.gogc = 666 runtime.gomaxprocs = 12 ui.addr = 7.8.9.0:1234 ui.color = fonzy ui.title = fabfab ` out := &Config{ Proxy: Proxy{ MaxConn: 666, LocalIP: "4.4.4.4", Strategy: "rr", ShutdownWait: 500 * time.Millisecond, DialTimeout: 60 * time.Second, KeepAliveTimeout: 3 * time.Second, ResponseHeaderTimeout: 3 * time.Second, ClientIPHeader: "clientip", TLSHeader: "tls", TLSHeaderValue: "tls-true", }, Registry: Registry{ Backend: "something", File: File{ Path: "/foo/bar", }, Static: Static{ Routes: "route add svc / http://127.0.0.1:6666/", }, Consul: Consul{ Addr: "1.2.3.4:5678", Token: "consul-token", KVPath: "/some/path", TagPrefix: "p-", ServiceAddr: "6.6.6.6:7777", ServiceName: "fab", CheckInterval: 5 * time.Second, CheckTimeout: 10 * time.Second, }, }, Listen: []Listen{ { Addr: ":1234", ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, }, }, Metrics: []Metrics{ { Target: "graphite", Prefix: "someprefix", Interval: 5 * time.Second, Addr: "5.6.7.8:9999", }, }, Runtime: Runtime{ GOGC: 666, GOMAXPROCS: 12, }, UI: UI{ Addr: "7.8.9.0:1234", Color: "fonzy", Title: "fabfab", }, } p, err := properties.Load([]byte(in), properties.UTF8) if err != nil { t.Fatalf("got %v want nil", err) } cfg, err := fromProperties(p) if err != nil { t.Fatalf("got %v want nil", err) } got, want := cfg, out verify.Values(t, "cfg", got, want) }
func TestNewSource(t *testing.T) { certsource := func(typ string) config.CertSource { return config.CertSource{ Type: typ, Name: "name", CertPath: "cert", KeyPath: "key", ClientCAPath: "clientca", CAUpgradeCN: "upgcn", Refresh: 3 * time.Second, Header: http.Header{"A": []string{"b"}}, } } tests := []struct { cfg config.CertSource src Source err string }{ { cfg: config.CertSource{ Type: "invalid", }, src: nil, err: `invalid certificate source "invalid"`, }, { cfg: certsource("file"), src: FileSource{ CertFile: "cert", KeyFile: "key", ClientAuthFile: "clientca", CAUpgradeCN: "upgcn", }, }, { cfg: certsource("path"), src: PathSource{ CertPath: "cert", ClientCAPath: "clientca", CAUpgradeCN: "upgcn", Refresh: 3 * time.Second, }, }, { cfg: certsource("http"), src: HTTPSource{ CertURL: "cert", ClientCAURL: "clientca", CAUpgradeCN: "upgcn", Refresh: 3 * time.Second, }, }, { cfg: certsource("consul"), src: ConsulSource{ CertURL: "cert", ClientCAURL: "clientca", CAUpgradeCN: "upgcn", }, }, { cfg: certsource("vault"), src: VaultSource{ CertPath: "cert", ClientCAPath: "clientca", CAUpgradeCN: "upgcn", Refresh: 3 * time.Second, }, }, } for i, tt := range tests { var errmsg string src, err := NewSource(tt.cfg) if err != nil { errmsg = err.Error() } if got, want := errmsg, tt.err; got != want { t.Fatalf("%d: got %q want %q", i, got, want) } got, want := src, tt.src verify.Values(t, "src", got, want) } }
func TestLoad(t *testing.T) { tests := []struct { desc string args []string environ []string path string data string cfg func(*Config) *Config err error }{ { args: []string{"-v"}, cfg: func(cfg *Config) *Config { return nil }, }, { args: []string{"--version"}, cfg: func(cfg *Config) *Config { return nil }, }, { desc: "-v with other args", args: []string{"-a", "-v", "-b"}, cfg: func(cfg *Config) *Config { return nil }, }, { desc: "--version with other args", args: []string{"-a", "--version", "-b"}, cfg: func(cfg *Config) *Config { return nil }, }, { desc: "default config", cfg: func(cfg *Config) *Config { return cfg }, }, { args: []string{"-proxy.addr", ":5555"}, cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{{Addr: ":5555", Proto: "http"}} return cfg }, }, { args: []string{"-proxy.addr", ":5555;proto=http"}, cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{{Addr: ":5555", Proto: "http"}} return cfg }, }, { args: []string{"-proxy.addr", ":5555;proto=tcp+sni"}, cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{{Addr: ":5555", Proto: "tcp+sni"}} return cfg }, }, { args: []string{"-proxy.addr", ":5555;rt=1s;wt=2s"}, cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{{Addr: ":5555", Proto: "http", ReadTimeout: 1 * time.Second, WriteTimeout: 2 * time.Second}} return cfg }, }, { desc: "-proxy.addr with legacy cert source config", args: []string{"-proxy.addr", ":5555;pathA;pathB;pathC"}, cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{Listen{Addr: ":5555", Proto: "https"}} cfg.Listen[0].CertSource = CertSource{Type: "file", CertPath: "pathA", KeyPath: "pathB", ClientCAPath: "pathC"} return cfg }, }, { desc: "-proxy.addr with file cert source", args: []string{"-proxy.addr", ":5555;cs=name", "-proxy.cs", "cs=name;type=file;cert=value"}, cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{Listen{Addr: ":5555", Proto: "https"}} cfg.Listen[0].CertSource = CertSource{Name: "name", Type: "file", CertPath: "value"} return cfg }, }, { desc: "-proxy.addr with path cert source", args: []string{"-proxy.addr", ":5555;cs=name", "-proxy.cs", "cs=name;type=path;cert=value"}, cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{Listen{Addr: ":5555", Proto: "https"}} cfg.Listen[0].CertSource = CertSource{Name: "name", Type: "path", CertPath: "value", Refresh: 3 * time.Second} return cfg }, }, { desc: "-proxy.addr with http cert source", args: []string{"-proxy.addr", ":5555;cs=name", "-proxy.cs", "cs=name;type=http;cert=value"}, cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{Listen{Addr: ":5555", Proto: "https"}} cfg.Listen[0].CertSource = CertSource{Name: "name", Type: "http", CertPath: "value", Refresh: 3 * time.Second} return cfg }, }, { desc: "-proxy.addr with consul cert source", args: []string{"-proxy.addr", ":5555;cs=name", "-proxy.cs", "cs=name;type=consul;cert=value"}, cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{Listen{Addr: ":5555", Proto: "https"}} cfg.Listen[0].CertSource = CertSource{Name: "name", Type: "consul", CertPath: "value", Refresh: 3 * time.Second} return cfg }, }, { desc: "-proxy.addr with vault cert source", args: []string{"-proxy.addr", ":5555;cs=name", "-proxy.cs", "cs=name;type=vault;cert=value"}, cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{Listen{Addr: ":5555", Proto: "https"}} cfg.Listen[0].CertSource = CertSource{Name: "name", Type: "vault", CertPath: "value", Refresh: 3 * time.Second} return cfg }, }, { desc: "-proxy.addr with cert source", args: []string{"-proxy.addr", ":5555;cs=name;strictmatch=true", "-proxy.cs", "cs=name;type=path;cert=foo;clientca=bar;refresh=2s;hdr=a: b;caupgcn=furb"}, cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{ Listen{ Addr: ":5555", Proto: "https", StrictMatch: true, CertSource: CertSource{ Name: "name", Type: "path", CertPath: "foo", ClientCAPath: "bar", Refresh: 2 * time.Second, Header: http.Header{"A": []string{"b"}}, CAUpgradeCN: "furb", }, }, } return cfg }, }, { desc: "-proxy.addr with cert source with full options", args: []string{"-proxy.addr", ":5555;cs=name;strictmatch=true;proto=https", "-proxy.cs", "cs=name;type=path;cert=foo;clientca=bar;refresh=2s;hdr=a: b;caupgcn=furb"}, cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{ Listen{ Addr: ":5555", Proto: "https", StrictMatch: true, CertSource: CertSource{ Name: "name", Type: "path", CertPath: "foo", ClientCAPath: "bar", Refresh: 2 * time.Second, Header: http.Header{"A": []string{"b"}}, CAUpgradeCN: "furb", }, }, } return cfg }, }, { args: []string{"-proxy.localip", "1.2.3.4"}, cfg: func(cfg *Config) *Config { cfg.Proxy.LocalIP = "1.2.3.4" return cfg }, }, { args: []string{"-proxy.strategy", "value"}, cfg: func(cfg *Config) *Config { cfg.Proxy.Strategy = "value" return cfg }, }, { args: []string{"-proxy.matcher", "value"}, cfg: func(cfg *Config) *Config { cfg.Proxy.Matcher = "value" return cfg }, }, { args: []string{"-proxy.noroutestatus", "555"}, cfg: func(cfg *Config) *Config { cfg.Proxy.NoRouteStatus = 555 return cfg }, }, { args: []string{"-proxy.shutdownwait", "5ms"}, cfg: func(cfg *Config) *Config { cfg.Proxy.ShutdownWait = 5 * time.Millisecond return cfg }, }, { args: []string{"-proxy.responseheadertimeout", "5ms"}, cfg: func(cfg *Config) *Config { cfg.Proxy.ResponseHeaderTimeout = 5 * time.Millisecond return cfg }, }, { args: []string{"-proxy.keepalivetimeout", "5ms"}, cfg: func(cfg *Config) *Config { cfg.Proxy.KeepAliveTimeout = 5 * time.Millisecond return cfg }, }, { args: []string{"-proxy.dialtimeout", "5ms"}, cfg: func(cfg *Config) *Config { cfg.Proxy.DialTimeout = 5 * time.Millisecond return cfg }, }, { args: []string{"-proxy.readtimeout", "5ms"}, cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{{Addr: ":9999", Proto: "http", ReadTimeout: 5 * time.Millisecond}} return cfg }, }, { args: []string{"-proxy.writetimeout", "5ms"}, cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{{Addr: ":9999", Proto: "http", WriteTimeout: 5 * time.Millisecond}} return cfg }, }, { args: []string{"-proxy.flushinterval", "5ms"}, cfg: func(cfg *Config) *Config { cfg.Proxy.FlushInterval = 5 * time.Millisecond return cfg }, }, { args: []string{"-proxy.maxconn", "555"}, cfg: func(cfg *Config) *Config { cfg.Proxy.MaxConn = 555 return cfg }, }, { args: []string{"-proxy.header.clientip", "value"}, cfg: func(cfg *Config) *Config { cfg.Proxy.ClientIPHeader = "value" return cfg }, }, { args: []string{"-proxy.header.tls", "value"}, cfg: func(cfg *Config) *Config { cfg.Proxy.TLSHeader = "value" return cfg }, }, { args: []string{"-proxy.header.tls.value", "value"}, cfg: func(cfg *Config) *Config { cfg.Proxy.TLSHeaderValue = "value" return cfg }, }, { args: []string{"-proxy.gzip.contenttype", `^text/.*$`}, cfg: func(cfg *Config) *Config { cfg.Proxy.GZIPContentTypes = regexp.MustCompile(`^text/.*$`) return cfg }, }, { args: []string{"-registry.backend", "value"}, cfg: func(cfg *Config) *Config { cfg.Registry.Backend = "value" return cfg }, }, { args: []string{"-registry.file.path", "value"}, cfg: func(cfg *Config) *Config { cfg.Registry.File.Path = "value" return cfg }, }, { args: []string{"-registry.static.routes", "value"}, cfg: func(cfg *Config) *Config { cfg.Registry.Static.Routes = "value" return cfg }, }, { args: []string{"-registry.consul.addr", "1.2.3.4:5555"}, cfg: func(cfg *Config) *Config { cfg.Registry.Consul.Addr = "1.2.3.4:5555" cfg.Registry.Consul.Scheme = "http" return cfg }, }, { args: []string{"-registry.consul.addr", "http://1.2.3.4:5555/"}, cfg: func(cfg *Config) *Config { cfg.Registry.Consul.Addr = "1.2.3.4:5555" cfg.Registry.Consul.Scheme = "http" return cfg }, }, { args: []string{"-registry.consul.addr", "https://1.2.3.4:5555/"}, cfg: func(cfg *Config) *Config { cfg.Registry.Consul.Addr = "1.2.3.4:5555" cfg.Registry.Consul.Scheme = "https" return cfg }, }, { args: []string{"-registry.consul.addr", "HTTPS://1.2.3.4:5555/"}, cfg: func(cfg *Config) *Config { cfg.Registry.Consul.Addr = "1.2.3.4:5555" cfg.Registry.Consul.Scheme = "https" return cfg }, }, { args: []string{"-registry.consul.token", "some-token"}, cfg: func(cfg *Config) *Config { cfg.Registry.Consul.Token = "some-token" return cfg }, }, { args: []string{"-registry.consul.kvpath", "/some/path"}, cfg: func(cfg *Config) *Config { cfg.Registry.Consul.KVPath = "/some/path" return cfg }, }, { args: []string{"-registry.consul.tagprefix", "p-"}, cfg: func(cfg *Config) *Config { cfg.Registry.Consul.TagPrefix = "p-" return cfg }, }, { args: []string{"-registry.consul.register.enabled=false"}, cfg: func(cfg *Config) *Config { cfg.Registry.Consul.Register = false return cfg }, }, { args: []string{"-registry.consul.register.addr", "1.2.3.4:5555"}, cfg: func(cfg *Config) *Config { cfg.Registry.Consul.ServiceAddr = "1.2.3.4:5555" return cfg }, }, { args: []string{"-registry.consul.register.name", "fab"}, cfg: func(cfg *Config) *Config { cfg.Registry.Consul.ServiceName = "fab" return cfg }, }, { args: []string{"-registry.consul.register.tags", "a, b, c, "}, cfg: func(cfg *Config) *Config { cfg.Registry.Consul.ServiceTags = []string{"a", "b", "c"} return cfg }, }, { args: []string{"-registry.consul.register.checkInterval", "5ms"}, cfg: func(cfg *Config) *Config { cfg.Registry.Consul.CheckInterval = 5 * time.Millisecond return cfg }, }, { args: []string{"-registry.consul.register.checkTimeout", "5ms"}, cfg: func(cfg *Config) *Config { cfg.Registry.Consul.CheckTimeout = 5 * time.Millisecond return cfg }, }, { args: []string{"-registry.consul.service.status", "a, b, "}, cfg: func(cfg *Config) *Config { cfg.Registry.Consul.ServiceStatus = []string{"a", "b"} return cfg }, }, { args: []string{"-metrics.target", "some-target"}, cfg: func(cfg *Config) *Config { cfg.Metrics.Target = "some-target" return cfg }, }, { args: []string{"-metrics.prefix", "some-prefix"}, cfg: func(cfg *Config) *Config { cfg.Metrics.Prefix = "some-prefix" return cfg }, }, { args: []string{"-metrics.names", "some names"}, cfg: func(cfg *Config) *Config { cfg.Metrics.Names = "some names" return cfg }, }, { args: []string{"-metrics.interval", "5ms"}, cfg: func(cfg *Config) *Config { cfg.Metrics.Interval = 5 * time.Millisecond return cfg }, }, { args: []string{"-metrics.graphite.addr", "1.2.3.4:5555"}, cfg: func(cfg *Config) *Config { cfg.Metrics.GraphiteAddr = "1.2.3.4:5555" return cfg }, }, { args: []string{"-metrics.statsd.addr", "1.2.3.4:5555"}, cfg: func(cfg *Config) *Config { cfg.Metrics.StatsDAddr = "1.2.3.4:5555" return cfg }, }, { args: []string{"-metrics.circonus.apiapp", "value"}, cfg: func(cfg *Config) *Config { cfg.Metrics.Circonus.APIApp = "value" return cfg }, }, { args: []string{"-metrics.circonus.apikey", "value"}, cfg: func(cfg *Config) *Config { cfg.Metrics.Circonus.APIKey = "value" return cfg }, }, { args: []string{"-metrics.circonus.apiurl", "value"}, cfg: func(cfg *Config) *Config { cfg.Metrics.Circonus.APIURL = "value" return cfg }, }, { args: []string{"-metrics.circonus.brokerid", "value"}, cfg: func(cfg *Config) *Config { cfg.Metrics.Circonus.BrokerID = "value" return cfg }, }, { args: []string{"-metrics.circonus.checkid", "value"}, cfg: func(cfg *Config) *Config { cfg.Metrics.Circonus.CheckID = "value" return cfg }, }, { args: []string{"-runtime.gogc", "555"}, cfg: func(cfg *Config) *Config { cfg.Runtime.GOGC = 555 return cfg }, }, { args: []string{"-runtime.gomaxprocs", "555"}, cfg: func(cfg *Config) *Config { cfg.Runtime.GOMAXPROCS = 555 return cfg }, }, { args: []string{"-ui.addr", "1.2.3.4:5555"}, cfg: func(cfg *Config) *Config { cfg.UI.Addr = "1.2.3.4:5555" return cfg }, }, { args: []string{"-ui.color", "value"}, cfg: func(cfg *Config) *Config { cfg.UI.Color = "value" return cfg }, }, { args: []string{"-ui.title", "value"}, cfg: func(cfg *Config) *Config { cfg.UI.Title = "value" return cfg }, }, { desc: "ignore aws.apigw.cert.cn", args: []string{"-aws.apigw.cert.cn", "value"}, cfg: func(cfg *Config) *Config { return cfg }, }, // config file { desc: "config from environ", environ: []string{"FABIO_proxy_addr=:6666"}, cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{{Addr: ":6666", Proto: "http"}} return cfg }, }, { desc: "config from url", args: []string{"-cfg", "URL"}, path: "http", data: "proxy.addr = :5555", cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{{Addr: ":5555", Proto: "http"}} return cfg }, }, { desc: "config from file I", args: []string{"-cfg", "/tmp/fabio-config-test"}, path: "/tmp/fabio-config-test", data: "proxy.addr = :5555", cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{{Addr: ":5555", Proto: "http"}} return cfg }, }, { desc: "config from file II", args: []string{"-cfg=/tmp/fabio-config-test"}, path: "/tmp/fabio-config-test", data: "proxy.addr = :5555", cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{{Addr: ":5555", Proto: "http"}} return cfg }, }, { desc: "config from file III", args: []string{"-cfg='/tmp/fabio-config-test'"}, path: "/tmp/fabio-config-test", data: "proxy.addr = :5555", cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{{Addr: ":5555", Proto: "http"}} return cfg }, }, { desc: "config from file IV", args: []string{"-cfg=\"/tmp/fabio-config-test\""}, path: "/tmp/fabio-config-test", data: "proxy.addr = :5555", cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{{Addr: ":5555", Proto: "http"}} return cfg }, }, // precedence rules { desc: "cmdline over config file I", args: []string{"-cfg", "/tmp/fabio-config-test", "-proxy.addr", ":6666"}, path: "/tmp/fabio-config-test", data: "proxy.addr = :5555", cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{{Addr: ":6666", Proto: "http"}} return cfg }, }, { desc: "cmdline over config file II", args: []string{"-proxy.addr", ":6666", "-cfg", "/tmp/fabio-config-test"}, path: "/tmp/fabio-config-test", data: "proxy.addr = :5555", cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{{Addr: ":6666", Proto: "http"}} return cfg }, }, { desc: "environ over config file", args: []string{"-cfg", "/tmp/fabio-config-test"}, environ: []string{"FABIO_proxy_addr=:6666"}, path: "/tmp/fabio-config-test", data: "proxy.addr = :5555", cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{{Addr: ":6666", Proto: "http"}} return cfg }, }, { desc: "cmdline over environ", args: []string{"-proxy.addr", ":5555"}, environ: []string{"FABIO_proxy_addr=:6666"}, cfg: func(cfg *Config) *Config { cfg.Listen = []Listen{{Addr: ":5555", Proto: "http"}} return cfg }, }, // errors { desc: "-proxy.addr with unknown cert source 'foo'", args: []string{"-proxy.addr", ":5555;cs=foo"}, cfg: func(cfg *Config) *Config { return nil }, err: errors.New("unknown certificate source \"foo\""), }, { desc: "-proxy.addr with unknown proto 'foo'", args: []string{"-proxy.addr", ":5555;proto=foo"}, cfg: func(cfg *Config) *Config { return nil }, err: errors.New("unknown protocol \"foo\""), }, { desc: "-proxy.addr with proto 'https' requires cert source", args: []string{"-proxy.addr", ":5555;proto=https"}, cfg: func(cfg *Config) *Config { return nil }, err: errors.New("proto 'https' requires cert source"), }, { desc: "-proxy.addr with cert source and proto 'http' requires proto 'https'", args: []string{"-proxy.addr", ":5555;cs=name;proto=http", "-proxy.cs", "cs=name;type=path;cert=value"}, cfg: func(cfg *Config) *Config { return nil }, err: errors.New("cert source requires proto 'https'"), }, { desc: "-proxy.addr with cert source and proto 'tcp+sni' requires proto 'https'", args: []string{"-proxy.addr", ":5555;cs=name;proto=tcp+sni", "-proxy.cs", "cs=name;type=path;cert=value"}, cfg: func(cfg *Config) *Config { return nil }, err: errors.New("cert source requires proto 'https'"), }, { args: []string{"-cfg"}, cfg: func(cfg *Config) *Config { return nil }, err: errInvalidConfig, }, { args: []string{"-cfg=''"}, cfg: func(cfg *Config) *Config { return nil }, err: errInvalidConfig, }, { args: []string{"-cfg=\"\""}, cfg: func(cfg *Config) *Config { return nil }, err: errInvalidConfig, }, } for _, tt := range tests { tt := tt // capture loop var if tt.desc == "" { tt.desc = strings.Join(tt.args, " ") } t.Run(tt.desc, func(t *testing.T) { // start a web server or write data to a file if tt.path is set switch { case tt.path == "http": srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, tt.data) })) defer srv.Close() // replace 'URL' with the actual server url in the command line args for i := range tt.args { tt.args[i] = strings.Replace(tt.args[i], "URL", srv.URL, -1) } case tt.path != "": if err := ioutil.WriteFile(tt.path, []byte(tt.data), 0600); err != nil { t.Fatalf("error writing file: %s", err) } defer os.Remove(tt.path) } // config parser expects the exe name to be the first argument cfg, err := Load(append([]string{"fabio"}, tt.args...), tt.environ) if got, want := err, tt.err; !reflect.DeepEqual(got, want) { t.Fatalf("got error %v want %v", got, want) } // limit the amount of code we have to write per test: // each test has a function which augments a pre-configured // config structure which is pre-filled with the defaults. clone := new(Config) *clone = *defaultConfig clone.Listen = []Listen{{Addr: ":9999", Proto: "http"}} got, want := cfg, tt.cfg(clone) verify.Values(t, "", got, want) }) } }
func TestAddHeaders(t *testing.T) { tests := []struct { desc string r *http.Request cfg config.Proxy hdrs http.Header err string }{ {"error", &http.Request{RemoteAddr: "1.2.3.4"}, config.Proxy{}, http.Header{}, "cannot parse 1.2.3.4", }, {"http request", &http.Request{RemoteAddr: "1.2.3.4:5555"}, config.Proxy{}, http.Header{ "Forwarded": []string{"for=1.2.3.4; proto=http"}, "X-Forwarded-Proto": []string{"http"}, "X-Forwarded-Port": []string{"80"}, "X-Real-Ip": []string{"1.2.3.4"}, }, "", }, {"https request", &http.Request{RemoteAddr: "1.2.3.4:5555", TLS: &tls.ConnectionState{}}, config.Proxy{}, http.Header{ "Forwarded": []string{"for=1.2.3.4; proto=https"}, "X-Forwarded-Proto": []string{"https"}, "X-Forwarded-Port": []string{"443"}, "X-Real-Ip": []string{"1.2.3.4"}, }, "", }, {"ws request", &http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"Upgrade": {"websocket"}}}, config.Proxy{}, http.Header{ "Forwarded": []string{"for=1.2.3.4; proto=ws"}, "Upgrade": []string{"websocket"}, "X-Forwarded-For": []string{"1.2.3.4"}, "X-Forwarded-Proto": []string{"ws"}, "X-Forwarded-Port": []string{"80"}, "X-Real-Ip": []string{"1.2.3.4"}, }, "", }, {"wss request", &http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"Upgrade": {"websocket"}}, TLS: &tls.ConnectionState{}}, config.Proxy{}, http.Header{ "Forwarded": []string{"for=1.2.3.4; proto=wss"}, "Upgrade": []string{"websocket"}, "X-Forwarded-For": []string{"1.2.3.4"}, "X-Forwarded-Proto": []string{"wss"}, "X-Forwarded-Port": []string{"443"}, "X-Real-Ip": []string{"1.2.3.4"}, }, "", }, {"set client ip header", &http.Request{RemoteAddr: "1.2.3.4:5555"}, config.Proxy{ClientIPHeader: "Client-IP"}, http.Header{ "Client-Ip": []string{"1.2.3.4"}, "Forwarded": []string{"for=1.2.3.4; proto=http"}, "X-Forwarded-Proto": []string{"http"}, "X-Forwarded-Port": []string{"80"}, "X-Real-Ip": []string{"1.2.3.4"}, }, "", }, {"set Forwarded with localIP", &http.Request{RemoteAddr: "1.2.3.4:5555"}, config.Proxy{LocalIP: "5.6.7.8"}, http.Header{ "Forwarded": []string{"for=1.2.3.4; proto=http; by=5.6.7.8"}, "X-Forwarded-Proto": []string{"http"}, "X-Forwarded-Port": []string{"80"}, "X-Real-Ip": []string{"1.2.3.4"}, }, "", }, {"set Forwarded with localIP for https", &http.Request{RemoteAddr: "1.2.3.4:5555", TLS: &tls.ConnectionState{}}, config.Proxy{LocalIP: "5.6.7.8"}, http.Header{ "Forwarded": []string{"for=1.2.3.4; proto=https; by=5.6.7.8"}, "X-Forwarded-Proto": []string{"https"}, "X-Forwarded-Port": []string{"443"}, "X-Real-Ip": []string{"1.2.3.4"}, }, "", }, {"extend Forwarded with localIP", &http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"Forwarded": {"for=9.9.9.9; proto=http; by=8.8.8.8"}}}, config.Proxy{LocalIP: "5.6.7.8"}, http.Header{ "Forwarded": []string{"for=9.9.9.9; proto=http; by=8.8.8.8; by=5.6.7.8"}, "X-Forwarded-Proto": []string{"http"}, "X-Forwarded-Port": []string{"80"}, "X-Real-Ip": []string{"1.2.3.4"}, }, "", }, {"set tls header", &http.Request{RemoteAddr: "1.2.3.4:5555", TLS: &tls.ConnectionState{}}, config.Proxy{TLSHeader: "Secure"}, http.Header{ "Forwarded": []string{"for=1.2.3.4; proto=https"}, "Secure": []string{""}, "X-Forwarded-Proto": []string{"https"}, "X-Forwarded-Port": []string{"443"}, "X-Real-Ip": []string{"1.2.3.4"}, }, "", }, {"set tls header with value", &http.Request{RemoteAddr: "1.2.3.4:5555", TLS: &tls.ConnectionState{}}, config.Proxy{TLSHeader: "Secure", TLSHeaderValue: "true"}, http.Header{ "Forwarded": []string{"for=1.2.3.4; proto=https"}, "Secure": []string{"true"}, "X-Forwarded-Proto": []string{"https"}, "X-Forwarded-Port": []string{"443"}, "X-Real-Ip": []string{"1.2.3.4"}, }, "", }, {"overwrite tls header for https, when set", &http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"Secure": []string{"on"}}, TLS: &tls.ConnectionState{}}, config.Proxy{TLSHeader: "Secure", TLSHeaderValue: "true"}, http.Header{ "Forwarded": []string{"for=1.2.3.4; proto=https"}, "Secure": []string{"true"}, "X-Forwarded-Proto": []string{"https"}, "X-Forwarded-Port": []string{"443"}, "X-Real-Ip": []string{"1.2.3.4"}, }, "", }, {"drop tls header for http, when set", &http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"Secure": []string{"on"}}}, config.Proxy{TLSHeader: "Secure", TLSHeaderValue: "true"}, http.Header{ "Forwarded": []string{"for=1.2.3.4; proto=http"}, "X-Forwarded-Proto": []string{"http"}, "X-Forwarded-Port": []string{"80"}, "X-Real-Ip": []string{"1.2.3.4"}, }, "", }, {"do not overwrite X-Forwarded-Proto, if present", &http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"X-Forwarded-Proto": {"some value"}}}, config.Proxy{}, http.Header{ "Forwarded": []string{"for=1.2.3.4; proto=http"}, "X-Forwarded-Proto": []string{"some value"}, "X-Forwarded-Port": []string{"80"}, "X-Real-Ip": []string{"1.2.3.4"}, }, "", }, {"set X-Forwarded-Port from Host", &http.Request{RemoteAddr: "1.2.3.4:5555", Host: "5.6.7.8:1234"}, config.Proxy{}, http.Header{ "Forwarded": []string{"for=1.2.3.4; proto=http"}, "X-Forwarded-Proto": []string{"http"}, "X-Forwarded-Port": []string{"1234"}, "X-Real-Ip": []string{"1.2.3.4"}, }, "", }, {"set X-Forwarded-Port from Host for https", &http.Request{RemoteAddr: "1.2.3.4:5555", Host: "5.6.7.8:1234", TLS: &tls.ConnectionState{}}, config.Proxy{}, http.Header{ "Forwarded": []string{"for=1.2.3.4; proto=https"}, "X-Forwarded-Proto": []string{"https"}, "X-Forwarded-Port": []string{"1234"}, "X-Real-Ip": []string{"1.2.3.4"}, }, "", }, {"do not overwrite X-Forwarded-Port header, if present", &http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"X-Forwarded-Port": {"4444"}}}, config.Proxy{}, http.Header{ "Forwarded": []string{"for=1.2.3.4; proto=http"}, "X-Forwarded-Proto": []string{"http"}, "X-Forwarded-Port": []string{"4444"}, "X-Real-Ip": []string{"1.2.3.4"}, }, "", }, {"do not overwrite X-Real-Ip, if present", &http.Request{RemoteAddr: "1.2.3.4:5555", Header: http.Header{"X-Real-Ip": {"6.6.6.6"}}}, config.Proxy{}, http.Header{ "Forwarded": []string{"for=1.2.3.4; proto=http"}, "X-Forwarded-Proto": []string{"http"}, "X-Forwarded-Port": []string{"80"}, "X-Real-Ip": []string{"6.6.6.6"}, }, "", }, } for i, tt := range tests { tt := tt // capture loop var t.Run(tt.desc, func(t *testing.T) { if tt.r.Header == nil { tt.r.Header = http.Header{} } err := addHeaders(tt.r, tt.cfg) if err != nil { if got, want := err.Error(), tt.err; got != want { t.Fatalf("%d: %s\ngot %q\nwant %q", i, tt.desc, got, want) } return } if tt.err != "" { t.Fatalf("%d: got nil want %q", i, tt.err) return } got, want := tt.r.Header, tt.hdrs verify.Values(t, "", got, want) }) } }
func TestFromProperties(t *testing.T) { in := ` proxy.cs = cs=name;type=path;cert=foo;clientca=bar;refresh=99s;hdr=a: b;caupgcn=furb proxy.addr = :1234 proxy.localip = 4.4.4.4 proxy.strategy = rr proxy.matcher = prefix proxy.noroutestatus = 929 proxy.shutdownwait = 500ms proxy.responseheadertimeout = 3s proxy.keepalivetimeout = 4s proxy.dialtimeout = 60s proxy.readtimeout = 5s proxy.writetimeout = 10s proxy.maxconn = 666 proxy.header.clientip = clientip proxy.header.tls = tls proxy.header.tls.value = tls-true registry.backend = something registry.file.path = /foo/bar registry.static.routes = route add svc / http://127.0.0.1:6666/ registry.consul.addr = https://1.2.3.4:5678 registry.consul.token = consul-token registry.consul.kvpath = /some/path registry.consul.tagprefix = p- registry.consul.register.enabled = false registry.consul.register.addr = 6.6.6.6:7777 registry.consul.register.name = fab registry.consul.register.tags = a, b, c , registry.consul.register.checkInterval = 5s registry.consul.register.checkTimeout = 10s registry.consul.service.status = a,b metrics.target = graphite metrics.prefix = someprefix metrics.interval = 5s metrics.graphite.addr = 5.6.7.8:9999 runtime.gogc = 666 runtime.gomaxprocs = 12 ui.addr = 7.8.9.0:1234 ui.color = fonzy ui.title = fabfab aws.apigw.cert.cn = furb ` out := &Config{ ListenerValue: []string{":1234"}, CertSourcesValue: []map[string]string{{"cs": "name", "type": "path", "cert": "foo", "clientca": "bar", "refresh": "99s", "hdr": "a: b", "caupgcn": "furb"}}, CertSources: map[string]CertSource{ "name": CertSource{ Name: "name", Type: "path", CertPath: "foo", ClientCAPath: "bar", CAUpgradeCN: "furb", Refresh: 99 * time.Second, Header: http.Header{"A": []string{"b"}}, }, }, Proxy: Proxy{ MaxConn: 666, LocalIP: "4.4.4.4", Strategy: "rr", Matcher: "prefix", NoRouteStatus: 929, ShutdownWait: 500 * time.Millisecond, DialTimeout: 60 * time.Second, ResponseHeaderTimeout: 3 * time.Second, KeepAliveTimeout: 4 * time.Second, ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, ClientIPHeader: "clientip", TLSHeader: "tls", TLSHeaderValue: "tls-true", }, Registry: Registry{ Backend: "something", File: File{ Path: "/foo/bar", }, Static: Static{ Routes: "route add svc / http://127.0.0.1:6666/", }, Consul: Consul{ Addr: "1.2.3.4:5678", Scheme: "https", Token: "consul-token", KVPath: "/some/path", TagPrefix: "p-", Register: false, ServiceAddr: "6.6.6.6:7777", ServiceName: "fab", ServiceTags: []string{"a", "b", "c"}, ServiceStatus: []string{"a", "b"}, CheckInterval: 5 * time.Second, CheckTimeout: 10 * time.Second, }, }, Listen: []Listen{ { Addr: ":1234", Scheme: "http", ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, }, }, Metrics: Metrics{ Target: "graphite", Prefix: "someprefix", Interval: 5 * time.Second, GraphiteAddr: "5.6.7.8:9999", }, Runtime: Runtime{ GOGC: 666, GOMAXPROCS: 12, }, UI: UI{ Addr: "7.8.9.0:1234", Color: "fonzy", Title: "fabfab", }, } p, err := properties.Load([]byte(in), properties.UTF8) if err != nil { t.Fatalf("got %v want nil", err) } cfg, err := load(p) if err != nil { t.Fatalf("got %v want nil", err) } got, want := cfg, out verify.Values(t, "cfg", got, want) }