CallResource: don't set Content-Type header if status is 204 (#50780)

Grafana's HTTPServer ensures that the Content-Type header is always set
in the response to a CallResource call, but when the status code is
204 No Content this shouldn't be done; the body should be empty and no
Content-Type header should be set.

We ran into this in the Grafana ML plugin where we were sending an empty
response with status 204, but the frontend client saw that the content
type was JSON and tried to parse it, resulting in an error that made it
to the JS console.
This commit is contained in:
Ben Sully 2022-11-07 16:25:49 +00:00 committed by GitHub
parent 43436bd6f0
commit 480277f612
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 2 deletions

View File

@ -193,7 +193,7 @@ func (hs *HTTPServer) flushStream(stream callResourceClientResponseStream, w htt
// Expected that headers and status are only part of first stream
if processedStreams == 0 && resp.Headers != nil {
// Make sure a content type always is returned in response
if _, exists := resp.Headers["Content-Type"]; !exists {
if _, exists := resp.Headers["Content-Type"]; !exists && resp.Status != http.StatusNoContent {
resp.Headers["Content-Type"] = []string{"application/json"}
}

View File

@ -331,10 +331,35 @@ func TestMakePluginResourceRequest(t *testing.T) {
}
}
require.Equal(t, resp.Header().Get("Content-Type"), "application/json")
require.Equal(t, "sandbox", resp.Header().Get("Content-Security-Policy"))
require.Empty(t, req.Header.Get(customHeader))
}
func TestMakePluginResourceRequestContentTypeEmpty(t *testing.T) {
pluginClient := &fakePluginClient{
statusCode: http.StatusNoContent,
}
hs := HTTPServer{
Cfg: setting.NewCfg(),
log: log.New(),
pluginClient: pluginClient,
}
req := httptest.NewRequest(http.MethodGet, "/", nil)
resp := httptest.NewRecorder()
pCtx := backend.PluginContext{}
err := hs.makePluginResourceRequest(resp, req, pCtx)
require.NoError(t, err)
for {
if resp.Flushed {
break
}
}
require.Zero(t, resp.Header().Get("Content-Type"))
}
func callGetPluginAsset(sc *scenarioContext) {
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
}
@ -366,6 +391,8 @@ type fakePluginClient struct {
req *backend.CallResourceRequest
backend.QueryDataHandlerFunc
statusCode int
}
func (c *fakePluginClient) CallResource(_ context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
@ -377,8 +404,13 @@ func (c *fakePluginClient) CallResource(_ context.Context, req *backend.CallReso
return err
}
statusCode := http.StatusOK
if c.statusCode != 0 {
statusCode = c.statusCode
}
return sender.Send(&backend.CallResourceResponse{
Status: http.StatusOK,
Status: statusCode,
Headers: make(map[string][]string),
Body: bytes,
})