@@ -14,6 +14,7 @@ import (
14
14
"io/fs"
15
15
"net/http"
16
16
"net/url"
17
+ "path"
17
18
"strings"
18
19
"sync"
19
20
"time"
@@ -42,10 +43,11 @@ type Server struct {
42
43
getDataSource func (context.Context ) internal.DataSource
43
44
queue queue.Queue
44
45
taskIDChangeInterval time.Duration
45
- staticPath template.TrustedSource
46
- thirdPartyPath string
47
- templateDir template. TrustedSource
46
+ templateFS template.TrustedFS
47
+ staticFS fs. FS
48
+ thirdPartyFS fs. FS
48
49
devMode bool
50
+ staticPath string // used only for dynamic loading in dev mode
49
51
errorPage []byte
50
52
appVersionLabel string
51
53
googleTagManagerID string
@@ -65,9 +67,11 @@ type ServerConfig struct {
65
67
DataSourceGetter func (context.Context ) internal.DataSource
66
68
Queue queue.Queue
67
69
TaskIDChangeInterval time.Duration
68
- StaticPath template.TrustedSource
69
- ThirdPartyPath string
70
+ TemplateFS template.TrustedFS // for loading templates safely
71
+ StaticFS fs.FS // for static/ directory
72
+ ThirdPartyFS fs.FS // for third_party/ directory
70
73
DevMode bool
74
+ StaticPath string // used only for dynamic loading in dev mode
71
75
AppVersionLabel string
72
76
GoogleTagManagerID string
73
77
ServeStats bool
@@ -78,20 +82,19 @@ type ServerConfig struct {
78
82
// NewServer creates a new Server for the given database and template directory.
79
83
func NewServer (scfg ServerConfig ) (_ * Server , err error ) {
80
84
defer derrors .Wrap (& err , "NewServer(...)" )
81
- templateDir := template .TrustedSourceJoin (scfg .StaticPath )
82
- ts , err := parsePageTemplates (templateDir )
85
+ ts , err := parsePageTemplates (scfg .TemplateFS )
83
86
if err != nil {
84
87
return nil , fmt .Errorf ("error parsing templates: %v" , err )
85
88
}
86
- docTemplateDir := template .TrustedSourceJoin (templateDir , template .TrustedSourceFromConstant ("doc" ))
87
- dochtml .LoadTemplates (docTemplateDir )
89
+ dochtml .LoadTemplates (scfg .TemplateFS )
88
90
s := & Server {
89
91
getDataSource : scfg .DataSourceGetter ,
90
92
queue : scfg .Queue ,
91
- staticPath : scfg .StaticPath ,
92
- thirdPartyPath : scfg .ThirdPartyPath ,
93
- templateDir : templateDir ,
93
+ templateFS : scfg .TemplateFS ,
94
+ staticFS : scfg .StaticFS ,
95
+ thirdPartyFS : scfg . ThirdPartyFS ,
94
96
devMode : scfg .DevMode ,
97
+ staticPath : scfg .StaticPath ,
95
98
templates : ts ,
96
99
taskIDChangeInterval : scfg .TaskIDChangeInterval ,
97
100
appVersionLabel : scfg .AppVersionLabel ,
@@ -134,10 +137,11 @@ func (s *Server) Install(handle func(string, http.Handler), redisClient *redis.C
134
137
log .Infof (r .Context (), "Request made to %q" , r .URL .Path )
135
138
}))
136
139
handle ("/static/" , s .staticHandler ())
137
- handle ("/third_party/" , http .StripPrefix ("/third_party" , http .FileServer (http .Dir (s .thirdPartyPath ))))
140
+ handle ("/third_party/" , http .StripPrefix ("/third_party" , http .FileServer (http .FS (s .thirdPartyFS ))))
138
141
handle ("/favicon.ico" , http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
139
- http . ServeFile (w , r , fmt . Sprintf ( "%s/ shared/icon/favicon.ico", http . Dir ( s . staticPath . String ())) )
142
+ serveFileFS (w , r , s . staticFS , " shared/icon/favicon.ico" )
140
143
}))
144
+
141
145
handle ("/sitemap/" , http .StripPrefix ("/sitemap/" , http .FileServer (http .Dir ("private/sitemap" ))))
142
146
handle ("/mod/" , http .HandlerFunc (s .handleModuleDetailsRedirect ))
143
147
handle ("/pkg/" , http .HandlerFunc (s .handlePackageDetailsRedirect ))
@@ -539,7 +543,7 @@ func (s *Server) findTemplate(templateName string) (*template.Template, error) {
539
543
s .mu .Lock ()
540
544
defer s .mu .Unlock ()
541
545
var err error
542
- s .templates , err = parsePageTemplates (s .templateDir )
546
+ s .templates , err = parsePageTemplates (s .templateFS )
543
547
if err != nil {
544
548
return nil , fmt .Errorf ("error parsing templates: %v" , err )
545
549
}
@@ -584,69 +588,73 @@ func stripScheme(url string) string {
584
588
return url
585
589
}
586
590
587
- // parsePageTemplates parses html templates contained in the given base
588
- // directory in order to generate a map of Name->*template.Template.
591
+ // parsePageTemplates parses html templates contained in the given filesystem in
592
+ // order to generate a map of Name->*template.Template.
589
593
//
590
594
// Separate templates are used so that certain contextual functions (e.g.
591
595
// templateName) can be bound independently for each page.
592
596
//
593
597
// Templates in directories prefixed with an underscore are considered helper
594
598
// templates and parsed together with the files in each base directory.
595
- func parsePageTemplates (base template.TrustedSource ) (map [string ]* template.Template , error ) {
596
- tsc := template .TrustedSourceFromConstant
597
- join := template .TrustedSourceJoin
598
-
599
+ func parsePageTemplates (fsys template.TrustedFS ) (map [string ]* template.Template , error ) {
599
600
templates := make (map [string ]* template.Template )
600
- htmlSets := [][]template. TrustedSource {
601
- {tsc ( "badge" ) },
602
- {tsc ( "error" ) },
603
- {tsc ( "fetch" ) },
604
- {tsc ( "homepage" ) },
605
- {tsc ( "legacy_search" ) },
606
- {tsc ( "license-policy" ) },
607
- {tsc ( "search" ) },
608
- {tsc ( "search-help" ) },
609
- {tsc ( "styleguide" ) },
610
- {tsc ( "subrepo" ) },
611
- {tsc ( "unit/importedby" ), tsc ( "unit" ) },
612
- {tsc ( "unit/imports" ), tsc ( "unit" ) },
613
- {tsc ( "unit/licenses" ), tsc ( "unit" ) },
614
- {tsc ( "unit/main" ), tsc ( "unit" ) },
615
- {tsc ( "unit/versions" ), tsc ( "unit" ) },
601
+ htmlSets := [][]string {
602
+ {"badge" },
603
+ {"error" },
604
+ {"fetch" },
605
+ {"homepage" },
606
+ {"legacy_search" },
607
+ {"license-policy" },
608
+ {"search" },
609
+ {"search-help" },
610
+ {"styleguide" },
611
+ {"subrepo" },
612
+ {"unit/importedby" , "unit" },
613
+ {"unit/imports" , "unit" },
614
+ {"unit/licenses" , "unit" },
615
+ {"unit/main" , "unit" },
616
+ {"unit/versions" , "unit" },
616
617
}
617
618
618
619
for _ , set := range htmlSets {
619
- t , err := template .New ("frontend.tmpl" ).Funcs (templateFuncs ).ParseGlobFromTrustedSource ( join ( base , tsc ( "frontend/*.tmpl" )) )
620
+ t , err := template .New ("frontend.tmpl" ).Funcs (templateFuncs ).ParseFS ( fsys , "frontend/*.tmpl" )
620
621
if err != nil {
621
- return nil , fmt .Errorf ("ParseFilesFromTrustedSources : %v" , err )
622
+ return nil , fmt .Errorf ("ParseFS : %v" , err )
622
623
}
623
- helperGlob := join ( base , tsc ( "shared/*/*.tmpl" ))
624
- if _ , err := t .ParseGlobFromTrustedSource ( helperGlob ); err != nil {
625
- return nil , fmt .Errorf ("ParseGlobFromTrustedSource (%q): %v" , helperGlob , err )
624
+ helperGlob := "shared/*/*.tmpl"
625
+ if _ , err := t .ParseFS ( fsys , helperGlob ); err != nil {
626
+ return nil , fmt .Errorf ("ParseFS (%q): %v" , helperGlob , err )
626
627
}
627
- var files []template.TrustedSource
628
628
for _ , f := range set {
629
- if _ , err := t .ParseGlobFromTrustedSource ( join ( base , tsc ("frontend" ) , f , tsc ( "*.tmpl" ) )); err != nil {
630
- return nil , fmt .Errorf ("ParseGlobFromTrustedSource (%v): %v" , files , err )
629
+ if _ , err := t .ParseFS ( fsys , path . Join ("frontend" , f , "*.tmpl" )); err != nil {
630
+ return nil , fmt .Errorf ("ParseFS (%v): %v" , f , err )
631
631
}
632
632
}
633
- templates [set [0 ]. String () ] = t
633
+ templates [set [0 ]] = t
634
634
}
635
635
636
636
return templates , nil
637
637
}
638
638
639
639
func (s * Server ) staticHandler () http.Handler {
640
- staticPath := s .staticPath .String ()
641
-
642
640
// In dev mode compile TypeScript files into minified JavaScript files
643
641
// and rebuild them on file changes.
644
642
if s .devMode {
643
+ if s .staticPath == "" {
644
+ panic ("staticPath is empty in dev mode; cannot rebuild static files" )
645
+ }
645
646
ctx := context .Background ()
646
- _ , err := static .Build (static.Config {EntryPoint : staticPath + "/frontend" , Watch : true , Bundle : true })
647
+ _ , err := static .Build (static.Config {EntryPoint : s . staticPath + "/frontend" , Watch : true , Bundle : true })
647
648
if err != nil {
648
649
log .Error (ctx , err )
649
650
}
650
651
}
651
- return http .StripPrefix ("/static/" , http .FileServer (http .Dir (staticPath )))
652
+ return http .StripPrefix ("/static/" , http .FileServer (http .FS (s .staticFS )))
653
+ }
654
+
655
+ // serveFileFS serves a file from the given filesystem.
656
+ func serveFileFS (w http.ResponseWriter , r * http.Request , fsys fs.FS , name string ) {
657
+ fs := http .FileServer (http .FS (fsys ))
658
+ r .URL .Path = name
659
+ fs .ServeHTTP (w , r )
652
660
}
0 commit comments