package pages import ( "mime" "net/http" "strconv" "strings" "time" "git.polynom.me/rio/internal/constants" "git.polynom.me/rio/internal/repo" "github.com/patrickmn/go-cache" log "github.com/sirupsen/logrus" ) var ( pageCache = cache.New(6*time.Hour, 1*time.Hour) ) type PageContentCache struct { Content []byte mimeType string RequestedAt time.Time } func makePageContentCacheEntry(username, path string) string { return username + ":" + path } func addHeaders(csp, contentType string, contentLength int, w http.ResponseWriter) { // Always set a content type if strings.Trim(contentType, " ") == "" { w.Header().Set("Content-Type", "application/octet-stream") } else { w.Header().Set("Content-Type", contentType) } w.Header().Set("X-Content-Type-Options", "nosniff") w.Header().Set("Strict-Transport-Security", "max-age=31536000") w.Header().Set("Content-Length", strconv.Itoa(contentLength)) if csp != "" { w.Header().Set("Content-Security-Policy", csp) } } func ServeFile(username, reponame, path, defaultCsp string, giteaClient *repo.GiteaClient, w http.ResponseWriter) { // Strip away a starting / as it messes with Gitea if path[:1] == "/" { path = path[1:] } key := makePageContentCacheEntry(username, path) entry, found := pageCache.Get(key) var content []byte var mimeType string var err error var since *time.Time = nil if found { log.Debugf("Returning %s from cache", path) content = entry.(PageContentCache).Content mimeType = entry.(PageContentCache).mimeType sinceRaw := entry.(PageContentCache).RequestedAt since = &sinceRaw } content, changed, err := giteaClient.GetFile( username, reponame, constants.PagesBranch, path, since, ) csp := repo.GetCSPForRepository(username, reponame, "", giteaClient) if err != nil { if !found { log.Errorf("Failed to get file %s/%s/%s (%s)", username, reponame, path, err) addHeaders(csp, "text/html", 0, w) w.WriteHeader(404) } else { log.Debugf("Request failed but page %s is cached in memory", path) addHeaders(csp, mimeType, len(content), w) w.WriteHeader(200) w.Write(content) } return } if found && !changed { log.Debugf("Page %s is unchanged and cached in memory", path) addHeaders(csp, mimeType, len(content), w) w.WriteHeader(200) w.Write(content) return } pathParts := strings.Split(path, ".") ext := pathParts[len(pathParts)-1] mimeType = mime.TypeByExtension("." + ext) now := time.Now() pageCache.Set( key, PageContentCache{ content, mimeType, now, }, cache.DefaultExpiration, ) log.Debugf("Page %s requested from Gitea and cached in memory at %v", path, now) addHeaders(csp, mimeType, len(content), w) w.WriteHeader(200) w.Write(content) }