Changes
4 changed files (+61/-20)
-
-
@@ -0,0 +1,41 @@// Copyright 2022 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package public import "strings" // wellKnownMimeTypesLower comes from Golang's builtin mime package: `builtinTypesLower`, see the comment of detectWellKnownMimeType var wellKnownMimeTypesLower = map[string]string{ ".avif": "image/avif", ".css": "text/css; charset=utf-8", ".gif": "image/gif", ".htm": "text/html; charset=utf-8", ".html": "text/html; charset=utf-8", ".jpeg": "image/jpeg", ".jpg": "image/jpeg", ".js": "text/javascript; charset=utf-8", ".json": "application/json", ".mjs": "text/javascript; charset=utf-8", ".pdf": "application/pdf", ".png": "image/png", ".svg": "image/svg+xml", ".wasm": "application/wasm", ".webp": "image/webp", ".xml": "text/xml; charset=utf-8", // well, there are some types missing from the builtin list ".txt": "text/plain; charset=utf-8", } // detectWellKnownMimeType will return the mime-type for a well-known file ext name // The purpose of this function is to bypass the unstable behavior of Golang's mime.TypeByExtension // mime.TypeByExtension would use OS's mime-type config to overwrite the well-known types (see its document). // If the user's OS has incorrect mime-type config, it would make Gitea can not respond a correct Content-Type to browsers. // For example, if Gitea returns `text/plain` for a `.js` file, the browser couldn't run the JS due to security reasons. // detectWellKnownMimeType makes the Content-Type for well-known files stable. func detectWellKnownMimeType(ext string) string { ext = strings.ToLower(ext) return wellKnownMimeTypesLower[ext] }
-
-
-
@@ -92,6 +92,15 @@ func parseAcceptEncoding(val string) map[string]bool {return types } // setWellKnownContentType will set the Content-Type if the file is a well-known type. // See the comments of detectWellKnownMimeType func setWellKnownContentType(w http.ResponseWriter, file string) { mimeType := detectWellKnownMimeType(filepath.Ext(file)) if mimeType != "" { w.Header().Set("Content-Type", mimeType) } } func (opts *Options) handle(w http.ResponseWriter, req *http.Request, fs http.FileSystem, file string) bool { // use clean to keep the file is a valid path with no . or .. f, err := fs.Open(path.Clean(file))
-
@@ -122,6 +131,8 @@ func (opts *Options) handle(w http.ResponseWriter, req *http.Request, fs http.Fireturn true } setWellKnownContentType(w, file) serveContent(w, req, fi, fi.ModTime(), f) return true }
-
-
-
-
@@ -9,15 +9,12 @@ package publicimport ( "bytes" "compress/gzip" "io" "mime" "net/http" "os" "path/filepath" "time" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" )
-
@@ -66,24 +63,16 @@ func serveContent(w http.ResponseWriter, req *http.Request, fi os.FileInfo, modtencodings := parseAcceptEncoding(req.Header.Get("Accept-Encoding")) if encodings["gzip"] { if cf, ok := fi.(*vfsgen۰CompressedFileInfo); ok { rd := bytes.NewReader(cf.GzipBytes()) w.Header().Set("Content-Encoding", "gzip") ctype := mime.TypeByExtension(filepath.Ext(fi.Name())) if ctype == "" { // read a chunk to decide between utf-8 text and binary var buf [512]byte grd, _ := gzip.NewReader(rd) n, _ := io.ReadFull(grd, buf[:]) ctype = http.DetectContentType(buf[:n]) _, err := rd.Seek(0, io.SeekStart) // rewind to output whole file if err != nil { log.Error("rd.Seek error: %v", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } rdGzip := bytes.NewReader(cf.GzipBytes()) // all static files are managed by Gitea, so we can make sure every file has the correct ext name // then we can get the correct Content-Type, we do not need to do http.DetectContentType on the decompressed data mimeType := detectWellKnownMimeType(filepath.Ext(fi.Name())) if mimeType == "" { mimeType = "application/octet-stream" } w.Header().Set("Content-Type", ctype) http.ServeContent(w, req, fi.Name(), modtime, rd) w.Header().Set("Content-Type", mimeType) w.Header().Set("Content-Encoding", "gzip") http.ServeContent(w, req, fi.Name(), modtime, rdGzip) return } }
-