From 8d7136fc066e0c67c417e23ea1ada82578c2ae3a Mon Sep 17 00:00:00 2001 From: Pedro Nasser Date: Thu, 14 Jul 2016 15:30:00 -0300 Subject: [PATCH 1/3] fix transparent host header #916 --- caddyhttp/proxy/upstream.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/caddyhttp/proxy/upstream.go b/caddyhttp/proxy/upstream.go index 64f8c5513..228e5d84a 100644 --- a/caddyhttp/proxy/upstream.go +++ b/caddyhttp/proxy/upstream.go @@ -69,6 +69,19 @@ func NewStaticUpstreams(c caddyfile.Dispenser) ([]Upstream, error) { to = append(to, parsed...) } + if len(to) == 0 { + return upstreams, c.ArgErr() + } + + upstream.Hosts = make([]*UpstreamHost, len(to)) + for i, host := range to { + uh, err := upstream.NewHost(host) + if err != nil { + return upstreams, err + } + upstream.Hosts[i] = uh + } + for c.NextBlock() { switch c.Val() { case "upstream": @@ -87,19 +100,6 @@ func NewStaticUpstreams(c caddyfile.Dispenser) ([]Upstream, error) { } } - if len(to) == 0 { - return upstreams, c.ArgErr() - } - - upstream.Hosts = make([]*UpstreamHost, len(to)) - for i, host := range to { - uh, err := upstream.NewHost(host) - if err != nil { - return upstreams, err - } - upstream.Hosts[i] = uh - } - if upstream.HealthCheck.Path != "" { upstream.HealthCheck.Client = http.Client{ Timeout: upstream.HealthCheck.Timeout, @@ -287,7 +287,7 @@ func parseBlock(c *caddyfile.Dispenser, u *staticUpstream) error { } u.downstreamHeaders.Add(header, value) case "transparent": - u.upstreamHeaders.Add("Host", "{host}") + u.upstreamHeaders.Add("Host", u.Hosts[0].Name) u.upstreamHeaders.Add("X-Real-IP", "{remote}") u.upstreamHeaders.Add("X-Forwarded-For", "{remote}") u.upstreamHeaders.Add("X-Forwarded-Proto", "{scheme}") From b58872925ad9cd5570ec0c59fab42c3f296834c8 Mon Sep 17 00:00:00 2001 From: Pedro Nasser Date: Thu, 14 Jul 2016 15:43:06 -0300 Subject: [PATCH 2/3] fixed transparent host and added test case --- caddyhttp/proxy/upstream.go | 34 +++++++++++++++++++------------- caddyhttp/proxy/upstream_test.go | 4 ++++ 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/caddyhttp/proxy/upstream.go b/caddyhttp/proxy/upstream.go index 228e5d84a..a2f75fa88 100644 --- a/caddyhttp/proxy/upstream.go +++ b/caddyhttp/proxy/upstream.go @@ -26,6 +26,7 @@ type staticUpstream struct { Hosts HostPool Policy Policy insecureSkipVerify bool + transparent bool FailTimeout time.Duration MaxFails int32 @@ -54,6 +55,7 @@ func NewStaticUpstreams(c caddyfile.Dispenser) ([]Upstream, error) { FailTimeout: 10 * time.Second, MaxFails: 1, MaxConns: 0, + transparent: false, } if !c.Args(&upstream.from) { @@ -69,19 +71,6 @@ func NewStaticUpstreams(c caddyfile.Dispenser) ([]Upstream, error) { to = append(to, parsed...) } - if len(to) == 0 { - return upstreams, c.ArgErr() - } - - upstream.Hosts = make([]*UpstreamHost, len(to)) - for i, host := range to { - uh, err := upstream.NewHost(host) - if err != nil { - return upstreams, err - } - upstream.Hosts[i] = uh - } - for c.NextBlock() { switch c.Val() { case "upstream": @@ -100,6 +89,23 @@ func NewStaticUpstreams(c caddyfile.Dispenser) ([]Upstream, error) { } } + if len(to) == 0 { + return upstreams, c.ArgErr() + } + + upstream.Hosts = make([]*UpstreamHost, len(to)) + for i, host := range to { + uh, err := upstream.NewHost(host) + if err != nil { + return upstreams, err + } + upstream.Hosts[i] = uh + } + + if upstream.transparent { + upstream.upstreamHeaders.Add("Host", upstream.Hosts[0].Name) + } + if upstream.HealthCheck.Path != "" { upstream.HealthCheck.Client = http.Client{ Timeout: upstream.HealthCheck.Timeout, @@ -287,7 +293,7 @@ func parseBlock(c *caddyfile.Dispenser, u *staticUpstream) error { } u.downstreamHeaders.Add(header, value) case "transparent": - u.upstreamHeaders.Add("Host", u.Hosts[0].Name) + u.transparent = true u.upstreamHeaders.Add("X-Real-IP", "{remote}") u.upstreamHeaders.Add("X-Forwarded-For", "{remote}") u.upstreamHeaders.Add("X-Forwarded-Proto", "{scheme}") diff --git a/caddyhttp/proxy/upstream_test.go b/caddyhttp/proxy/upstream_test.go index 4fb990f6d..02e2c605e 100644 --- a/caddyhttp/proxy/upstream_test.go +++ b/caddyhttp/proxy/upstream_test.go @@ -213,6 +213,10 @@ func TestParseBlock(t *testing.T) { t.Errorf("Test %d: Could not find the Host header", i+1) } + if v, _ := headers["Host"]; v[0] != upstream.Select().Name { + t.Errorf("Test %d: Host not match first hostname in upstream", i+1) + } + if _, ok := headers["X-Real-Ip"]; !ok { t.Errorf("Test %d: Could not find the X-Real-Ip header", i+1) } From 6bb1e0c674bb8844571f4ae309a6a77d85cf7b57 Mon Sep 17 00:00:00 2001 From: Pedro Nasser Date: Thu, 14 Jul 2016 18:16:58 -0300 Subject: [PATCH 3/3] improve transparent mode --- caddyhttp/proxy/proxy.go | 6 ++++++ caddyhttp/proxy/proxy_test.go | 8 ++++++++ caddyhttp/proxy/upstream.go | 9 +++++---- caddyhttp/proxy/upstream_test.go | 8 ++------ 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/caddyhttp/proxy/proxy.go b/caddyhttp/proxy/proxy.go index c4cd19606..68810c05b 100644 --- a/caddyhttp/proxy/proxy.go +++ b/caddyhttp/proxy/proxy.go @@ -30,6 +30,8 @@ type Upstream interface { Select() *UpstreamHost // Checks if subpath is not an ignored path AllowedPath(string) bool + // Is Upstream in transparent mode? + Transparent() bool } // UpstreamHostDownFunc can be used to customize how Down behaves. @@ -94,6 +96,7 @@ func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { start := time.Now() for time.Now().Sub(start) < tryDuration { host := upstream.Select() + if host == nil { return http.StatusBadGateway, errUnreachable } @@ -125,6 +128,9 @@ func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { if v, ok := host.UpstreamHeaders["Host"]; ok { outreq.Host = replacer.Replace(v[len(v)-1]) } + if upstream.Transparent() { + host.UpstreamHeaders.Set("Host", host.Name) + } // modify headers for request that will be sent to the upstream host upHeaders := createHeadersByRules(host.UpstreamHeaders, r.Header, replacer) for k, v := range upHeaders { diff --git a/caddyhttp/proxy/proxy_test.go b/caddyhttp/proxy/proxy_test.go index 62c4118c5..e13a3d465 100644 --- a/caddyhttp/proxy/proxy_test.go +++ b/caddyhttp/proxy/proxy_test.go @@ -687,6 +687,10 @@ func (u *fakeUpstream) AllowedPath(requestPath string) bool { return true } +func (u *fakeUpstream) Transparent() bool { + return true +} + // newWebSocketTestProxy returns a test proxy that will // redirect to the specified backendAddr. The function // also sets up the rules/environment for testing WebSocket @@ -729,6 +733,10 @@ func (u *fakeWsUpstream) AllowedPath(requestPath string) bool { return true } +func (u *fakeWsUpstream) Transparent() bool { + return true +} + // recorderHijacker is a ResponseRecorder that can // be hijacked. type recorderHijacker struct { diff --git a/caddyhttp/proxy/upstream.go b/caddyhttp/proxy/upstream.go index a2f75fa88..67ca09573 100644 --- a/caddyhttp/proxy/upstream.go +++ b/caddyhttp/proxy/upstream.go @@ -102,10 +102,6 @@ func NewStaticUpstreams(c caddyfile.Dispenser) ([]Upstream, error) { upstream.Hosts[i] = uh } - if upstream.transparent { - upstream.upstreamHeaders.Add("Host", upstream.Hosts[0].Name) - } - if upstream.HealthCheck.Path != "" { upstream.HealthCheck.Client = http.Client{ Timeout: upstream.HealthCheck.Timeout, @@ -372,6 +368,11 @@ func (u *staticUpstream) Select() *UpstreamHost { return u.Policy.Select(pool) } +// Transparent returns true if upstream in transparent mode +func (u *staticUpstream) Transparent() bool { + return u.transparent +} + func (u *staticUpstream) AllowedPath(requestPath string) bool { for _, ignoredSubPath := range u.IgnoredSubPaths { if httpserver.Path(path.Clean(requestPath)).Matches(path.Join(u.From(), ignoredSubPath)) { diff --git a/caddyhttp/proxy/upstream_test.go b/caddyhttp/proxy/upstream_test.go index 02e2c605e..5431962e6 100644 --- a/caddyhttp/proxy/upstream_test.go +++ b/caddyhttp/proxy/upstream_test.go @@ -209,12 +209,8 @@ func TestParseBlock(t *testing.T) { for _, upstream := range upstreams { headers := upstream.Select().UpstreamHeaders - if _, ok := headers["Host"]; !ok { - t.Errorf("Test %d: Could not find the Host header", i+1) - } - - if v, _ := headers["Host"]; v[0] != upstream.Select().Name { - t.Errorf("Test %d: Host not match first hostname in upstream", i+1) + if !upstream.Transparent() { + t.Errorf("Test %d: Upstream should be in transparent mode", i+1) } if _, ok := headers["X-Real-Ip"]; !ok {