Exemplo n.º 1
0
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)
	}
}
Exemplo n.º 2
0
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)
}
Exemplo n.º 3
0
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)
	}
}
Exemplo n.º 4
0
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)
		})
	}
}
Exemplo n.º 5
0
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)
		})
	}
}
Exemplo n.º 6
0
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)
}