mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Plugins: Automatically forward plugin request HTTP headers in outgoing HTTP requests (#60417)
Automatically forward core plugin request HTTP headers in outgoing HTTP requests. Core datasource plugin authors don't have to specifically handle forwarding of HTTP headers, e.g. do not have to "hardcode" the header-names in the datasource plugin, if not having custom needs. Fixes #57065
This commit is contained in:
committed by
GitHub
parent
aaab477594
commit
c35c689a96
@@ -1,27 +0,0 @@
|
||||
package httpclientprovider
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
)
|
||||
|
||||
const DeleteHeadersMiddlewareName = "delete-headers"
|
||||
|
||||
// DeleteHeadersMiddleware middleware that delete headers on the outgoing
|
||||
// request if header names provided.
|
||||
func DeleteHeadersMiddleware(headerNames ...string) httpclient.Middleware {
|
||||
return httpclient.NamedMiddlewareFunc(DeleteHeadersMiddlewareName, func(opts httpclient.Options, next http.RoundTripper) http.RoundTripper {
|
||||
if len(headerNames) == 0 {
|
||||
return next
|
||||
}
|
||||
|
||||
return httpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
|
||||
for _, k := range headerNames {
|
||||
req.Header.Del(k)
|
||||
}
|
||||
|
||||
return next.RoundTrip(req)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package httpclientprovider
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDeleteHeadersMiddleware(t *testing.T) {
|
||||
t.Run("Without headerNames should return next http.RoundTripper", func(t *testing.T) {
|
||||
ctx := &testContext{}
|
||||
finalRoundTripper := ctx.createRoundTripper("finalrt")
|
||||
var headerNames []string
|
||||
mw := DeleteHeadersMiddleware(headerNames...)
|
||||
rt := mw.CreateMiddleware(httpclient.Options{}, finalRoundTripper)
|
||||
require.NotNil(t, rt)
|
||||
middlewareName, ok := mw.(httpclient.MiddlewareName)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, DeleteHeadersMiddlewareName, middlewareName.MiddlewareName())
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://", nil)
|
||||
require.NoError(t, err)
|
||||
res, err := rt.RoundTrip(req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
if res.Body != nil {
|
||||
require.NoError(t, res.Body.Close())
|
||||
}
|
||||
require.Len(t, ctx.callChain, 1)
|
||||
require.ElementsMatch(t, []string{"finalrt"}, ctx.callChain)
|
||||
})
|
||||
|
||||
t.Run("With headers set should apply HTTP headers to the request", func(t *testing.T) {
|
||||
ctx := &testContext{}
|
||||
finalRoundTripper := ctx.createRoundTripper("final")
|
||||
headerNames := []string{"X-Header-B", "X-Header-C"}
|
||||
mw := DeleteHeadersMiddleware(headerNames...)
|
||||
rt := mw.CreateMiddleware(httpclient.Options{}, finalRoundTripper)
|
||||
require.NotNil(t, rt)
|
||||
middlewareName, ok := mw.(httpclient.MiddlewareName)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, DeleteHeadersMiddlewareName, middlewareName.MiddlewareName())
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://", nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("X-Header-A", "a")
|
||||
req.Header.Set("X-Header-B", "b")
|
||||
req.Header.Set("X-Header-C", "c")
|
||||
req.Header.Set("X-Header-D", "d")
|
||||
res, err := rt.RoundTrip(req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
if res.Body != nil {
|
||||
require.NoError(t, res.Body.Close())
|
||||
}
|
||||
require.Len(t, ctx.callChain, 1)
|
||||
require.ElementsMatch(t, []string{"final"}, ctx.callChain)
|
||||
|
||||
require.Equal(t, "a", req.Header.Get("X-Header-A"))
|
||||
require.Empty(t, req.Header.Get("X-Header-B"))
|
||||
require.Empty(t, req.Header.Get("X-Header-C"))
|
||||
require.Equal(t, "d", req.Header.Get("X-Header-D"))
|
||||
})
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package httpclientprovider
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
)
|
||||
|
||||
const SetHeadersMiddlewareName = "set-headers"
|
||||
|
||||
// SetHeadersMiddleware middleware that sets headers on the outgoing
|
||||
// request if headers provided.
|
||||
// If the request already contains any of the headers provided, they
|
||||
// will be overwritten.
|
||||
func SetHeadersMiddleware(headers http.Header) httpclient.Middleware {
|
||||
return httpclient.NamedMiddlewareFunc(SetHeadersMiddlewareName, func(opts httpclient.Options, next http.RoundTripper) http.RoundTripper {
|
||||
if len(headers) == 0 {
|
||||
return next
|
||||
}
|
||||
|
||||
return httpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
|
||||
for k, v := range headers {
|
||||
canonicalKey := textproto.CanonicalMIMEHeaderKey(k)
|
||||
req.Header[canonicalKey] = v
|
||||
}
|
||||
|
||||
return next.RoundTrip(req)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package httpclientprovider
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSetHeadersMiddleware(t *testing.T) {
|
||||
t.Run("Without headers set should return next http.RoundTripper", func(t *testing.T) {
|
||||
ctx := &testContext{}
|
||||
finalRoundTripper := ctx.createRoundTripper("finalrt")
|
||||
var headers http.Header
|
||||
mw := SetHeadersMiddleware(headers)
|
||||
rt := mw.CreateMiddleware(httpclient.Options{}, finalRoundTripper)
|
||||
require.NotNil(t, rt)
|
||||
middlewareName, ok := mw.(httpclient.MiddlewareName)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, SetHeadersMiddlewareName, middlewareName.MiddlewareName())
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://", nil)
|
||||
require.NoError(t, err)
|
||||
res, err := rt.RoundTrip(req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
if res.Body != nil {
|
||||
require.NoError(t, res.Body.Close())
|
||||
}
|
||||
require.Len(t, ctx.callChain, 1)
|
||||
require.ElementsMatch(t, []string{"finalrt"}, ctx.callChain)
|
||||
})
|
||||
|
||||
t.Run("With headers set should apply HTTP headers to the request", func(t *testing.T) {
|
||||
ctx := &testContext{}
|
||||
finalRoundTripper := ctx.createRoundTripper("final")
|
||||
headers := http.Header{
|
||||
"X-Header-A": []string{"value a"},
|
||||
"X-Header-B": []string{"value b"},
|
||||
"X-HEader-C": []string{"value c"},
|
||||
}
|
||||
mw := SetHeadersMiddleware(headers)
|
||||
rt := mw.CreateMiddleware(httpclient.Options{}, finalRoundTripper)
|
||||
require.NotNil(t, rt)
|
||||
middlewareName, ok := mw.(httpclient.MiddlewareName)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, SetHeadersMiddlewareName, middlewareName.MiddlewareName())
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://", nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("X-Header-B", "d")
|
||||
res, err := rt.RoundTrip(req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
if res.Body != nil {
|
||||
require.NoError(t, res.Body.Close())
|
||||
}
|
||||
require.Len(t, ctx.callChain, 1)
|
||||
require.ElementsMatch(t, []string{"final"}, ctx.callChain)
|
||||
|
||||
require.Equal(t, "value a", req.Header.Get("X-Header-A"))
|
||||
require.Equal(t, "value b", req.Header.Get("X-Header-B"))
|
||||
require.Equal(t, "value c", req.Header.Get("X-Header-C"))
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user