diff --git a/workhorse/internal/artifacts/entry.go b/workhorse/internal/artifacts/entry.go index e727e82ec5f5735306c7c935209b49297d022476..0c697d400204d0a9015ee1e9c850811e7a3fddab 100644 --- a/workhorse/internal/artifacts/entry.go +++ b/workhorse/internal/artifacts/entry.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "io" + "mime" "net/http" "os" "os/exec" @@ -52,6 +53,14 @@ func (e *entry) Inject(w http.ResponseWriter, r *http.Request, sendData string) } } +func detectFileContentType(fileName string) string { + contentType := mime.TypeByExtension(filepath.Ext(fileName)) + if contentType == "" { + contentType = "application/octet-stream" + } + return contentType +} + func unpackFileFromZip(ctx context.Context, archivePath, encodedFilename string, headers http.Header, output io.Writer) error { fileName, err := zipartifacts.DecodeFileEntry(encodedFilename) if err != nil { @@ -88,15 +97,7 @@ func unpackFileFromZip(ctx context.Context, archivePath, encodedFilename string, // Write http headers about the file headers.Set("Content-Length", contentLength) - - // Using application/octet-stream tells the client that we don't - // really know what Content-Type is. Since this file is being sent - // as attachment, browsers don't need to know to save the - // file. Chrome doesn't appear to pay attention to Content-Type when - // Content-Disposition is an attachment, and Firefox only uses it if there - // is no extension in the filename. Thus, there's no need for - // Workhorse to guess Content-Type based on the filename. - headers.Set("Content-Type", "application/octet-stream") + headers.Set("Content-Type", detectFileContentType(fileName)) headers.Set("Content-Disposition", "attachment; filename=\""+escapeQuotes(basename)+"\"") // Copy file body to client if _, err := io.Copy(output, reader); err != nil { diff --git a/workhorse/internal/artifacts/entry_test.go b/workhorse/internal/artifacts/entry_test.go index 35e2bf06a0c89732e292e6465ca2c48d385a19b0..6f1e9d360aae3cd01d9d941cd85722ff5eafcac5 100644 --- a/workhorse/internal/artifacts/entry_test.go +++ b/workhorse/internal/artifacts/entry_test.go @@ -54,7 +54,7 @@ func TestDownloadingFromValidArchive(t *testing.T) { testhelper.RequireResponseHeader(t, response, "Content-Type", - "application/octet-stream") + "text/plain; charset=utf-8") testhelper.RequireResponseHeader(t, response, "Content-Disposition", "attachment; filename=\"test.txt\"") @@ -88,7 +88,7 @@ func TestDownloadingFromValidHTTPArchive(t *testing.T) { testhelper.RequireResponseHeader(t, response, "Content-Type", - "application/octet-stream") + "text/plain; charset=utf-8") testhelper.RequireResponseHeader(t, response, "Content-Disposition", "attachment; filename=\"test.txt\"")