From 9f7a973acef57b9789572d21a009e30bd1f61159 Mon Sep 17 00:00:00 2001 From: James Phillips Date: Tue, 16 Aug 2016 11:31:41 -0700 Subject: [PATCH] Adds an `X-Consul-Translate-Addresses` to signal translation is enabled. --- api/api.go | 12 +++++ api/api_test.go | 4 ++ command/agent/http.go | 10 +++++ command/agent/http_test.go | 45 +++++++++++++++++++ website/source/docs/agent/http.html.markdown | 9 ++++ .../source/docs/agent/options.html.markdown | 11 +++-- 6 files changed, 88 insertions(+), 3 deletions(-) diff --git a/api/api.go b/api/api.go index b0fe6d83b7..dd811fde4b 100644 --- a/api/api.go +++ b/api/api.go @@ -80,6 +80,9 @@ type QueryMeta struct { // How long did the request take RequestTime time.Duration + + // Is address translation enabled for HTTP responses on this agent + AddressTranslationEnabled bool } // WriteMeta is used to return meta data about a write @@ -542,6 +545,15 @@ func parseQueryMeta(resp *http.Response, q *QueryMeta) error { default: q.KnownLeader = false } + + // Parse X-Consul-Translate-Addresses + switch header.Get("X-Consul-Translate-Addresses") { + case "true": + q.AddressTranslationEnabled = true + default: + q.AddressTranslationEnabled = false + } + return nil } diff --git a/api/api_test.go b/api/api_test.go index 71f01c483a..b00101bd31 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -306,6 +306,7 @@ func TestParseQueryMeta(t *testing.T) { resp.Header.Set("X-Consul-Index", "12345") resp.Header.Set("X-Consul-LastContact", "80") resp.Header.Set("X-Consul-KnownLeader", "true") + resp.Header.Set("X-Consul-Translate-Addresses", "true") qm := &QueryMeta{} if err := parseQueryMeta(resp, qm); err != nil { @@ -321,6 +322,9 @@ func TestParseQueryMeta(t *testing.T) { if !qm.KnownLeader { t.Fatalf("Bad: %v", qm) } + if !qm.AddressTranslationEnabled { + t.Fatalf("Bad: %v", qm) + } } func TestAPI_UnixSocket(t *testing.T) { diff --git a/command/agent/http.go b/command/agent/http.go index cd36204544..5d7dcce7c6 100644 --- a/command/agent/http.go +++ b/command/agent/http.go @@ -329,6 +329,7 @@ func (s *HTTPServer) registerHandlers(enableDebug bool) { func (s *HTTPServer) wrap(handler func(resp http.ResponseWriter, req *http.Request) (interface{}, error)) func(resp http.ResponseWriter, req *http.Request) { f := func(resp http.ResponseWriter, req *http.Request) { setHeaders(resp, s.agent.config.HTTPAPIResponseHeaders) + setTranslateAddr(resp, s.agent.config.TranslateWanAddrs) // Obfuscate any tokens from appearing in the logs formVals, err := url.ParseQuery(req.URL.RawQuery) @@ -373,6 +374,7 @@ func (s *HTTPServer) wrap(handler func(resp http.ResponseWriter, req *http.Reque if strings.Contains(errMsg, "Permission denied") || strings.Contains(errMsg, "ACL not found") { code = http.StatusForbidden // 403 } + resp.WriteHeader(code) resp.Write([]byte(err.Error())) return @@ -452,6 +454,14 @@ func decodeBody(req *http.Request, out interface{}, cb func(interface{}) error) return mapstructure.Decode(raw, out) } +// setTranslateAddr is used to set the address translation header. This is only +// present if the feature is active. +func setTranslateAddr(resp http.ResponseWriter, active bool) { + if active { + resp.Header().Set("X-Consul-Translate-Addresses", "true") + } +} + // setIndex is used to set the index response header func setIndex(resp http.ResponseWriter, index uint64) { resp.Header().Set("X-Consul-Index", strconv.FormatUint(index, 10)) diff --git a/command/agent/http_test.go b/command/agent/http_test.go index b6618977f3..7cc80cb240 100644 --- a/command/agent/http_test.go +++ b/command/agent/http_test.go @@ -223,6 +223,51 @@ func TestSetMeta(t *testing.T) { } } +func TestHTTPAPI_TranslateAddrHeader(t *testing.T) { + // Header should not be present if address translation is off. + { + dir, srv := makeHTTPServer(t) + defer os.RemoveAll(dir) + defer srv.Shutdown() + defer srv.agent.Shutdown() + + resp := httptest.NewRecorder() + handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) { + return nil, nil + } + + req, _ := http.NewRequest("GET", "/v1/agent/self", nil) + srv.wrap(handler)(resp, req) + + translate := resp.Header().Get("X-Consul-Translate-Addresses") + if translate != "" { + t.Fatalf("bad: expected %q, got %q", "", translate) + } + } + + // Header should be set to true if it's turned on. + { + dir, srv := makeHTTPServer(t) + srv.agent.config.TranslateWanAddrs = true + defer os.RemoveAll(dir) + defer srv.Shutdown() + defer srv.agent.Shutdown() + + resp := httptest.NewRecorder() + handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) { + return nil, nil + } + + req, _ := http.NewRequest("GET", "/v1/agent/self", nil) + srv.wrap(handler)(resp, req) + + translate := resp.Header().Get("X-Consul-Translate-Addresses") + if translate != "true" { + t.Fatalf("bad: expected %q, got %q", "true", translate) + } + } +} + func TestHTTPAPIResponseHeaders(t *testing.T) { dir, srv := makeHTTPServer(t) srv.agent.config.HTTPAPIResponseHeaders = map[string]string{ diff --git a/website/source/docs/agent/http.html.markdown b/website/source/docs/agent/http.html.markdown index 6ea2b03f01..0f226ed846 100644 --- a/website/source/docs/agent/http.html.markdown +++ b/website/source/docs/agent/http.html.markdown @@ -96,3 +96,12 @@ configuration option. However, the token can also be specified per-request by using the `X-Consul-Token` request header or the `token` querystring parameter. The request header takes precedence over the default token, and the querystring parameter takes precedence over everything. + + +## Translated Addresses + +Consul 0.7 added the ability to translate addresses in HTTP response based on the configuration +setting for [`translate_wan_addrs`](/docs/agent/options.html#translate_wan_addrs). In order to +allow clients to know if address translation is in effect, the `X-Consul-Translate-Addresses` +header will be added if translation is enabled, and will have a value of `true`. If translation +is not enabled then this header will not be present. diff --git a/website/source/docs/agent/options.html.markdown b/website/source/docs/agent/options.html.markdown index 506a361ff3..4409cbf711 100644 --- a/website/source/docs/agent/options.html.markdown +++ b/website/source/docs/agent/options.html.markdown @@ -756,9 +756,14 @@ Consul will not enable TLS for the HTTP API unless the `https` port has been ass default.

- Starting in Consul 0.7 and later, node addresses in responses to the following HTTP endpoints will - prefer a node's configured WAN address when querying for a node in a - remote datacenter: + Starting in Consul 0.7 and later, node addresses in responses to HTTP requests will also prefer a + node's configured WAN address when querying for a node in a remote + datacenter. An [`X-Consul-Translate-Addresses`](/docs/agent/http.html#translate_header) header + will be present on all responses when translation is enabled to help clients know that the addresses + may be translated. The `TaggedAddresses` field in responses also have a `lan` address for clients that + need knowledge of that address, regardless of translation. +
+
The following endpoints translate addresses:
* [`/v1/catalog/nodes`](/docs/agent/http/catalog.html#catalog_nodes) * [`/v1/catalog/node/`](/docs/agent/http/catalog.html#catalog_node)