mirror of https://github.com/status-im/consul.git
Merge pull request #558 from ceh/http-api-response-headers
add ability to specify response headers on the HTTP API
This commit is contained in:
commit
f86d7c3a09
|
@ -310,6 +310,9 @@ type Config struct {
|
|||
// send with the update check. This is used to deduplicate messages.
|
||||
DisableAnonymousSignature bool `mapstructure:"disable_anonymous_signature"`
|
||||
|
||||
// HTTPAPIResponseHeaders are used to add HTTP header response fields to the HTTP API responses.
|
||||
HTTPAPIResponseHeaders map[string]string `mapstructure:"http_api_response_headers"`
|
||||
|
||||
// AEInterval controls the anti-entropy interval. This is how often
|
||||
// the agent attempts to reconcile it's local state with the server'
|
||||
// representation of our state. Defaults to every 60s.
|
||||
|
@ -859,6 +862,15 @@ func MergeConfig(a, b *Config) *Config {
|
|||
result.DisableAnonymousSignature = true
|
||||
}
|
||||
|
||||
if len(b.HTTPAPIResponseHeaders) != 0 {
|
||||
if result.HTTPAPIResponseHeaders == nil {
|
||||
result.HTTPAPIResponseHeaders = make(map[string]string)
|
||||
}
|
||||
for field, value := range b.HTTPAPIResponseHeaders {
|
||||
result.HTTPAPIResponseHeaders[field] = value
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the start join addresses
|
||||
result.StartJoin = make([]string, 0, len(a.StartJoin)+len(b.StartJoin))
|
||||
result.StartJoin = append(result.StartJoin, a.StartJoin...)
|
||||
|
|
|
@ -589,6 +589,21 @@ func TestDecodeConfig(t *testing.T) {
|
|||
if !config.DisableAnonymousSignature {
|
||||
t.Fatalf("bad: %#v", config)
|
||||
}
|
||||
|
||||
// HTTP API response header fields
|
||||
input = `{"http_api_response_headers": {"Access-Control-Allow-Origin": "*", "X-XSS-Protection": "1; mode=block"}}`
|
||||
config, err = DecodeConfig(bytes.NewReader([]byte(input)))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if config.HTTPAPIResponseHeaders["Access-Control-Allow-Origin"] != "*" {
|
||||
t.Fatalf("bad: %#v", config)
|
||||
}
|
||||
|
||||
if config.HTTPAPIResponseHeaders["X-XSS-Protection"] != "1; mode=block" {
|
||||
t.Fatalf("bad: %#v", config)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeConfig_Services(t *testing.T) {
|
||||
|
@ -960,6 +975,9 @@ func TestMergeConfig(t *testing.T) {
|
|||
StatsdAddr: "127.0.0.1:7251",
|
||||
DisableUpdateCheck: true,
|
||||
DisableAnonymousSignature: true,
|
||||
HTTPAPIResponseHeaders: map[string]string{
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
},
|
||||
}
|
||||
|
||||
c := MergeConfig(a, b)
|
||||
|
|
|
@ -231,6 +231,8 @@ func (s *HTTPServer) registerHandlers(enableDebug bool) {
|
|||
// wrap is used to wrap functions to make them more convenient
|
||||
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)
|
||||
|
||||
// Invoke the handler
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
|
@ -336,6 +338,13 @@ func setMeta(resp http.ResponseWriter, m *structs.QueryMeta) {
|
|||
setKnownLeader(resp, m.KnownLeader)
|
||||
}
|
||||
|
||||
// setHeaders is used to set canonical response header fields
|
||||
func setHeaders(resp http.ResponseWriter, headers map[string]string) {
|
||||
for field, value := range headers {
|
||||
resp.Header().Set(http.CanonicalHeaderKey(field), value)
|
||||
}
|
||||
}
|
||||
|
||||
// parseWait is used to parse the ?wait and ?index query params
|
||||
// Returns true on error
|
||||
func parseWait(resp http.ResponseWriter, req *http.Request, b *structs.QueryOptions) bool {
|
||||
|
|
|
@ -102,6 +102,37 @@ func TestSetMeta(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestHTTPAPIResponseHeaders(t *testing.T) {
|
||||
dir, srv := makeHTTPServer(t)
|
||||
srv.agent.config.HTTPAPIResponseHeaders = map[string]string{
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"X-XSS-Protection": "1; mode=block",
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
origin := resp.Header().Get("Access-Control-Allow-Origin")
|
||||
if origin != "*" {
|
||||
t.Fatalf("bad Access-Control-Allow-Origin: expected %q, got %q", "*", origin)
|
||||
}
|
||||
|
||||
xss := resp.Header().Get("X-XSS-Protection")
|
||||
if xss != "1; mode=block" {
|
||||
t.Fatalf("bad X-XSS-Protection header: expected %q, got %q", "1; mode=block", xss)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContentTypeIsJSON(t *testing.T) {
|
||||
dir, srv := makeHTTPServer(t)
|
||||
|
||||
|
|
|
@ -5,5 +5,8 @@
|
|||
"bootstrap": true,
|
||||
"server": true,
|
||||
"acl_datacenter": "dc1",
|
||||
"acl_master_token": "dev"
|
||||
"acl_master_token": "dev",
|
||||
"http_api_response_headers": {
|
||||
"Access-Control-Allow-Origin": "*"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -172,7 +172,7 @@ being configured using a configuration management system.
|
|||
|
||||
The configuration files are JSON formatted, making them easily readable
|
||||
and editable by both humans and computers. The configuration is formatted
|
||||
at a single JSON object with configuration within it.
|
||||
as a single JSON object with configuration within it.
|
||||
|
||||
Configuration files are used for more than just setting up the agent,
|
||||
they are also used to provide check and service definitions. These are used
|
||||
|
@ -327,6 +327,18 @@ definitions support being updated during a reload.
|
|||
The key is used with the certificate to verify the agents authenticity.
|
||||
Must be provided along with the `cert_file`.
|
||||
|
||||
* `http_api_response_headers` - This object allows adding HTTP header response fields to
|
||||
the HTTP API responses. For example, the following config can be used to enable CORS on
|
||||
the HTTP API endpoints:
|
||||
|
||||
```javascript
|
||||
{
|
||||
"http_api_response_headers": {
|
||||
"Access-Control-Allow-Origin": "*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* `leave_on_terminate` - If enabled, when the agent receives a TERM signal,
|
||||
it will send a Leave message to the rest of the cluster and gracefully
|
||||
leave. Defaults to false.
|
||||
|
|
Loading…
Reference in New Issue