From bd37e64c1cee138da8d05f283228cce8c8966e6c Mon Sep 17 00:00:00 2001 From: dragon Date: Tue, 15 Apr 2025 16:31:19 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=A8=A1=E6=9D=BF=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=20/=20=E4=BF=AE=E5=A4=8D=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 +++++++++ config.yaml | 2 +- go.mod | 25 ++++++++------- go.sum | 17 ++++++++++ pkg/core/domain.go | 17 +++++++++- pkg/core/meta.go | 66 ++++++++++++++++++++++++++++++++------- pkg/providers/gitea.go | 7 ++--- pkg/renders/gotemplate.go | 44 ++++++++++++++++++++++++++ pkg/renders/render.go | 29 +++++++++++++++++ pkg/server.go | 43 +++++++++++++++++++------ 10 files changed, 226 insertions(+), 38 deletions(-) create mode 100644 pkg/renders/gotemplate.go create mode 100644 pkg/renders/render.go diff --git a/README.md b/README.md index d6a5d5b..255b82f 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ 本项目的侧重于功能实现,并未考虑任何性能优化,亦未考虑大规模部署的情况,由此带来的任何问题与项目无关。 +注意,项目在最近加入了自定义渲染器功能,可能导致严重的安全和性能问题,如出现相关问题请反馈。 + ## Get Started 安装 `go1.23` 或更高版本,同时安装 `Make` 工具 ,然后执行如下命令: @@ -22,6 +24,18 @@ make gitea-pages 具体配置可查看 [`config.yaml`](./config.yaml)。 +### Render + +说明: **不会**将文件系统 引入到渲染器中,复杂的渲染流程应该采用更加灵活轻便的方案 + +在项目的根目录创建 `.render` 文件,填入如下内容: + +```sh +# parser match +gotemplate **/*.tmpl +``` +其中,`gotemplate` 为解析器类型,`**/*.tmpl` 为匹配的路径,使用 `github.com/gobwas/glob` + ## TODO - [x] 内容缓存 diff --git a/config.yaml b/config.yaml index 6196929..3f57d62 100644 --- a/config.yaml +++ b/config.yaml @@ -22,4 +22,4 @@ page: # 默认 404 页面模板 404: /path/to/html.gotmpl # 默认 500 页面模板 - 500: /path/to/html.gotmpl \ No newline at end of file + 500: /path/to/html.gotmpl diff --git a/go.mod b/go.mod index 7458737..6f8cf38 100644 --- a/go.mod +++ b/go.mod @@ -1,24 +1,27 @@ module code.d7z.net/d7z-project/gitea-pages -go 1.23.2 +go 1.24.2 require ( - code.gitea.io/sdk/gitea v0.19.0 - github.com/stretchr/testify v1.9.0 + code.gitea.io/sdk/gitea v0.21.0 + github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 + github.com/pkg/errors v0.9.1 + github.com/stretchr/testify v1.10.0 + go.uber.org/zap v1.27.0 + gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect + github.com/42wim/httpsig v1.2.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/davidmz/go-pageant v1.0.2 // indirect github.com/go-fed/httpsig v1.1.0 // indirect - github.com/hashicorp/go-version v1.6.0 // indirect - github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/pkg/errors v0.9.1 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/gobwas/glob v0.2.3 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.31.0 // indirect - golang.org/x/sys v0.28.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect + golang.org/x/crypto v0.37.0 // indirect + golang.org/x/sys v0.32.0 // indirect ) diff --git a/go.sum b/go.sum index 781338d..089ebee 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,9 @@ code.gitea.io/sdk/gitea v0.19.0 h1:8I6s1s4RHgzxiPHhOQdgim1RWIRcr0LVMbHBjBFXq4Y= code.gitea.io/sdk/gitea v0.19.0/go.mod h1:IG9xZJoltDNeDSW0qiF2Vqx5orMWa7OhVWrjvrd5NpI= +code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4= +code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA= +github.com/42wim/httpsig v1.2.2 h1:ofAYoHUNs/MJOLqQ8hIxeyz2QxOz8qdSVvp3PX/oPgA= +github.com/42wim/httpsig v1.2.2/go.mod h1:P/UYo7ytNBFwc+dg35IubuAUIs8zj5zzFIgUCEl55WY= github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0= github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -9,8 +13,14 @@ github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454Wv github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -27,6 +37,8 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= @@ -36,6 +48,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -43,9 +57,12 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/pkg/core/domain.go b/pkg/core/domain.go index 216d9e3..12ca816 100644 --- a/pkg/core/domain.go +++ b/pkg/core/domain.go @@ -6,6 +6,8 @@ import ( "regexp" "strings" + "code.d7z.net/d7z-project/gitea-pages/pkg/renders" + "code.d7z.net/d7z-project/gitea-pages/pkg/utils" "github.com/pkg/errors" @@ -43,6 +45,17 @@ func (m *PageDomainContent) CacheKey() string { return fmt.Sprintf("%s/%s/%s/%s", m.Owner, m.Repo, m.CommitID, m.Path) } +func (m *PageDomainContent) TryRender(path ...string) renders.Render { + for _, s := range path { + for _, compiler := range m.rendersL { + if compiler.regex.Match(s) { + return compiler.Render + } + } + } + return nil +} + func (p *PageDomain) ParseDomainMeta(domain, path, branch string) (*PageDomainContent, error) { if branch == "" { branch = p.defaultBranch @@ -93,6 +106,8 @@ func (p *PageDomain) ReturnMeta(owner string, repo string, branch string, path [ } } return rel, nil + } else { + zap.L().Debug("查询错误", zap.Error(err)) } - return nil, os.ErrNotExist + return nil, errors.Wrapf(os.ErrNotExist, strings.Join(path, "/")) } diff --git a/pkg/core/meta.go b/pkg/core/meta.go index c1e8cfd..12402e2 100644 --- a/pkg/core/meta.go +++ b/pkg/core/meta.go @@ -10,6 +10,10 @@ import ( "strings" "time" + "code.d7z.net/d7z-project/gitea-pages/pkg/renders" + + "github.com/gobwas/glob" + "go.uber.org/zap" "github.com/pkg/errors" @@ -29,17 +33,33 @@ type ServerMeta struct { locker *utils.Locker } +type renderCompiler struct { + regex glob.Glob + renders.Render +} + type PageMetaContent struct { - CommitID string `json:"id"` // 提交 COMMIT ID - IsPage bool `json:"pg"` // 是否为 Page - Domain string `json:"domain"` // 匹配的域名 - HistoryRouteMode bool `json:"historyRouteMode"` // 路由模式 - CustomNotFound bool `json:"404"` // 注册了自定义 404 页面 - LastModified time.Time `json:"up"` // 上次更新时间 + CommitID string `json:"id"` // 提交 COMMIT ID + IsPage bool `json:"pg"` // 是否为 Page + Domain string `json:"domain"` // 匹配的域名 + HistoryRouteMode bool `json:"historyRouteMode"` // 路由模式 + CustomNotFound bool `json:"404"` // 注册了自定义 404 页面 + LastModified time.Time `json:"up"` // 上次更新时间 + Renders map[string]string `json:"renders"` // 配置的渲染器 + + rendersL []*renderCompiler } func (m *PageMetaContent) From(data string) error { - return json.Unmarshal([]byte(data), m) + err := json.Unmarshal([]byte(data), m) + clear(m.rendersL) + for key, g := range m.Renders { + m.rendersL = append(m.rendersL, &renderCompiler{ + regex: glob.MustCompile(g), + Render: renders.GetRender(key), + }) + } + return err } func (m *PageMetaContent) String() string { @@ -53,7 +73,8 @@ func NewServerMeta(client *http.Client, backend Backend, config utils.Config, tt func (s *ServerMeta) GetMeta(owner, repo, branch string) (*PageMetaContent, error) { rel := &PageMetaContent{ - IsPage: false, + IsPage: false, + Renders: make(map[string]string), } if repos, err := s.Repos(owner); err != nil { return nil, err @@ -111,11 +132,9 @@ func (s *ServerMeta) GetMeta(owner, repo, branch string) (*PageMetaContent, erro rel.IsPage = true } if find, _ := s.FileExists(owner, repo, rel.CommitID, "404.html"); !find { - rel.CustomNotFound = true + rel.CustomNotFound = find } - if cname, err := s.ReadString(owner, repo, rel.CommitID, "CNAME"); err != nil && !errors.Is(err, os.ErrNotExist) { - return nil, err - } else { + if cname, err := s.ReadString(owner, repo, rel.CommitID, "CNAME"); err == nil { cname = strings.TrimSpace(cname) if regexpHostname.MatchString(cname) { rel.Domain = cname @@ -123,6 +142,29 @@ func (s *ServerMeta) GetMeta(owner, repo, branch string) (*PageMetaContent, erro zap.L().Debug("指定的 CNAME 不合法", zap.String("cname", cname)) } } + if r, err := s.ReadString(owner, repo, rel.CommitID, ".render"); err == nil { + for _, render := range strings.Split(r, "\n") { + render = strings.TrimSpace(render) + if strings.HasPrefix(render, "#") { + continue + } + before, after, found := strings.Cut(render, " ") + before = strings.TrimSpace(before) + after = strings.TrimSpace(after) + if found { + if r := renders.GetRender(before); r != nil { + if g, err := glob.Compile(after); err == nil { + rel.Renders[before] = after + rel.rendersL = append(rel.rendersL, &renderCompiler{ + regex: g, + Render: r, + }) + } + } + } + + } + } if find, _ := s.FileExists(owner, repo, rel.CommitID, ".history"); find { rel.HistoryRouteMode = true } diff --git a/pkg/providers/gitea.go b/pkg/providers/gitea.go index df84f58..4715e31 100644 --- a/pkg/providers/gitea.go +++ b/pkg/providers/gitea.go @@ -37,11 +37,10 @@ func (g *ProviderGitea) Repos(owner string) (map[string]string, error) { ListOptions: gitea.ListOptions{ PageSize: GiteaMaxCount, }, - }); err != nil || (resp != nil && resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNotFound) { + }); err != nil { if resp != nil { _ = resp.Body.Close() } - return nil, err } else { if resp != nil { _ = resp.Body.Close() @@ -49,17 +48,17 @@ func (g *ProviderGitea) Repos(owner string) (map[string]string, error) { for _, item := range repos { result[item.Name] = item.DefaultBranch } + return result, nil } if len(result) == 0 { if repos, resp, err := g.gitea.ListUserRepos(owner, gitea.ListReposOptions{ ListOptions: gitea.ListOptions{ PageSize: GiteaMaxCount, }, - }); err != nil || (resp != nil && resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNotFound) { + }); err != nil { if resp != nil { _ = resp.Body.Close() } - return nil, err } else { if resp != nil { _ = resp.Body.Close() diff --git a/pkg/renders/gotemplate.go b/pkg/renders/gotemplate.go new file mode 100644 index 0000000..c86b42e --- /dev/null +++ b/pkg/renders/gotemplate.go @@ -0,0 +1,44 @@ +package renders + +import ( + "bytes" + "io" + "net/http" + "strings" + "text/template" + + sprig "github.com/go-task/slim-sprig/v3" +) + +type GoTemplate struct{} + +func init() { + RegisterRender("gotemplate", &GoTemplate{}) +} + +func (g GoTemplate) Render(w http.ResponseWriter, r *http.Request, input io.Reader) error { + dataB, err := io.ReadAll(input) + if err != nil { + return err + } + out := &bytes.Buffer{} + parse, err := template.New("tmpl").Funcs(sprig.FuncMap()).Option("missingkey=error").Parse(string(dataB)) + headers := make(map[string]string) + for k, vs := range r.Header { + headers[k] = strings.Join(vs, ",") + } + if err != nil { + return err + } + err = parse.Execute(out, map[string]interface{}{ + "Request": map[string]any{ + "Method": r.Method, + "Headers": headers, + }, + }) + if err != nil { + return err + } + _, err = out.WriteTo(w) + return err +} diff --git a/pkg/renders/render.go b/pkg/renders/render.go new file mode 100644 index 0000000..7d2433e --- /dev/null +++ b/pkg/renders/render.go @@ -0,0 +1,29 @@ +package renders + +import ( + "io" + "net/http" + "sync" +) + +var ( + renders = make(map[string]Render) + lock = &sync.Mutex{} +) + +type Render interface { + Render(w http.ResponseWriter, r *http.Request, input io.Reader) error +} + +func RegisterRender(fType string, r Render) { + lock.Lock() + defer lock.Unlock() + if renders[fType] != nil { + panic("duplicate render type: " + fType) + } + renders[fType] = r +} + +func GetRender(key string) Render { + return renders[key] +} diff --git a/pkg/server.go b/pkg/server.go index 06ce679..52ce3ab 100644 --- a/pkg/server.go +++ b/pkg/server.go @@ -79,16 +79,20 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) { }() err := s.Serve(writer, request) if err != nil { + zap.L().Debug("错误请求", zap.Error(err), zap.Any("request", request.RequestURI)) s.options.DefaultErrorHandler(writer, request, err) } } func (s *Server) Serve(writer http.ResponseWriter, request *http.Request) error { + if request.Method != "GET" { + return os.ErrNotExist + } meta, err := s.meta.ParseDomainMeta(request.Host, request.URL.Path, request.URL.Query().Get("branch")) if err != nil { return err } - zap.L().Debug("获取请求", zap.Any("meta", meta)) + zap.L().Debug("获取请求", zap.Any("request", meta.Path)) // todo(feat) : 支持 http range result, err := s.reader.Open(meta.Owner, meta.Repo, meta.CommitID, meta.Path) if err != nil { @@ -106,27 +110,48 @@ func (s *Server) Serve(writer http.ResponseWriter, request *http.Request) error } writer.Header().Set("Content-Type", mime.TypeByExtension(".html")) writer.WriteHeader(http.StatusNotFound) - _, _ = io.Copy(writer, result) - _ = result.Close() + if render := meta.TryRender(meta.Path, "/404.html"); render != nil { + defer result.Close() + if err = render.Render(writer, request, result); err != nil { + return err + } + return nil + } else { + _, _ = io.Copy(writer, result) + _ = result.Close() + } return nil } fileName := filepath.Base(meta.Path) + render := meta.TryRender(meta.Path) + defer result.Close() if reader, ok := result.(*utils.CacheContent); ok { writer.Header().Add("X-Cache", "HIT") + writer.Header().Set("Content-Type", mime.TypeByExtension(filepath.Ext(fileName))) writer.Header().Add("Cache-Control", "public, max-age=86400") - http.ServeContent(writer, request, fileName, reader.LastModified, reader) - _ = reader.Close() + if render != nil { + if err = render.Render(writer, request, reader); err != nil { + return err + } + } else { + http.ServeContent(writer, request, fileName, reader.LastModified, reader) + } } else { - if reader, ok := result.(*utils.SizeReadCloser); ok { + if reader, ok := result.(*utils.SizeReadCloser); ok && render == nil { writer.Header().Add("Content-Length", strconv.Itoa(reader.Size)) } // todo(bug) : 直连模式下告知数据长度 writer.Header().Add("X-Cache", "MISS") writer.Header().Add("Cache-Control", "public, max-age=86400") - writer.Header().Set("Content-Type", mime.TypeByExtension(meta.Path)) + writer.Header().Set("Content-Type", mime.TypeByExtension(filepath.Ext(fileName))) writer.WriteHeader(http.StatusOK) - _, _ = io.Copy(writer, result) - _ = result.Close() + if render != nil { + if err = render.Render(writer, request, reader); err != nil { + return err + } + } else { + _, _ = io.Copy(writer, result) + } } return nil }