diff --git a/server/channels/api4/system.go b/server/channels/api4/system.go index 592e9554e6..6e82f467be 100644 --- a/server/channels/api4/system.go +++ b/server/channels/api4/system.go @@ -133,10 +133,13 @@ func generateSupportPacket(c *Context, w http.ResponseWriter, r *http.Request) { } fileBytesReader := bytes.NewReader(fileBytes) - // Send the zip file back to client - // We are able to pass 0 for content size due to the fact that Golang's serveContent (https://golang.org/src/net/http/fs.go) - // already sets that for us - web.WriteFileResponse(outputZipFilename, FileMime, 0, now, *c.App.Config().ServiceSettings.WebserverMode, fileBytesReader, true, w, r) + // Prevent caching so support packets are always fresh + w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") + + err = web.WriteStreamResponse(w, fileBytesReader, outputZipFilename, FileMime, true) + if err != nil { + c.Logger.Warn("Error while writing response", mlog.Err(err)) + } } // supportPacketFileName returns the ZIP file name in the format mm_support_packet_$CUSTOMER_NAME_YYYY-MM-DDTHH-MM.zip. diff --git a/server/channels/api4/system_test.go b/server/channels/api4/system_test.go index 9eff6c9507..7bf56c0fa8 100644 --- a/server/channels/api4/system_test.go +++ b/server/channels/api4/system_test.go @@ -236,7 +236,7 @@ func TestGenerateSupportPacket(t *testing.T) { th.App.Srv().SetLicense(l) th.TestForSystemAdminAndLocal(t, func(t *testing.T, c *model.Client4) { - file, filename, _, err := th.SystemAdminClient.GenerateSupportPacket(context.Background()) + file, filename, resp, err := th.SystemAdminClient.GenerateSupportPacket(context.Background()) require.NoError(t, err) assert.Contains(t, filename, "mm_support_packet_My_awesome_Company_") @@ -244,6 +244,9 @@ func TestGenerateSupportPacket(t *testing.T) { d, err := io.ReadAll(file) require.NoError(t, err) assert.NotZero(t, len(d)) + + // Verify that the Cache-Control header is set to prevent caching + assert.Equal(t, "no-cache, no-store, must-revalidate", resp.Header.Get("Cache-Control")) }) }) diff --git a/server/platform/shared/web/files.go b/server/platform/shared/web/files.go index 4b652401af..4eb8a3fb0c 100644 --- a/server/platform/shared/web/files.go +++ b/server/platform/shared/web/files.go @@ -54,20 +54,23 @@ func WriteFileResponse(filename string, contentType string, contentSize int64, l http.ServeContent(w, r, filename, lastModification, fileReader) } -// WriteStreamResponse copies the ReadCloser `r` to the ResponseWriter `w`. Use this when you need to stream a response +// WriteStreamResponse copies the Reader `r` to the ResponseWriter `w`. Use this when you need to stream a response // to the client that will appear as a file `filename` of type `contentType`. -func WriteStreamResponse(w http.ResponseWriter, r io.ReadCloser, filename string, contentType string, forceDownload bool) error { +func WriteStreamResponse(w http.ResponseWriter, r io.Reader, filename string, contentType string, forceDownload bool) error { setHeaders(w, contentType, forceDownload, filename) if _, err := io.Copy(w, r); err != nil { - return errors.Wrap(err, "error streaming ReadCloser") + return errors.Wrap(err, "error streaming Reader") } return nil } func setHeaders(w http.ResponseWriter, contentType string, forceDownload bool, filename string) { - w.Header().Set("Cache-Control", "private, max-age=86400") + // Only set Cache-Control if it hasn't been set already + if w.Header().Get("Cache-Control") == "" { + w.Header().Set("Cache-Control", "private, max-age=86400") + } w.Header().Set("X-Content-Type-Options", "nosniff") if contentType == "" {