From 1bdbf9d6baa02d8b9ca8ad8480cf400a2c5dfe40 Mon Sep 17 00:00:00 2001 From: Marc Guasch Date: Fri, 3 Jun 2016 21:34:31 +0200 Subject: [PATCH 1/4] Add parseUpstream method --- middleware/proxy/upstream.go | 71 +++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/middleware/proxy/upstream.go b/middleware/proxy/upstream.go index e28db643..a1d9fcfc 100644 --- a/middleware/proxy/upstream.go +++ b/middleware/proxy/upstream.go @@ -1,6 +1,7 @@ package proxy import ( + "fmt" "io" "io/ioutil" "net/http" @@ -56,17 +57,38 @@ func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) { if !c.Args(&upstream.from) { return upstreams, c.ArgErr() } - to := c.RemainingArgs() - if len(to) == 0 { - return upstreams, c.ArgErr() + + var to []string + for _, t := range c.RemainingArgs() { + parsed, err := parseUpstream(t) + if err != nil { + return upstreams, err + } + to = append(to, parsed...) } for c.NextBlock() { - if err := parseBlock(&c, upstream); err != nil { - return upstreams, err + switch c.Val() { + case "upstream": + if !c.NextArg() { + return upstreams, c.ArgErr() + } + parsed, err := parseUpstream(c.Val()) + if err != nil { + return upstreams, err + } + to = append(to, parsed...) + default: + if err := parseBlock(&c, upstream); err != nil { + return upstreams, err + } } } + if len(to) == 0 { + return upstreams, c.ArgErr() + } + upstream.Hosts = make([]*UpstreamHost, len(to)) for i, host := range to { uh, err := upstream.NewHost(host) @@ -134,6 +156,45 @@ func (u *staticUpstream) NewHost(host string) (*UpstreamHost, error) { return uh, nil } +func parseUpstream(u string) ([]string, error) { + if !strings.HasPrefix(u, "unix:") { + colonIdx := strings.LastIndex(u, ":") + protoIdx := strings.Index(u, "://") + + if colonIdx != -1 && colonIdx != protoIdx { + us := u[:colonIdx] + ports := u[len(us)+1:] + if separators := strings.Count(ports, "-"); separators > 1 { + return nil, fmt.Errorf("port range [%s] is invalid", ports) + } else if separators == 1 { + portsStr := strings.Split(ports, "-") + pIni, err := strconv.Atoi(portsStr[0]) + if err != nil { + return nil, err + } + + pEnd, err := strconv.Atoi(portsStr[1]) + if err != nil { + return nil, err + } + + if pEnd <= pIni { + return nil, fmt.Errorf("port range [%s] is invalid", ports) + } + + hosts := []string{} + for p := pIni; p <= pEnd; p++ { + hosts = append(hosts, fmt.Sprintf("%s:%d", us, p)) + } + return hosts, nil + } + } + } + + return []string{u}, nil + +} + func parseBlock(c *parse.Dispenser, u *staticUpstream) error { switch c.Val() { case "policy": From 881da313dd4c401591739eb115e174b51e072e28 Mon Sep 17 00:00:00 2001 From: Marc Guasch Date: Fri, 3 Jun 2016 21:34:54 +0200 Subject: [PATCH 2/4] Add tests for upstream directive --- caddy/setup/proxy_test.go | 122 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 caddy/setup/proxy_test.go diff --git a/caddy/setup/proxy_test.go b/caddy/setup/proxy_test.go new file mode 100644 index 00000000..c689a507 --- /dev/null +++ b/caddy/setup/proxy_test.go @@ -0,0 +1,122 @@ +package setup + +import ( + "reflect" + "testing" + + "github.com/mholt/caddy/middleware/proxy" +) + +func TestUpstream(t *testing.T) { + for i, test := range []struct { + input string + shouldErr bool + expectedHosts map[string]struct{} + }{ + // test #0 test usual to destination still works normally + { + "proxy / localhost:80", + false, + map[string]struct{}{ + "http://localhost:80": struct{}{}, + }, + }, + + // test #1 test usual to destination with port range + { + "proxy / localhost:8080-8082", + false, + map[string]struct{}{ + "http://localhost:8080": struct{}{}, + "http://localhost:8081": struct{}{}, + "http://localhost:8082": struct{}{}, + }, + }, + + // test #2 test upstream directive + { + "proxy / {\n upstream localhost:8080\n}", + false, + map[string]struct{}{ + "http://localhost:8080": struct{}{}, + }, + }, + + // test #3 test upstream directive with port range + { + "proxy / {\n upstream localhost:8080-8081\n}", + false, + map[string]struct{}{ + "http://localhost:8080": struct{}{}, + "http://localhost:8081": struct{}{}, + }, + }, + + // test #4 test to destination with upstream directive + { + "proxy / localhost:8080 {\n upstream localhost:8081-8082\n}", + false, + map[string]struct{}{ + "http://localhost:8080": struct{}{}, + "http://localhost:8081": struct{}{}, + "http://localhost:8082": struct{}{}, + }, + }, + + // test #5 test with unix sockets + { + "proxy / localhost:8080 {\n upstream unix:/var/foo\n}", + false, + map[string]struct{}{ + "http://localhost:8080": struct{}{}, + "unix:/var/foo": struct{}{}, + }, + }, + + // test #6 test fail on malformed port range + { + "proxy / localhost:8090-8080", + true, + nil, + }, + + // test #7 test fail on malformed port range 2 + { + "proxy / {\n upstream localhost:80-A\n}", + true, + nil, + }, + + // test #8 test upstreams without ports work correctly + { + "proxy / http://localhost {\n upstream testendpoint\n}", + false, + map[string]struct{}{ + "http://localhost": struct{}{}, + "http://testendpoint": struct{}{}, + }, + }, + } { + receivedFunc, err := Proxy(NewTestController(test.input)) + if err != nil && !test.shouldErr { + t.Errorf("Test case #%d received an error of %v", i, err) + } else if test.shouldErr { + continue + } + + upstreams := receivedFunc(nil).(proxy.Proxy).Upstreams + for _, upstream := range upstreams { + val := reflect.ValueOf(upstream).Elem() + hosts := val.FieldByName("Hosts").Interface().(proxy.HostPool) + if len(hosts) != len(test.expectedHosts) { + t.Errorf("Test case #%d expected %d hosts but received %d", i, len(test.expectedHosts), len(hosts)) + } else { + for _, host := range hosts { + if _, found := test.expectedHosts[host.Name]; !found { + t.Errorf("Test case #%d has an unexpected host %s", i, host.Name) + } + } + } + } + } +} From d291b7672159528723c7402d9bef4c3c4579b2b8 Mon Sep 17 00:00:00 2001 From: Marc Guasch Date: Fri, 3 Jun 2016 22:48:24 +0200 Subject: [PATCH 3/4] Gofmt fixes --- caddy/setup/proxy_test.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/caddy/setup/proxy_test.go b/caddy/setup/proxy_test.go index c689a507..1a1043b2 100644 --- a/caddy/setup/proxy_test.go +++ b/caddy/setup/proxy_test.go @@ -18,7 +18,7 @@ func TestUpstream(t *testing.T) { "proxy / localhost:80", false, map[string]struct{}{ - "http://localhost:80": struct{}{}, + "http://localhost:80": {}, }, }, @@ -27,9 +27,9 @@ func TestUpstream(t *testing.T) { "proxy / localhost:8080-8082", false, map[string]struct{}{ - "http://localhost:8080": struct{}{}, - "http://localhost:8081": struct{}{}, - "http://localhost:8082": struct{}{}, + "http://localhost:8080": {}, + "http://localhost:8081": {}, + "http://localhost:8082": {}, }, }, @@ -38,7 +38,7 @@ func TestUpstream(t *testing.T) { "proxy / {\n upstream localhost:8080\n}", false, map[string]struct{}{ - "http://localhost:8080": struct{}{}, + "http://localhost:8080": {}, }, }, @@ -47,8 +47,8 @@ func TestUpstream(t *testing.T) { "proxy / {\n upstream localhost:8080-8081\n}", false, map[string]struct{}{ - "http://localhost:8080": struct{}{}, - "http://localhost:8081": struct{}{}, + "http://localhost:8080": {}, + "http://localhost:8081": {}, }, }, @@ -57,9 +57,9 @@ func TestUpstream(t *testing.T) { "proxy / localhost:8080 {\n upstream localhost:8081-8082\n}", false, map[string]struct{}{ - "http://localhost:8080": struct{}{}, - "http://localhost:8081": struct{}{}, - "http://localhost:8082": struct{}{}, + "http://localhost:8080": {}, + "http://localhost:8081": {}, + "http://localhost:8082": {}, }, }, @@ -68,8 +68,8 @@ func TestUpstream(t *testing.T) { "proxy / localhost:8080 {\n upstream unix:/var/foo\n}", false, map[string]struct{}{ - "http://localhost:8080": struct{}{}, - "unix:/var/foo": struct{}{}, + "http://localhost:8080": {}, + "unix:/var/foo": {}, }, }, @@ -92,8 +92,8 @@ func TestUpstream(t *testing.T) { "proxy / http://localhost {\n upstream testendpoint\n}", false, map[string]struct{}{ - "http://localhost": struct{}{}, - "http://testendpoint": struct{}{}, + "http://localhost": {}, + "http://testendpoint": {}, }, }, } { From 9b5ad487d7b54e947568da81cd36d81810de21b7 Mon Sep 17 00:00:00 2001 From: Marc Guasch Date: Sat, 4 Jun 2016 01:07:53 +0200 Subject: [PATCH 4/4] Add several upstreams test case --- caddy/setup/proxy_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/caddy/setup/proxy_test.go b/caddy/setup/proxy_test.go index 1a1043b2..3d6d04a0 100644 --- a/caddy/setup/proxy_test.go +++ b/caddy/setup/proxy_test.go @@ -96,6 +96,20 @@ func TestUpstream(t *testing.T) { "http://testendpoint": {}, }, }, + + // test #9 test several upstream directives + { + "proxy / localhost:8080 {\n upstream localhost:8081-8082\n upstream localhost:8083-8085\n}", + false, + map[string]struct{}{ + "http://localhost:8080": {}, + "http://localhost:8081": {}, + "http://localhost:8082": {}, + "http://localhost:8083": {}, + "http://localhost:8084": {}, + "http://localhost:8085": {}, + }, + }, } { receivedFunc, err := Proxy(NewTestController(test.input)) if err != nil && !test.shouldErr {