From 6573d5c119d81e68adc49b865bd9dc39445ca369 Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Fri, 18 Sep 2015 11:03:15 -0700 Subject: [PATCH] Add http.host parameter This allows the administrator to specify an externally-reachable URL for the registry. It takes precedence over the X-Forwarded-Proto and X-Forwarded-Host headers, and the hostname in the request. Signed-off-by: Aaron Lehmann --- docs/api/v2/urls_test.go | 40 ++++++++++++++++++++++++++++++++++------ docs/handlers/app.go | 27 ++++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/docs/api/v2/urls_test.go b/docs/api/v2/urls_test.go index 1113a7dd..61d41547 100644 --- a/docs/api/v2/urls_test.go +++ b/docs/api/v2/urls_test.go @@ -158,8 +158,9 @@ func TestBuilderFromRequest(t *testing.T) { forwardedHostHeader2.Set("X-Forwarded-Host", "first.example.com, proxy1.example.com") testRequests := []struct { - request *http.Request - base string + request *http.Request + base string + configHost url.URL }{ { request: &http.Request{URL: u, Host: u.Host}, @@ -177,10 +178,23 @@ func TestBuilderFromRequest(t *testing.T) { request: &http.Request{URL: u, Host: u.Host, Header: forwardedHostHeader2}, base: "http://first.example.com", }, + { + request: &http.Request{URL: u, Host: u.Host, Header: forwardedHostHeader2}, + base: "https://third.example.com:5000", + configHost: url.URL{ + Scheme: "https", + Host: "third.example.com:5000", + }, + }, } for _, tr := range testRequests { - builder := NewURLBuilderFromRequest(tr.request) + var builder *URLBuilder + if tr.configHost.Scheme != "" && tr.configHost.Host != "" { + builder = NewURLBuilder(&tr.configHost) + } else { + builder = NewURLBuilderFromRequest(tr.request) + } for _, testCase := range makeURLBuilderTestCases(builder) { url, err := testCase.build() @@ -207,8 +221,9 @@ func TestBuilderFromRequestWithPrefix(t *testing.T) { forwardedProtoHeader.Set("X-Forwarded-Proto", "https") testRequests := []struct { - request *http.Request - base string + request *http.Request + base string + configHost url.URL }{ { request: &http.Request{URL: u, Host: u.Host}, @@ -218,10 +233,23 @@ func TestBuilderFromRequestWithPrefix(t *testing.T) { request: &http.Request{URL: u, Host: u.Host, Header: forwardedProtoHeader}, base: "https://example.com/prefix/", }, + { + request: &http.Request{URL: u, Host: u.Host, Header: forwardedProtoHeader}, + base: "https://subdomain.example.com/prefix/", + configHost: url.URL{ + Scheme: "https", + Host: "subdomain.example.com/prefix", + }, + }, } for _, tr := range testRequests { - builder := NewURLBuilderFromRequest(tr.request) + var builder *URLBuilder + if tr.configHost.Scheme != "" && tr.configHost.Host != "" { + builder = NewURLBuilder(&tr.configHost) + } else { + builder = NewURLBuilderFromRequest(tr.request) + } for _, testCase := range makeURLBuilderTestCases(builder) { url, err := testCase.build() diff --git a/docs/handlers/app.go b/docs/handlers/app.go index 5103c5fb..f2f6ad9d 100644 --- a/docs/handlers/app.go +++ b/docs/handlers/app.go @@ -7,6 +7,7 @@ import ( "math/rand" "net" "net/http" + "net/url" "os" "time" @@ -54,6 +55,10 @@ type App struct { registry distribution.Namespace // registry is the primary registry backend for the app instance. accessController auth.AccessController // main access controller for application + // httpHost is a parsed representation of the http.host parameter from + // the configuration. Only the Scheme and Host fields are used. + httpHost url.URL + // events contains notification related configuration. events struct { sink notifications.Sink @@ -120,6 +125,14 @@ func NewApp(ctx context.Context, configuration *configuration.Configuration) *Ap app.configureRedis(configuration) app.configureLogHook(configuration) + if configuration.HTTP.Host != "" { + u, err := url.Parse(configuration.HTTP.Host) + if err != nil { + panic(fmt.Sprintf(`could not parse http "host" parameter: %v`, err)) + } + app.httpHost = *u + } + options := []storage.RegistryOption{} if app.isCache { @@ -639,9 +652,17 @@ func (app *App) context(w http.ResponseWriter, r *http.Request) *Context { "vars.uuid")) context := &Context{ - App: app, - Context: ctx, - urlBuilder: v2.NewURLBuilderFromRequest(r), + App: app, + Context: ctx, + } + + if app.httpHost.Scheme != "" && app.httpHost.Host != "" { + // A "host" item in the configuration takes precedence over + // X-Forwarded-Proto and X-Forwarded-Host headers, and the + // hostname in the request. + context.urlBuilder = v2.NewURLBuilder(&app.httpHost) + } else { + context.urlBuilder = v2.NewURLBuilderFromRequest(r) } return context