Skip to content

Commit

Permalink
Issue #294: Use upstream hostname
Browse files Browse the repository at this point in the history
This patch adds support for a 'host=dst' option on a route which will
trigger the proxy to use the hostname of the target host for the 
outgoing request instead of the one provided by incoming request.
This allows fabio to act as a reverse proxy for an external site.
  • Loading branch information
mitchelldavis authored and magiconair committed May 31, 2017
1 parent aa1fdd9 commit b14a199
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 0 deletions.
39 changes: 39 additions & 0 deletions proxy/http_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,45 @@ func TestProxyStripsPath(t *testing.T) {
}
}

// TestProxyHost
func TestProxyHost(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, r.Host)
}))

proxy := httptest.NewServer(&HTTPProxy{
Transport: &http.Transport{
Dial: func(network, addr string) (net.Conn, error) {
addr = server.URL[len("http://"):]
return net.Dial(network, addr)
},
},
Lookup: func(r *http.Request) *route.Target {
routes := "route add mock /hostdst http://a.com/ opts \"host=dst\"\n"
routes += "route add mock /hostunknown http://a.com/ opts \"host=garbble\"\n"
routes += "route add mock / http://a.com/"
tbl, _ := route.NewTable(routes)
return tbl.Lookup(r, "", route.Picker["rr"], route.Matcher["prefix"])
},
})
defer proxy.Close()

check := func(uri, host string) {
resp, body := mustGet(proxy.URL + uri)
if got, want := resp.StatusCode, http.StatusOK; got != want {
t.Fatalf("got status %d want %d", got, want)
}
if got, want := string(body), host; got != want {
t.Fatalf("got body %q want %q", got, want)
}
}

proxyHost := proxy.URL[len("http://"):]
t.Run("host eq dst", func(t *testing.T) { check("/hostdst", "a.com") })
t.Run("host is unknown", func(t *testing.T) { check("/hostunknown", proxyHost) })
t.Run("no host", func(t *testing.T) { check("/", proxyHost) })
}

func TestProxyLogOutput(t *testing.T) {
// build a format string from all log fields and one header field
fields := []string{"header.X-Foo:$header.X-Foo"}
Expand Down
4 changes: 4 additions & 0 deletions proxy/http_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ func (p *HTTPProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
targetURL.RawQuery = t.URL.RawQuery + "&" + r.URL.RawQuery
}

if t.Host == "dst" {
r.Host = targetURL.Host
}

// TODO(fs): The HasPrefix check seems redundant since the lookup function should
// TODO(fs): have found the target based on the prefix but there may be other
// TODO(fs): matchers which may have different rules. I'll keep this for
Expand Down
1 change: 1 addition & 0 deletions route/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func (r *Route) addTarget(service string, targetURL *url.URL, fixedWeight float6
if r.Opts != nil {
t.StripPath = r.Opts["strip"]
t.TLSSkipVerify = r.Opts["tlsskipverify"] == "true"
t.Host = r.Opts["host"]
}

r.Targets = append(r.Targets, t)
Expand Down
6 changes: 6 additions & 0 deletions route/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ type Target struct {
// TLS connections.
TLSSkipVerify bool

// Host signifies what the proxy will set the Host header to.
// The proxy does not modify the Host header by default.
// When Host is set to 'dst' the proxy will use the host name
// of the target host for the outgoing request.
Host string

// URL is the endpoint the service instance listens on
URL *url.URL

Expand Down

0 comments on commit b14a199

Please sign in to comment.