summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorwxiaoguang <wxiaoguang@gmail.com>2023-04-30 20:22:23 +0800
committerGitHub <noreply@github.com>2023-04-30 08:22:23 -0400
commite3750370df3be1413b1526668cbee60dc2a39f03 (patch)
treecc61431d00e62df1a8be5e2f050105749ddad5e8 /modules
parent8f4dafcd4e6b0b5d307c3e060ffe908c2a96f047 (diff)
downloadgitea-e3750370df3be1413b1526668cbee60dc2a39f03.tar.gz
gitea-e3750370df3be1413b1526668cbee60dc2a39f03.zip
Use globally shared HTMLRender (#24436)
The old `HTMLRender` is not ideal. 1. It shouldn't be initialized multiple times, it consumes a lot of memory and is slow. 2. It shouldn't depend on short-lived requests, the `WatchLocalChanges` needs a long-running context. 3. It doesn't make sense to use FuncsMap slice. HTMLRender was designed to only work for GItea's specialized 400+ templates, so it's good to make it a global shared instance.
Diffstat (limited to 'modules')
-rw-r--r--modules/context/context.go2
-rw-r--r--modules/context/package.go2
-rw-r--r--modules/templates/helper.go10
-rw-r--r--modules/templates/htmlrenderer.go37
-rw-r--r--modules/templates/mailer.go7
5 files changed, 30 insertions, 28 deletions
diff --git a/modules/context/context.go b/modules/context/context.go
index 702da8a965..cd7fcebe55 100644
--- a/modules/context/context.go
+++ b/modules/context/context.go
@@ -677,7 +677,7 @@ func getCsrfOpts() CsrfOptions {
// Contexter initializes a classic context for a request.
func Contexter(ctx context.Context) func(next http.Handler) http.Handler {
- _, rnd := templates.HTMLRenderer(ctx)
+ rnd := templates.HTMLRenderer()
csrfOpts := getCsrfOpts()
if !setting.IsProd {
CsrfTokenRegenerationInterval = 5 * time.Second // in dev, re-generate the tokens more aggressively for debug purpose
diff --git a/modules/context/package.go b/modules/context/package.go
index 2a0159eb5c..6c418b3164 100644
--- a/modules/context/package.go
+++ b/modules/context/package.go
@@ -131,7 +131,7 @@ func determineAccessMode(ctx *Context) (perm.AccessMode, error) {
// PackageContexter initializes a package context for a request.
func PackageContexter(ctx gocontext.Context) func(next http.Handler) http.Handler {
- _, rnd := templates.HTMLRenderer(ctx)
+ rnd := templates.HTMLRenderer()
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
ctx := Context{
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 20261eb959..4abd94d46e 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -10,7 +10,6 @@ import (
"html"
"html/template"
"net/url"
- "regexp"
"strings"
"time"
@@ -26,12 +25,9 @@ import (
"code.gitea.io/gitea/services/gitdiff"
)
-// Used from static.go && dynamic.go
-var mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}[\s]*$`)
-
// NewFuncMap returns functions for injecting to templates
-func NewFuncMap() []template.FuncMap {
- return []template.FuncMap{map[string]interface{}{
+func NewFuncMap() template.FuncMap {
+ return map[string]interface{}{
"DumpVar": dumpVar,
// -----------------------------------------------------------------
@@ -192,7 +188,7 @@ func NewFuncMap() []template.FuncMap {
"FilenameIsImage": FilenameIsImage,
"TabSizeClass": TabSizeClass,
- }}
+ }
}
// Safe render raw as HTML
diff --git a/modules/templates/htmlrenderer.go b/modules/templates/htmlrenderer.go
index 2cecef5f84..d60be88727 100644
--- a/modules/templates/htmlrenderer.go
+++ b/modules/templates/htmlrenderer.go
@@ -6,7 +6,6 @@ package templates
import (
"bufio"
"bytes"
- "context"
"errors"
"fmt"
"io"
@@ -15,24 +14,29 @@ import (
"regexp"
"strconv"
"strings"
+ "sync"
"sync/atomic"
texttemplate "text/template"
"code.gitea.io/gitea/modules/assetfs"
+ "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates/scopedtmpl"
"code.gitea.io/gitea/modules/util"
)
-var rendererKey interface{} = "templatesHtmlRenderer"
-
type TemplateExecutor scopedtmpl.TemplateExecutor
type HTMLRender struct {
templates atomic.Pointer[scopedtmpl.ScopedTemplate]
}
+var (
+ htmlRender *HTMLRender
+ htmlRenderOnce sync.Once
+)
+
var ErrTemplateNotInitialized = errors.New("template system is not initialized, check your log for errors")
func (h *HTMLRender) HTML(w io.Writer, status int, name string, data interface{}) error {
@@ -55,14 +59,14 @@ func (h *HTMLRender) TemplateLookup(name string) (TemplateExecutor, error) {
return nil, ErrTemplateNotInitialized
}
- return tmpls.Executor(name, NewFuncMap()[0])
+ return tmpls.Executor(name, NewFuncMap())
}
func (h *HTMLRender) CompileTemplates() error {
assets := AssetFS()
extSuffix := ".tmpl"
tmpls := scopedtmpl.NewScopedTemplate()
- tmpls.Funcs(NewFuncMap()[0])
+ tmpls.Funcs(NewFuncMap())
files, err := ListWebTemplateAssetNames(assets)
if err != nil {
return nil
@@ -86,20 +90,21 @@ func (h *HTMLRender) CompileTemplates() error {
return nil
}
-// HTMLRenderer returns the current html renderer for the context or creates and stores one within the context for future use
-func HTMLRenderer(ctx context.Context) (context.Context, *HTMLRender) {
- if renderer, ok := ctx.Value(rendererKey).(*HTMLRender); ok {
- return ctx, renderer
- }
+// HTMLRenderer init once and returns the globally shared html renderer
+func HTMLRenderer() *HTMLRender {
+ htmlRenderOnce.Do(initHTMLRenderer)
+ return htmlRender
+}
+func initHTMLRenderer() {
rendererType := "static"
if !setting.IsProd {
rendererType = "auto-reloading"
}
- log.Log(1, log.DEBUG, "Creating "+rendererType+" HTML Renderer")
+ log.Debug("Creating %s HTML Renderer", rendererType)
- renderer := &HTMLRender{}
- if err := renderer.CompileTemplates(); err != nil {
+ htmlRender = &HTMLRender{}
+ if err := htmlRender.CompileTemplates(); err != nil {
p := &templateErrorPrettier{assets: AssetFS()}
wrapFatal(p.handleFuncNotDefinedError(err))
wrapFatal(p.handleUnexpectedOperandError(err))
@@ -107,14 +112,14 @@ func HTMLRenderer(ctx context.Context) (context.Context, *HTMLRender) {
wrapFatal(p.handleGenericTemplateError(err))
log.Fatal("HTMLRenderer CompileTemplates error: %v", err)
}
+
if !setting.IsProd {
- go AssetFS().WatchLocalChanges(ctx, func() {
- if err := renderer.CompileTemplates(); err != nil {
+ go AssetFS().WatchLocalChanges(graceful.GetManager().ShutdownContext(), func() {
+ if err := htmlRender.CompileTemplates(); err != nil {
log.Error("Template error: %v\n%s", err, log.Stack(2))
}
})
}
- return context.WithValue(ctx, rendererKey, renderer), renderer
}
func wrapFatal(msg string) {
diff --git a/modules/templates/mailer.go b/modules/templates/mailer.go
index 280ac0e587..ac1715fcd0 100644
--- a/modules/templates/mailer.go
+++ b/modules/templates/mailer.go
@@ -6,6 +6,7 @@ package templates
import (
"context"
"html/template"
+ "regexp"
"strings"
texttmpl "text/template"
@@ -14,6 +15,8 @@ import (
"code.gitea.io/gitea/modules/setting"
)
+var mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}\s*$`)
+
// mailSubjectTextFuncMap returns functions for injecting to text templates, it's only used for mail subject
func mailSubjectTextFuncMap() texttmpl.FuncMap {
return texttmpl.FuncMap{
@@ -55,9 +58,7 @@ func Mailer(ctx context.Context) (*texttmpl.Template, *template.Template) {
bodyTemplates := template.New("")
subjectTemplates.Funcs(mailSubjectTextFuncMap())
- for _, funcs := range NewFuncMap() {
- bodyTemplates.Funcs(funcs)
- }
+ bodyTemplates.Funcs(NewFuncMap())
assetFS := AssetFS()
refreshTemplates := func() {