From 14178c56bb0fbc8b0b5820539e7681a7defeff47 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 23 Jul 2022 08:38:03 +0200
Subject: [PATCH] Add Cache-Control header to html and api responses, add
 no-transform (#20432)

`no-transform` allegedly disables CloudFlare auto-minify and we did not
set caching headers on html or api requests, which seems good to have
regardless.

Transformation is still allowed for asset requests.

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Andrew Thornton <art27@cantab.net>
---
 modules/context/api.go         |  2 ++
 modules/context/context.go     |  2 ++
 modules/httpcache/httpcache.go | 17 ++++++++++++-----
 routers/install/routes.go      |  2 ++
 routers/web/base.go            |  1 +
 5 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/modules/context/api.go b/modules/context/api.go
index 558a9f51ee..b9d130e2a8 100644
--- a/modules/context/api.go
+++ b/modules/context/api.go
@@ -16,6 +16,7 @@ import (
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/modules/cache"
 	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/httpcache"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/web/middleware"
@@ -268,6 +269,7 @@ func APIContexter() func(http.Handler) http.Handler {
 				}
 			}
 
+			httpcache.AddCacheControlToHeader(ctx.Resp.Header(), 0, "no-transform")
 			ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
 
 			ctx.Data["Context"] = &ctx
diff --git a/modules/context/context.go b/modules/context/context.go
index 68f8a1b408..8824911619 100644
--- a/modules/context/context.go
+++ b/modules/context/context.go
@@ -28,6 +28,7 @@ import (
 	"code.gitea.io/gitea/modules/base"
 	mc "code.gitea.io/gitea/modules/cache"
 	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/httpcache"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
@@ -767,6 +768,7 @@ func Contexter() func(next http.Handler) http.Handler {
 				}
 			}
 
+			httpcache.AddCacheControlToHeader(ctx.Resp.Header(), 0, "no-transform")
 			ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
 
 			ctx.Data["CsrfToken"] = ctx.csrf.GetToken()
diff --git a/modules/httpcache/httpcache.go b/modules/httpcache/httpcache.go
index 5797e981cf..750233d4a7 100644
--- a/modules/httpcache/httpcache.go
+++ b/modules/httpcache/httpcache.go
@@ -17,16 +17,23 @@ import (
 )
 
 // AddCacheControlToHeader adds suitable cache-control headers to response
-func AddCacheControlToHeader(h http.Header, d time.Duration) {
+func AddCacheControlToHeader(h http.Header, maxAge time.Duration, additionalDirectives ...string) {
+	directives := make([]string, 0, 2+len(additionalDirectives))
+
 	if setting.IsProd {
-		h.Set("Cache-Control", "private, max-age="+strconv.Itoa(int(d.Seconds())))
+		if maxAge == 0 {
+			directives = append(directives, "no-store")
+		} else {
+			directives = append(directives, "private", "max-age="+strconv.Itoa(int(maxAge.Seconds())))
+		}
 	} else {
-		h.Set("Cache-Control", "no-store")
+		directives = append(directives, "no-store")
+
 		// to remind users they are using non-prod setting.
-		// some users may be confused by "Cache-Control: no-store" in their setup if they did wrong to `RUN_MODE` in `app.ini`.
 		h.Add("X-Gitea-Debug", "RUN_MODE="+setting.RunMode)
-		h.Add("X-Gitea-Debug", "CacheControl=no-store")
 	}
+
+	h.Set("Cache-Control", strings.Join(append(directives, additionalDirectives...), ", "))
 }
 
 // generateETag generates an ETag based on size, filename and file modification time
diff --git a/routers/install/routes.go b/routers/install/routes.go
index 32829ede9e..fdabcb9dc2 100644
--- a/routers/install/routes.go
+++ b/routers/install/routes.go
@@ -9,6 +9,7 @@ import (
 	"net/http"
 	"path"
 
+	"code.gitea.io/gitea/modules/httpcache"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/public"
 	"code.gitea.io/gitea/modules/setting"
@@ -62,6 +63,7 @@ func installRecovery() func(next http.Handler) http.Handler {
 						"SignedUserName": "",
 					}
 
+					httpcache.AddCacheControlToHeader(w.Header(), 0, "no-transform")
 					w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
 
 					if !setting.IsProd {
diff --git a/routers/web/base.go b/routers/web/base.go
index c7ade55a61..30a24a1275 100644
--- a/routers/web/base.go
+++ b/routers/web/base.go
@@ -158,6 +158,7 @@ func Recovery() func(next http.Handler) http.Handler {
 						store["SignedUserName"] = ""
 					}
 
+					httpcache.AddCacheControlToHeader(w.Header(), 0, "no-transform")
 					w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
 
 					if !setting.IsProd {