From 4bf45c65f3c8028ef91370a1e55d0981666ae9d9 Mon Sep 17 00:00:00 2001 From: Bogdan Drutu Date: Fri, 26 Jun 2020 12:43:14 -0700 Subject: [PATCH] Allow cors in HTTPServerSettings Signed-off-by: Bogdan Drutu --- config/confighttp/confighttp.go | 12 ++++++ config/confighttp/confighttp_test.go | 58 ++++++++++++++++++++++++++++ receiver/otlpreceiver/otlp_test.go | 18 +++------ 3 files changed, 75 insertions(+), 13 deletions(-) diff --git a/config/confighttp/confighttp.go b/config/confighttp/confighttp.go index 90b05c1accc..84c7f642532 100644 --- a/config/confighttp/confighttp.go +++ b/config/confighttp/confighttp.go @@ -20,6 +20,8 @@ import ( "net/http" "time" + "github.com/rs/cors" + "go.opentelemetry.io/collector/config/configtls" ) @@ -55,6 +57,12 @@ type HTTPServerSettings struct { // TLSSetting struct exposes TLS client configuration. TLSSetting *configtls.TLSServerSetting `mapstructure:"tls_settings, omitempty"` + + // CorsOrigins are the allowed CORS origins for HTTP/JSON requests to grpc-gateway adapter + // for the OTLP receiver. See github.com/rs/cors + // An empty list means that CORS is not enabled at all. A wildcard (*) can be + // used to match any origin or one or more characters of an origin. + CorsOrigins []string `mapstructure:"cors_allowed_origins"` } func (hss *HTTPServerSettings) ToListener() (net.Listener, error) { @@ -75,6 +83,10 @@ func (hss *HTTPServerSettings) ToListener() (net.Listener, error) { } func (hss *HTTPServerSettings) ToServer(handler http.Handler) *http.Server { + if len(hss.CorsOrigins) > 0 { + co := cors.Options{AllowedOrigins: hss.CorsOrigins} + handler = cors.New(co).Handler(handler) + } return &http.Server{ Handler: handler, } diff --git a/config/confighttp/confighttp_test.go b/config/confighttp/confighttp_test.go index aa9c137aba9..bad666cc30f 100644 --- a/config/confighttp/confighttp_test.go +++ b/config/confighttp/confighttp_test.go @@ -20,6 +20,7 @@ import ( "net/http" "path" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -259,6 +260,63 @@ func TestHttpReception(t *testing.T) { } } +func TestHttpCors(t *testing.T) { + hss := &HTTPServerSettings{ + Endpoint: "localhost:0", + CorsOrigins: []string{"allowed-*.com"}, + } + + ln, err := hss.ToListener() + assert.NoError(t, err) + s := hss.ToServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + go func() { + _ = s.Serve(ln) + }() + + // TODO: make starting server deterministic + // Wait for the servers to start + <-time.After(10 * time.Millisecond) + + url := fmt.Sprintf("http://%s", ln.Addr().String()) + + // Verify allowed domain gets responses that allow CORS. + verifyCorsResp(t, url, "allowed-origin.com", 200, true) + + // Verify disallowed domain gets responses that disallow CORS. + verifyCorsResp(t, url, "disallowed-origin.com", 200, false) + + require.NoError(t, s.Close()) +} + +func verifyCorsResp(t *testing.T, url string, origin string, wantStatus int, wantAllowed bool) { + req, err := http.NewRequest("OPTIONS", url, nil) + require.NoError(t, err, "Error creating trace OPTIONS request: %v", err) + req.Header.Set("Origin", origin) + req.Header.Set("Access-Control-Request-Method", "POST") + + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err, "Error sending OPTIONS to http server: %v", err) + + err = resp.Body.Close() + if err != nil { + t.Errorf("Error closing OPTIONS response body, %v", err) + } + + assert.Equal(t, wantStatus, resp.StatusCode) + + gotAllowOrigin := resp.Header.Get("Access-Control-Allow-Origin") + gotAllowMethods := resp.Header.Get("Access-Control-Allow-Methods") + + wantAllowOrigin := "" + wantAllowMethods := "" + if wantAllowed { + wantAllowOrigin = origin + wantAllowMethods = "POST" + } + assert.Equal(t, wantAllowOrigin, gotAllowOrigin) + assert.Equal(t, wantAllowMethods, gotAllowMethods) +} + func ExampleHTTPServerSettings() { settings := HTTPServerSettings{ Endpoint: ":443", diff --git a/receiver/otlpreceiver/otlp_test.go b/receiver/otlpreceiver/otlp_test.go index b47cdbbeba3..ea1d39915bb 100644 --- a/receiver/otlpreceiver/otlp_test.go +++ b/receiver/otlpreceiver/otlp_test.go @@ -396,18 +396,15 @@ func verifyCorsResp(t *testing.T, url string, origin string, wantStatus int, wan req.Header.Set("Origin", origin) req.Header.Set("Access-Control-Request-Method", "POST") - client := &http.Client{} - resp, err := client.Do(req) - require.NoError(t, err, "Error sending OPTIONS to grpc-gateway server: %v", err) + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err, "Error sending OPTIONS to http server: %v", err) err = resp.Body.Close() if err != nil { t.Errorf("Error closing OPTIONS response body, %v", err) } - if resp.StatusCode != wantStatus { - t.Errorf("Unexpected status from OPTIONS: %v", resp.StatusCode) - } + assert.Equal(t, wantStatus, resp.StatusCode) gotAllowOrigin := resp.Header.Get("Access-Control-Allow-Origin") gotAllowMethods := resp.Header.Get("Access-Control-Allow-Methods") @@ -418,13 +415,8 @@ func verifyCorsResp(t *testing.T, url string, origin string, wantStatus int, wan wantAllowOrigin = origin wantAllowMethods = "POST" } - - if gotAllowOrigin != wantAllowOrigin { - t.Errorf("Unexpected Access-Control-Allow-Origin: %v", gotAllowOrigin) - } - if gotAllowMethods != wantAllowMethods { - t.Errorf("Unexpected Access-Control-Allow-Methods: %v", gotAllowMethods) - } + assert.Equal(t, wantAllowOrigin, gotAllowOrigin) + assert.Equal(t, wantAllowMethods, gotAllowMethods) } func TestStopWithoutStartNeverCrashes(t *testing.T) {