aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2021-11-09 17:43:17 -0800
committerMike Crute <mike@crute.us>2021-11-09 17:43:17 -0800
commitef4444e6217592e2666b8a46e8c762eaf93fa8a6 (patch)
tree84c4f7891c21c90072590451dc088deeaca78eb7
parent9f9e8fa552b945aad3d487b6dafd095f939fbfc4 (diff)
downloadgolib-ef4444e6217592e2666b8a46e8c762eaf93fa8a6.tar.bz2
golib-ef4444e6217592e2666b8a46e8c762eaf93fa8a6.tar.xz
golib-ef4444e6217592e2666b8a46e8c762eaf93fa8a6.zip
Convert templates to use FS interfaceecho/v0.2.0
-rw-r--r--echo/echo_default.go37
-rw-r--r--echo/error_handler.go17
-rw-r--r--echo/static_file.go71
-rw-r--r--echo/template_renderer.go25
-rw-r--r--tls/ocsp.go1
5 files changed, 119 insertions, 32 deletions
diff --git a/echo/echo_default.go b/echo/echo_default.go
index ab163ff..895ee6e 100644
--- a/echo/echo_default.go
+++ b/echo/echo_default.go
@@ -5,7 +5,9 @@ import (
5 "crypto/tls" 5 "crypto/tls"
6 "fmt" 6 "fmt"
7 "html/template" 7 "html/template"
8 "io/fs"
8 "net/http" 9 "net/http"
10 "path"
9 "sync" 11 "sync"
10 12
11 glmw "code.crute.us/mcrute/golib/echo/middleware" 13 glmw "code.crute.us/mcrute/golib/echo/middleware"
@@ -20,10 +22,10 @@ type EchoConfig struct {
20 Debug bool 22 Debug bool
21 BindAddress string 23 BindAddress string
22 BindTLSAddress string 24 BindTLSAddress string
23 TLSCert string 25 TLSCacheDir string
24 TLSKey string
25 TrustedProxyIPRanges []string 26 TrustedProxyIPRanges []string
26 TemplatePath *string 27 EmbeddedTemplates fs.FS
28 DiskTemplates fs.FS
27 TemplateGlob *string 29 TemplateGlob *string
28 TemplateFunctions template.FuncMap 30 TemplateFunctions template.FuncMap
29 CombinedHostLogFile string 31 CombinedHostLogFile string
@@ -34,6 +36,7 @@ type EchoConfig struct {
34type EchoWrapper struct { 36type EchoWrapper struct {
35 *echo.Echo 37 *echo.Echo
36 tlsServer http.Server 38 tlsServer http.Server
39 templateFS fs.FS
37 bindAddress string 40 bindAddress string
38 ocspErrors chan gltls.OcspError 41 ocspErrors chan gltls.OcspError
39 ocspManager *gltls.OcspManager 42 ocspManager *gltls.OcspManager
@@ -51,7 +54,7 @@ func (w *EchoWrapper) Init() error {
51} 54}
52 55
53func (w *EchoWrapper) CachedStaticRoute(prefix, path string) { 56func (w *EchoWrapper) CachedStaticRoute(prefix, path string) {
54 w.Group(prefix, glmw.CacheOneMonthMiddleware).Static("/", path) 57 StaticFS(w.GET, w.templateFS, prefix, path, glmw.CacheOneMonthMiddleware)
55} 58}
56 59
57func (w *EchoWrapper) OcspErrors() chan gltls.OcspError { 60func (w *EchoWrapper) OcspErrors() chan gltls.OcspError {
@@ -103,6 +106,10 @@ func (w *EchoWrapper) ServeTLS(ctx context.Context, wg *sync.WaitGroup) error {
103 ) 106 )
104} 107}
105 108
109func (w *EchoWrapper) GetTemplateFS() fs.FS {
110 return w.templateFS
111}
112
106// NewDefaultEchoWithConfig builds a wrapper around an Echo instance and 113// NewDefaultEchoWithConfig builds a wrapper around an Echo instance and
107// configures it in the default way that it should probably be configured in 114// configures it in the default way that it should probably be configured in
108// all cases. The struct returned from this function can be treated like a 115// all cases. The struct returned from this function can be treated like a
@@ -120,11 +127,22 @@ func NewDefaultEchoWithConfig(c EchoConfig) (*EchoWrapper, error) {
120 return nil, fmt.Errorf("Error building XFF IP extractor: %w", err) 127 return nil, fmt.Errorf("Error building XFF IP extractor: %w", err)
121 } 128 }
122 129
130 // Use templates from disk in debug mode and the embedded ones that are
131 // built-in to the binary for prod mode.
132 var templates fs.FS
133 if c.DiskTemplates != nil && c.Debug { // Debug Mode
134 templates = c.DiskTemplates
135 } else if c.EmbeddedTemplates != nil && !c.Debug { // Prod Mode
136 templates = c.EmbeddedTemplates
137 } else {
138 return nil, fmt.Errorf("No templates available for use")
139 }
140
123 // Only install template handlers if the path and glob are set 141 // Only install template handlers if the path and glob are set
124 if c.TemplatePath != nil && c.TemplateGlob != nil { 142 if templates != nil && c.TemplateGlob != nil {
125 e.HTTPErrorHandler = ErrorHandler(*c.TemplatePath, c.TemplateFunctions) 143 e.HTTPErrorHandler = ErrorHandler(templates, c.TemplateFunctions)
126 144
127 tr, err := NewTemplateRenderer(*c.TemplatePath, *c.TemplateGlob, c.TemplateFunctions) 145 tr, err := NewTemplateRenderer(templates, *c.TemplateGlob, c.TemplateFunctions)
128 if err != nil { 146 if err != nil {
129 return nil, fmt.Errorf("Error loading template renderer: %w", err) 147 return nil, fmt.Errorf("Error loading template renderer: %w", err)
130 } 148 }
@@ -146,8 +164,8 @@ func NewDefaultEchoWithConfig(c EchoConfig) (*EchoWrapper, error) {
146 164
147 cmek := make(chan gltls.OcspError) 165 cmek := make(chan gltls.OcspError)
148 cm := &gltls.OcspManager{ 166 cm := &gltls.OcspManager{
149 CertPath: c.TLSCert, 167 CertPath: path.Join(c.TLSCacheDir, "cert.pem"),
150 KeyPath: c.TLSKey, 168 KeyPath: path.Join(c.TLSCacheDir, "key.pem"),
151 Errors: cmek, 169 Errors: cmek,
152 } 170 }
153 171
@@ -181,5 +199,6 @@ func NewDefaultEchoWithConfig(c EchoConfig) (*EchoWrapper, error) {
181 bindAddress: c.BindAddress, 199 bindAddress: c.BindAddress,
182 ocspErrors: cmek, 200 ocspErrors: cmek,
183 ocspManager: cm, 201 ocspManager: cm,
202 templateFS: templates,
184 }, nil 203 }, nil
185} 204}
diff --git a/echo/error_handler.go b/echo/error_handler.go
index c32fe86..bfbcfb6 100644
--- a/echo/error_handler.go
+++ b/echo/error_handler.go
@@ -4,14 +4,14 @@ import (
4 "bytes" 4 "bytes"
5 "fmt" 5 "fmt"
6 "html/template" 6 "html/template"
7 "io/fs"
7 "net/http" 8 "net/http"
8 "path"
9 9
10 "github.com/labstack/echo/v4" 10 "github.com/labstack/echo/v4"
11) 11)
12 12
13// Copied from echo and tweaked to make our errors nicer 13// Copied from echo and tweaked to make our errors nicer
14func ErrorHandler(templatePath string, funcs template.FuncMap) func(error, echo.Context) { 14func ErrorHandler(templates fs.FS, funcs template.FuncMap) func(error, echo.Context) {
15 return func(err error, c echo.Context) { 15 return func(err error, c echo.Context) {
16 defer func() { 16 defer func() {
17 if r := recover(); r != nil { 17 if r := recover(); r != nil {
@@ -37,12 +37,13 @@ func ErrorHandler(templatePath string, funcs template.FuncMap) func(error, echo.
37 } 37 }
38 } 38 }
39 39
40 t, err := template.New("").Funcs(funcs).ParseFiles( 40 t, err := template.New("").Funcs(funcs).ParseFS(
41 path.Join(templatePath, "404.tpl"), 41 templates,
42 path.Join(templatePath, "40x.tpl"), 42 "404.tpl",
43 path.Join(templatePath, "50x.tpl"), 43 "40x.tpl",
44 path.Join(templatePath, "header.tpl"), 44 "50x.tpl",
45 path.Join(templatePath, "footer.tpl"), 45 "header.tpl",
46 "footer.tpl",
46 ) 47 )
47 if err != nil { 48 if err != nil {
48 he = &echo.HTTPError{ 49 he = &echo.HTTPError{
diff --git a/echo/static_file.go b/echo/static_file.go
new file mode 100644
index 0000000..9723db7
--- /dev/null
+++ b/echo/static_file.go
@@ -0,0 +1,71 @@
1package echo
2
3import (
4 "io"
5 "io/fs"
6 "net/http"
7 "net/url"
8 "path/filepath"
9
10 "github.com/labstack/echo/v4"
11)
12
13type routeFunc func(string, echo.HandlerFunc, ...echo.MiddlewareFunc) *echo.Route
14
15func StaticFS(get routeFunc, f fs.FS, prefix, root string, m ...echo.MiddlewareFunc) *echo.Route {
16 if root == "" {
17 root = "." // For security we want to restrict to CWD.
18 }
19
20 h := func(c echo.Context) error {
21 p, err := url.PathUnescape(c.Param("*"))
22 if err != nil {
23 return err
24 }
25
26 name := filepath.Join(root, filepath.Clean("/"+p)) // "/"+ for security
27 fp, err := f.Open(name)
28 if err != nil {
29 // The access path does not exist
30 return echo.NotFoundHandler(c)
31 }
32 defer fp.Close()
33
34 fi, err := fp.Stat()
35 if err != nil {
36 // The access path does not exist
37 return echo.NotFoundHandler(c)
38 }
39
40 // If the request is for a directory and does not end with "/"
41 p = c.Request().URL.Path // path must not be empty.
42 if fi.IsDir() && p[len(p)-1] != '/' {
43 // Redirect to ends with "/"
44 // return c.Redirect(http.StatusMovedPermanently, p+"/")
45 // TODO: Serve an index.html if there is one for this dir
46 return echo.NotFoundHandler(c)
47 }
48
49 fs, ok := fp.(io.ReadSeeker)
50 if !ok {
51 c.Logger().Errorf("File %s is not a io.ReadSeeker", p)
52 return echo.ErrInternalServerError
53 }
54
55 http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), fs)
56 return nil
57 }
58
59 // Handle added routes based on trailing slash:
60 // /prefix => exact route "/prefix" + any route "/prefix/*"
61 // /prefix/ => only any route "/prefix/*"
62 if prefix != "" {
63 if prefix[len(prefix)-1] == '/' {
64 // Only add any route for intentional trailing slash
65 return get(prefix+"*", h, m...)
66 }
67 get(prefix, h, m...)
68 }
69
70 return get(prefix+"/*", h, m...)
71}
diff --git a/echo/template_renderer.go b/echo/template_renderer.go
index 0bafd92..7bfd006 100644
--- a/echo/template_renderer.go
+++ b/echo/template_renderer.go
@@ -3,8 +3,8 @@ package echo
3import ( 3import (
4 "html/template" 4 "html/template"
5 "io" 5 "io"
6 "io/fs"
6 "net/http" 7 "net/http"
7 "os"
8 "path/filepath" 8 "path/filepath"
9 9
10 "github.com/labstack/echo/v4" 10 "github.com/labstack/echo/v4"
@@ -15,16 +15,16 @@ type TemplateChecker interface {
15} 15}
16 16
17type TemplateRenderer struct { 17type TemplateRenderer struct {
18 path string 18 templates fs.FS
19 glob string 19 glob string
20 funcs template.FuncMap 20 funcs template.FuncMap
21 cache *template.Template 21 cache *template.Template
22} 22}
23 23
24func NewTemplateRenderer(path, glob string, funcs template.FuncMap) (*TemplateRenderer, error) { 24func NewTemplateRenderer(templates fs.FS, glob string, funcs template.FuncMap) (*TemplateRenderer, error) {
25 var err error 25 var err error
26 26
27 r := &TemplateRenderer{path: path, glob: glob, funcs: funcs} 27 r := &TemplateRenderer{templates: templates, glob: glob, funcs: funcs}
28 r.cache, err = r.loadTemplates() 28 r.cache, err = r.loadTemplates()
29 if err != nil { 29 if err != nil {
30 return nil, err 30 return nil, err
@@ -36,7 +36,7 @@ func NewTemplateRenderer(path, glob string, funcs template.FuncMap) (*TemplateRe
36func (r *TemplateRenderer) loadTemplates() (*template.Template, error) { 36func (r *TemplateRenderer) loadTemplates() (*template.Template, error) {
37 t := template.New("").Funcs(r.funcs) 37 t := template.New("").Funcs(r.funcs)
38 38
39 err := filepath.Walk(r.path, func(path string, info os.FileInfo, err error) error { 39 err := fs.WalkDir(r.templates, ".", func(path string, info fs.DirEntry, err error) error {
40 if err != nil { 40 if err != nil {
41 return err 41 return err
42 } 42 }
@@ -44,17 +44,12 @@ func (r *TemplateRenderer) loadTemplates() (*template.Template, error) {
44 if !info.IsDir() { 44 if !info.IsDir() {
45 _, fn := filepath.Split(path) 45 _, fn := filepath.Split(path)
46 if ok, _ := filepath.Match(r.glob, fn); ok { 46 if ok, _ := filepath.Match(r.glob, fn); ok {
47 fc, err := os.ReadFile(path) 47 fc, err := fs.ReadFile(r.templates, path)
48 if err != nil { 48 if err != nil {
49 return err 49 return err
50 } 50 }
51 51
52 rp, err := filepath.Rel(r.path, path) 52 _, err = t.New(path).Parse(string(fc))
53 if err != nil {
54 return err
55 }
56
57 _, err = t.New(rp).Parse(string(fc))
58 if err != nil { 53 if err != nil {
59 return err 54 return err
60 } 55 }
diff --git a/tls/ocsp.go b/tls/ocsp.go
index 94f7f68..b80764f 100644
--- a/tls/ocsp.go
+++ b/tls/ocsp.go
@@ -125,6 +125,7 @@ func (m *OcspManager) Run(ctx context.Context, wg *sync.WaitGroup) error {
125 } 125 }
126} 126}
127 127
128// TODO: TLS.GetCertificate for dyanmic certs for LE (cache these)
128func (m *OcspManager) GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) { 129func (m *OcspManager) GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) {
129 m.RLock() 130 m.RLock()
130 defer m.RUnlock() 131 defer m.RUnlock()