From 266aa517c73fa8bfa0686df7808ecacf23cf5672 Mon Sep 17 00:00:00 2001 From: Bozhidar Hristov Date: Fri, 9 Aug 2024 13:41:38 +0300 Subject: [PATCH 1/9] add immutable option --- config.go | 2 + plugin.go | 185 ++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 132 insertions(+), 55 deletions(-) diff --git a/config.go b/config.go index a3de203..d47a73b 100644 --- a/config.go +++ b/config.go @@ -18,6 +18,8 @@ type Config struct { // Weak etag `W/` Weak bool `mapstructure:"weak"` + Immutable bool `mapstructure:"immutable"` + // forbid specifies a list of file extensions which are forbidden for access. // example: .php, .exe, .bat, .htaccess etc. Forbid []string `mapstructure:"forbid"` diff --git a/plugin.go b/plugin.go index 7b5b885..43b0f17 100644 --- a/plugin.go +++ b/plugin.go @@ -34,6 +34,129 @@ type Logger interface { NamedLogger(name string) *zap.Logger } +type FileServer func(ps *Plugin, next http.Handler, w http.ResponseWriter, r *http.Request, fp string) + +func server(s *Plugin, next http.Handler, w http.ResponseWriter, r *http.Request, fp string) { + // ok, file is not in the forbidden list + // Stat it and get file info + f, err := s.root.Open(fp) + if err != nil { + // else no such file, show error in logs only in debug mode + s.log.Debug("no such file or directory", zap.Error(err)) + // pass request to the worker + next.ServeHTTP(w, r) + return + } + + // at high confidence here should not be an error + // because we stat-ed the path previously and know, that that is file (not a dir), and it exists + finfo, err := f.Stat() + if err != nil { + // else no such file, show error in logs only in debug mode + s.log.Debug("no such file or directory", zap.Error(err)) + // pass request to the worker + next.ServeHTTP(w, r) + return + } + + defer func() { + err = f.Close() + if err != nil { + s.log.Error("file close error", zap.Error(err)) + } + }() + + // if provided path to the dir, do not serve the dir, but pass the request to the worker + if finfo.IsDir() { + s.log.Debug("possible path to dir provided") + // pass request to the worker + next.ServeHTTP(w, r) + return + } + + // set etag + if s.cfg.CalculateEtag { + SetEtag(s.cfg.Weak, f, finfo.Name(), w) + } + + if s.cfg.Request != nil { + for k, v := range s.cfg.Request { + r.Header.Add(k, v) + } + } + + if s.cfg.Response != nil { + for k, v := range s.cfg.Response { + w.Header().Set(k, v) + } + } + + // we passed all checks - serve the file + http.ServeContent(w, r, finfo.Name(), finfo.ModTime(), f) +} + +func createImmutableServer(rootDir http.Dir) FileServer { + // HOW TO SCAN files ?! + + return func(s *Plugin, next http.Handler, w http.ResponseWriter, r *http.Request, fp string) { + // Stat it and get file info + f, err := s.root.Open(fp) + if err != nil { + // else no such file, show error in logs only in debug mode + s.log.Debug("no such file or directory", zap.Error(err)) + // pass request to the worker + next.ServeHTTP(w, r) + return + } + + // at high confidence here should not be an error + // because we stat-ed the path previously and know, that that is file (not a dir), and it exists + finfo, err := f.Stat() + if err != nil { + // else no such file, show error in logs only in debug mode + s.log.Debug("no such file or directory", zap.Error(err)) + // pass request to the worker + next.ServeHTTP(w, r) + return + } + + defer func() { + err = f.Close() + if err != nil { + s.log.Error("file close error", zap.Error(err)) + } + }() + + // if provided path to the dir, do not serve the dir, but pass the request to the worker + if finfo.IsDir() { + s.log.Debug("possible path to dir provided") + // pass request to the worker + next.ServeHTTP(w, r) + return + } + + // set etag + if s.cfg.CalculateEtag { + SetEtag(s.cfg.Weak, f, finfo.Name(), w) + } + + if s.cfg.Request != nil { + for k, v := range s.cfg.Request { + r.Header.Add(k, v) + } + } + + if s.cfg.Response != nil { + for k, v := range s.cfg.Response { + w.Header().Set(k, v) + } + } + + // we passed all checks - serve the file + http.ServeContent(w, r, finfo.Name(), finfo.ModTime(), f) + } +} + // Plugin serves static files. Potentially convert into middleware? type Plugin struct { // server configuration (location, forbidden files etc) @@ -116,6 +239,12 @@ func (s *Plugin) Name() string { // Middleware must return true if a request/response pair is handled within the middleware. func (s *Plugin) Middleware(next http.Handler) http.Handler { //nolint:gocognit,gocyclo + var server FileServer = server + + if s.cfg.Immutable { + server = createImmutableServer(s.root) + } + // Define the http.HandlerFunc return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if val, ok := r.Context().Value(rrcontext.OtelTracerNameKey).(string); ok { @@ -170,63 +299,9 @@ func (s *Plugin) Middleware(next http.Handler) http.Handler { //nolint:gocognit, // file extension allowed } - // ok, file is not in the forbidden list - // Stat it and get file info - f, err := s.root.Open(fp) - if err != nil { - // else no such file, show error in logs only in debug mode - s.log.Debug("no such file or directory", zap.Error(err)) - // pass request to the worker - next.ServeHTTP(w, r) - return - } - - // at high confidence here should not be an error - // because we stat-ed the path previously and know, that that is file (not a dir), and it exists - finfo, err := f.Stat() - if err != nil { - // else no such file, show error in logs only in debug mode - s.log.Debug("no such file or directory", zap.Error(err)) - // pass request to the worker - next.ServeHTTP(w, r) - return - } - defer func() { - err = f.Close() - if err != nil { - s.log.Error("file close error", zap.Error(err)) - } - }() - - // if provided path to the dir, do not serve the dir, but pass the request to the worker - if finfo.IsDir() { - s.log.Debug("possible path to dir provided") - // pass request to the worker - next.ServeHTTP(w, r) - return - } - - // set etag - if s.cfg.CalculateEtag { - SetEtag(s.cfg.Weak, f, finfo.Name(), w) - } - - if s.cfg.Request != nil { - for k, v := range s.cfg.Request { - r.Header.Add(k, v) - } - } - - if s.cfg.Response != nil { - for k, v := range s.cfg.Response { - w.Header().Set(k, v) - } - } - - // we passed all checks - serve the file - http.ServeContent(w, r, finfo.Name(), finfo.ModTime(), f) + server(s, next, w, r, fp) }) } From 074182274ff7fd461dcff27fcbb3655a993f603b Mon Sep 17 00:00:00 2001 From: Bozhidar Hristov Date: Fri, 9 Aug 2024 13:54:06 +0300 Subject: [PATCH 2/9] immutable server --- plugin.go | 46 ++++++++++++++-------------------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/plugin.go b/plugin.go index 43b0f17..473c3b1 100644 --- a/plugin.go +++ b/plugin.go @@ -3,8 +3,10 @@ package static import ( "fmt" "net/http" + "os" "path" "strings" + "time" "unsafe" rrcontext "github.com/roadrunner-server/context" @@ -95,41 +97,21 @@ func server(s *Plugin, next http.Handler, w http.ResponseWriter, r *http.Request http.ServeContent(w, r, finfo.Name(), finfo.ModTime(), f) } +type ScannedFile struct { + file *os.File + name string + modTime time.Time +} + func createImmutableServer(rootDir http.Dir) FileServer { // HOW TO SCAN files ?! + var files map[string]*ScannedFile return func(s *Plugin, next http.Handler, w http.ResponseWriter, r *http.Request, fp string) { - // Stat it and get file info - f, err := s.root.Open(fp) - if err != nil { + var file = files[fp] + if file == nil { // else no such file, show error in logs only in debug mode - s.log.Debug("no such file or directory", zap.Error(err)) - // pass request to the worker - next.ServeHTTP(w, r) - return - } - - // at high confidence here should not be an error - // because we stat-ed the path previously and know, that that is file (not a dir), and it exists - finfo, err := f.Stat() - if err != nil { - // else no such file, show error in logs only in debug mode - s.log.Debug("no such file or directory", zap.Error(err)) - // pass request to the worker - next.ServeHTTP(w, r) - return - } - - defer func() { - err = f.Close() - if err != nil { - s.log.Error("file close error", zap.Error(err)) - } - }() - - // if provided path to the dir, do not serve the dir, but pass the request to the worker - if finfo.IsDir() { - s.log.Debug("possible path to dir provided") + s.log.Debug("no such file or directory") // pass request to the worker next.ServeHTTP(w, r) return @@ -137,7 +119,7 @@ func createImmutableServer(rootDir http.Dir) FileServer { // set etag if s.cfg.CalculateEtag { - SetEtag(s.cfg.Weak, f, finfo.Name(), w) + SetEtag(s.cfg.Weak, file.file, file.name, w) } if s.cfg.Request != nil { @@ -153,7 +135,7 @@ func createImmutableServer(rootDir http.Dir) FileServer { } // we passed all checks - serve the file - http.ServeContent(w, r, finfo.Name(), finfo.ModTime(), f) + http.ServeContent(w, r, file.name, file.modTime, file.file) } } From dcd05c214cd5f2bc27f5d4c22d6478d4037938e3 Mon Sep 17 00:00:00 2001 From: Bozhidar Hristov Date: Fri, 9 Aug 2024 13:59:11 +0300 Subject: [PATCH 3/9] optimize etag calculation --- etag.go | 17 +++++++++-------- plugin.go | 7 ++++--- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/etag.go b/etag.go index cc3239a..2a704a5 100644 --- a/etag.go +++ b/etag.go @@ -14,31 +14,27 @@ var weakPrefix = []byte(`W/`) //nolint:gochecknoglobals // CRC32 table, constant var crc32q = crc32.MakeTable(0x48D90782) //nolint:gochecknoglobals -// SetEtag sets etag for the file -func SetEtag(weak bool, f http.File, name string, w http.ResponseWriter) { +func calculateEtag(weak bool, f http.File, name string) []byte { // preallocate calculatedEtag := make([]byte, 0, 64) - // write weak if weak { calculatedEtag = append(calculatedEtag, weakPrefix...) calculatedEtag = append(calculatedEtag, '"') calculatedEtag = appendUint(calculatedEtag, crc32.Checksum(strToBytes(name), crc32q)) calculatedEtag = append(calculatedEtag, '"') - - w.Header().Set(etag, bytesToStr(calculatedEtag)) - return + return calculatedEtag } // read the file content body, err := io.ReadAll(f) if err != nil { - return + return calculatedEtag } // skip for 0 body if len(body) == 0 { - return + return calculatedEtag } calculatedEtag = append(calculatedEtag, '"') @@ -47,6 +43,11 @@ func SetEtag(weak bool, f http.File, name string, w http.ResponseWriter) { calculatedEtag = appendUint(calculatedEtag, crc32.Checksum(body, crc32q)) calculatedEtag = append(calculatedEtag, '"') + return calculatedEtag +} + +// SetEtag sets etag for the file +func SetEtag(w http.ResponseWriter, calculatedEtag []byte) { w.Header().Set(etag, bytesToStr(calculatedEtag)) } diff --git a/plugin.go b/plugin.go index 473c3b1..e5a3af3 100644 --- a/plugin.go +++ b/plugin.go @@ -78,7 +78,7 @@ func server(s *Plugin, next http.Handler, w http.ResponseWriter, r *http.Request // set etag if s.cfg.CalculateEtag { - SetEtag(s.cfg.Weak, f, finfo.Name(), w) + SetEtag(w, calculateEtag(s.cfg.Weak, f, finfo.Name())) } if s.cfg.Request != nil { @@ -101,6 +101,7 @@ type ScannedFile struct { file *os.File name string modTime time.Time + etag []byte } func createImmutableServer(rootDir http.Dir) FileServer { @@ -118,8 +119,8 @@ func createImmutableServer(rootDir http.Dir) FileServer { } // set etag - if s.cfg.CalculateEtag { - SetEtag(s.cfg.Weak, file.file, file.name, w) + if file.etag != nil { + SetEtag(w, file.etag) } if s.cfg.Request != nil { From 121192a500587330842f43eefc3ad97eaf129109 Mon Sep 17 00:00:00 2001 From: Bozhidar Hristov Date: Fri, 9 Aug 2024 14:00:41 +0300 Subject: [PATCH 4/9] optimize etag calculation --- etag.go | 14 +++++++------- plugin.go | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/etag.go b/etag.go index 2a704a5..6e9e43e 100644 --- a/etag.go +++ b/etag.go @@ -14,7 +14,7 @@ var weakPrefix = []byte(`W/`) //nolint:gochecknoglobals // CRC32 table, constant var crc32q = crc32.MakeTable(0x48D90782) //nolint:gochecknoglobals -func calculateEtag(weak bool, f http.File, name string) []byte { +func calculateEtag(weak bool, f http.File, name string) string { // preallocate calculatedEtag := make([]byte, 0, 64) @@ -23,18 +23,18 @@ func calculateEtag(weak bool, f http.File, name string) []byte { calculatedEtag = append(calculatedEtag, '"') calculatedEtag = appendUint(calculatedEtag, crc32.Checksum(strToBytes(name), crc32q)) calculatedEtag = append(calculatedEtag, '"') - return calculatedEtag + return bytesToStr(calculatedEtag) } // read the file content body, err := io.ReadAll(f) if err != nil { - return calculatedEtag + return bytesToStr(calculatedEtag) } // skip for 0 body if len(body) == 0 { - return calculatedEtag + return bytesToStr(calculatedEtag) } calculatedEtag = append(calculatedEtag, '"') @@ -43,12 +43,12 @@ func calculateEtag(weak bool, f http.File, name string) []byte { calculatedEtag = appendUint(calculatedEtag, crc32.Checksum(body, crc32q)) calculatedEtag = append(calculatedEtag, '"') - return calculatedEtag + return bytesToStr(calculatedEtag) } // SetEtag sets etag for the file -func SetEtag(w http.ResponseWriter, calculatedEtag []byte) { - w.Header().Set(etag, bytesToStr(calculatedEtag)) +func SetEtag(w http.ResponseWriter, calculatedEtag string) { + w.Header().Set(etag, calculatedEtag) } // appendUint appends n to dst and returns the extended dst. diff --git a/plugin.go b/plugin.go index e5a3af3..eed5d1c 100644 --- a/plugin.go +++ b/plugin.go @@ -101,7 +101,7 @@ type ScannedFile struct { file *os.File name string modTime time.Time - etag []byte + etag string } func createImmutableServer(rootDir http.Dir) FileServer { @@ -119,7 +119,7 @@ func createImmutableServer(rootDir http.Dir) FileServer { } // set etag - if file.etag != nil { + if file.etag != "" { SetEtag(w, file.etag) } From 698b92408a520d28e2ec27939b74fb8f374da576 Mon Sep 17 00:00:00 2001 From: Bozhidar Hristov Date: Fri, 9 Aug 2024 15:27:26 +0300 Subject: [PATCH 5/9] READY ?! --- plugin.go | 48 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/plugin.go b/plugin.go index eed5d1c..4be1da4 100644 --- a/plugin.go +++ b/plugin.go @@ -5,6 +5,7 @@ import ( "net/http" "os" "path" + "path/filepath" "strings" "time" "unsafe" @@ -98,19 +99,52 @@ func server(s *Plugin, next http.Handler, w http.ResponseWriter, r *http.Request } type ScannedFile struct { - file *os.File + file http.File name string modTime time.Time etag string } -func createImmutableServer(rootDir http.Dir) FileServer { - // HOW TO SCAN files ?! - var files map[string]*ScannedFile +func createImmutableServer(s *Plugin) FileServer { + var files map[string]ScannedFile + + var scanner func(path string, info os.FileInfo, err error) error + scanner = func(path string, info os.FileInfo, err error) error { + if info.IsDir() { + return filepath.Walk(info.Name(), scanner) + } + + file, err := s.root.Open(path) + + if err != nil { + panic(err) + } + + var etag string = "" + + if s.cfg.CalculateEtag { + etag = calculateEtag(s.cfg.Weak, file, info.Name()) + } + + files[path] = ScannedFile{ + file: file, + modTime: info.ModTime(), + name: info.Name(), + etag: etag, + } + + return nil + } + + err := filepath.Walk(s.cfg.Dir, scanner) + + if err != nil { + panic(err) + } return func(s *Plugin, next http.Handler, w http.ResponseWriter, r *http.Request, fp string) { - var file = files[fp] - if file == nil { + file, ok := files[fp] + if ok { // else no such file, show error in logs only in debug mode s.log.Debug("no such file or directory") // pass request to the worker @@ -225,7 +259,7 @@ func (s *Plugin) Middleware(next http.Handler) http.Handler { //nolint:gocognit, var server FileServer = server if s.cfg.Immutable { - server = createImmutableServer(s.root) + server = createImmutableServer(s) } // Define the http.HandlerFunc From 20bbe599dc7f38e21cb7ceaf8e30c6f5ad000415 Mon Sep 17 00:00:00 2001 From: Bozhidar Hristov Date: Fri, 9 Aug 2024 19:22:32 +0300 Subject: [PATCH 6/9] Check walk err + fixes --- plugin.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/plugin.go b/plugin.go index 4be1da4..1e6bc33 100644 --- a/plugin.go +++ b/plugin.go @@ -110,17 +110,21 @@ func createImmutableServer(s *Plugin) FileServer { var scanner func(path string, info os.FileInfo, err error) error scanner = func(path string, info os.FileInfo, err error) error { + if err != nil { + panic(err) + } + if info.IsDir() { return filepath.Walk(info.Name(), scanner) } - file, err := s.root.Open(path) + file, openError := s.root.Open(path) - if err != nil { - panic(err) + if openError != nil { + panic(openError) } - var etag string = "" + var etag string if s.cfg.CalculateEtag { etag = calculateEtag(s.cfg.Weak, file, info.Name()) @@ -255,7 +259,7 @@ func (s *Plugin) Name() string { } // Middleware must return true if a request/response pair is handled within the middleware. -func (s *Plugin) Middleware(next http.Handler) http.Handler { //nolint:gocognit,gocyclo +func (s *Plugin) Middleware(next http.Handler) http.Handler { var server FileServer = server if s.cfg.Immutable { From 3fc3c06e1e0dcc338f1b4fbd6dd6e6d1a8d676a4 Mon Sep 17 00:00:00 2001 From: Bozhidar Hristov Date: Fri, 9 Aug 2024 21:32:10 +0300 Subject: [PATCH 7/9] return error, panic at plugin only --- plugin.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/plugin.go b/plugin.go index 1e6bc33..21279b4 100644 --- a/plugin.go +++ b/plugin.go @@ -105,13 +105,13 @@ type ScannedFile struct { etag string } -func createImmutableServer(s *Plugin) FileServer { +func createImmutableServer(s *Plugin) (FileServer, error) { var files map[string]ScannedFile var scanner func(path string, info os.FileInfo, err error) error scanner = func(path string, info os.FileInfo, err error) error { if err != nil { - panic(err) + return err } if info.IsDir() { @@ -121,7 +121,7 @@ func createImmutableServer(s *Plugin) FileServer { file, openError := s.root.Open(path) if openError != nil { - panic(openError) + return openError } var etag string @@ -143,7 +143,7 @@ func createImmutableServer(s *Plugin) FileServer { err := filepath.Walk(s.cfg.Dir, scanner) if err != nil { - panic(err) + return nil, err } return func(s *Plugin, next http.Handler, w http.ResponseWriter, r *http.Request, fp string) { @@ -175,7 +175,7 @@ func createImmutableServer(s *Plugin) FileServer { // we passed all checks - serve the file http.ServeContent(w, r, file.name, file.modTime, file.file) - } + }, nil } // Plugin serves static files. Potentially convert into middleware? @@ -263,7 +263,12 @@ func (s *Plugin) Middleware(next http.Handler) http.Handler { var server FileServer = server if s.cfg.Immutable { - server = createImmutableServer(s) + immutableServer, err := createImmutableServer(s) + if err != nil { + panic(err) + } + + server = immutableServer } // Define the http.HandlerFunc From 548cea31bc4acf35391dd98ece34ff384fe1ce1d Mon Sep 17 00:00:00 2001 From: Bozhidar Hristov Date: Fri, 9 Aug 2024 21:46:33 +0300 Subject: [PATCH 8/9] remove panic ? --- plugin.go | 132 +++++++++++++++++++++++++++++------------------------- 1 file changed, 70 insertions(+), 62 deletions(-) diff --git a/plugin.go b/plugin.go index 21279b4..0b0ac7d 100644 --- a/plugin.go +++ b/plugin.go @@ -37,65 +37,67 @@ type Logger interface { NamedLogger(name string) *zap.Logger } -type FileServer func(ps *Plugin, next http.Handler, w http.ResponseWriter, r *http.Request, fp string) +type FileServer func(next http.Handler, w http.ResponseWriter, r *http.Request, fp string) -func server(s *Plugin, next http.Handler, w http.ResponseWriter, r *http.Request, fp string) { - // ok, file is not in the forbidden list - // Stat it and get file info - f, err := s.root.Open(fp) - if err != nil { - // else no such file, show error in logs only in debug mode - s.log.Debug("no such file or directory", zap.Error(err)) - // pass request to the worker - next.ServeHTTP(w, r) - return - } - - // at high confidence here should not be an error - // because we stat-ed the path previously and know, that that is file (not a dir), and it exists - finfo, err := f.Stat() - if err != nil { - // else no such file, show error in logs only in debug mode - s.log.Debug("no such file or directory", zap.Error(err)) - // pass request to the worker - next.ServeHTTP(w, r) - return - } +func createMutableServer(s *Plugin) FileServer { + return func(next http.Handler, w http.ResponseWriter, r *http.Request, fp string) { + // ok, file is not in the forbidden list + // Stat it and get file info + f, err := s.root.Open(fp) + if err != nil { + // else no such file, show error in logs only in debug mode + s.log.Debug("no such file or directory", zap.Error(err)) + // pass request to the worker + next.ServeHTTP(w, r) + return + } - defer func() { - err = f.Close() + // at high confidence here should not be an error + // because we stat-ed the path previously and know, that that is file (not a dir), and it exists + finfo, err := f.Stat() if err != nil { - s.log.Error("file close error", zap.Error(err)) + // else no such file, show error in logs only in debug mode + s.log.Debug("no such file or directory", zap.Error(err)) + // pass request to the worker + next.ServeHTTP(w, r) + return } - }() - - // if provided path to the dir, do not serve the dir, but pass the request to the worker - if finfo.IsDir() { - s.log.Debug("possible path to dir provided") - // pass request to the worker - next.ServeHTTP(w, r) - return - } - // set etag - if s.cfg.CalculateEtag { - SetEtag(w, calculateEtag(s.cfg.Weak, f, finfo.Name())) - } + defer func() { + err = f.Close() + if err != nil { + s.log.Error("file close error", zap.Error(err)) + } + }() - if s.cfg.Request != nil { - for k, v := range s.cfg.Request { - r.Header.Add(k, v) + // if provided path to the dir, do not serve the dir, but pass the request to the worker + if finfo.IsDir() { + s.log.Debug("possible path to dir provided") + // pass request to the worker + next.ServeHTTP(w, r) + return } - } - if s.cfg.Response != nil { - for k, v := range s.cfg.Response { - w.Header().Set(k, v) + // set etag + if s.cfg.CalculateEtag { + SetEtag(w, calculateEtag(s.cfg.Weak, f, finfo.Name())) + } + + if s.cfg.Request != nil { + for k, v := range s.cfg.Request { + r.Header.Add(k, v) + } + } + + if s.cfg.Response != nil { + for k, v := range s.cfg.Response { + w.Header().Set(k, v) + } } - } - // we passed all checks - serve the file - http.ServeContent(w, r, finfo.Name(), finfo.ModTime(), f) + // we passed all checks - serve the file + http.ServeContent(w, r, finfo.Name(), finfo.ModTime(), f) + } } type ScannedFile struct { @@ -146,7 +148,7 @@ func createImmutableServer(s *Plugin) (FileServer, error) { return nil, err } - return func(s *Plugin, next http.Handler, w http.ResponseWriter, r *http.Request, fp string) { + return func(next http.Handler, w http.ResponseWriter, r *http.Request, fp string) { file, ok := files[fp] if ok { // else no such file, show error in logs only in debug mode @@ -192,6 +194,8 @@ type Plugin struct { forbiddenExtensions map[string]struct{} // opentelemetry prop propagation.TextMapPropagator + + fileServer FileServer } // Init must return configure service and return true if the service hasStatus enabled. Must return error in case of @@ -250,6 +254,21 @@ func (s *Plugin) Init(cfg Configurer, log Logger) error { s.prop = propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}, jprop.Jaeger{}) + var server FileServer + + if s.cfg.Immutable { + immutableServer, err := createImmutableServer(s) + if err != nil { + return errors.E(op, err) + } + + server = immutableServer + } else { + server = createMutableServer(s) + } + + s.fileServer = server + // at this point we have distinct allowed and forbidden hashmaps, also with alwaysServed return nil } @@ -260,17 +279,6 @@ func (s *Plugin) Name() string { // Middleware must return true if a request/response pair is handled within the middleware. func (s *Plugin) Middleware(next http.Handler) http.Handler { - var server FileServer = server - - if s.cfg.Immutable { - immutableServer, err := createImmutableServer(s) - if err != nil { - panic(err) - } - - server = immutableServer - } - // Define the http.HandlerFunc return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if val, ok := r.Context().Value(rrcontext.OtelTracerNameKey).(string); ok { @@ -327,7 +335,7 @@ func (s *Plugin) Middleware(next http.Handler) http.Handler { } // ok, file is not in the forbidden list - server(s, next, w, r, fp) + s.fileServer(next, w, r, fp) }) } From 6b3ecced983afdfafad056fdbd5f0803afeca7c4 Mon Sep 17 00:00:00 2001 From: Bozhidar Hristov Date: Fri, 9 Aug 2024 22:09:16 +0300 Subject: [PATCH 9/9] do not pass Plugin in server creators --- plugin.go | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/plugin.go b/plugin.go index 0b0ac7d..fd416c9 100644 --- a/plugin.go +++ b/plugin.go @@ -39,14 +39,14 @@ type Logger interface { type FileServer func(next http.Handler, w http.ResponseWriter, r *http.Request, fp string) -func createMutableServer(s *Plugin) FileServer { +func createMutableServer(root http.Dir, cfg *Config, logger *zap.Logger) FileServer { return func(next http.Handler, w http.ResponseWriter, r *http.Request, fp string) { // ok, file is not in the forbidden list // Stat it and get file info - f, err := s.root.Open(fp) + f, err := root.Open(fp) if err != nil { // else no such file, show error in logs only in debug mode - s.log.Debug("no such file or directory", zap.Error(err)) + logger.Debug("no such file or directory", zap.Error(err)) // pass request to the worker next.ServeHTTP(w, r) return @@ -57,7 +57,7 @@ func createMutableServer(s *Plugin) FileServer { finfo, err := f.Stat() if err != nil { // else no such file, show error in logs only in debug mode - s.log.Debug("no such file or directory", zap.Error(err)) + logger.Debug("no such file or directory", zap.Error(err)) // pass request to the worker next.ServeHTTP(w, r) return @@ -66,31 +66,31 @@ func createMutableServer(s *Plugin) FileServer { defer func() { err = f.Close() if err != nil { - s.log.Error("file close error", zap.Error(err)) + logger.Error("file close error", zap.Error(err)) } }() // if provided path to the dir, do not serve the dir, but pass the request to the worker if finfo.IsDir() { - s.log.Debug("possible path to dir provided") + logger.Debug("possible path to dir provided") // pass request to the worker next.ServeHTTP(w, r) return } // set etag - if s.cfg.CalculateEtag { - SetEtag(w, calculateEtag(s.cfg.Weak, f, finfo.Name())) + if cfg.CalculateEtag { + SetEtag(w, calculateEtag(cfg.Weak, f, finfo.Name())) } - if s.cfg.Request != nil { - for k, v := range s.cfg.Request { + if cfg.Request != nil { + for k, v := range cfg.Request { r.Header.Add(k, v) } } - if s.cfg.Response != nil { - for k, v := range s.cfg.Response { + if cfg.Response != nil { + for k, v := range cfg.Response { w.Header().Set(k, v) } } @@ -107,7 +107,7 @@ type ScannedFile struct { etag string } -func createImmutableServer(s *Plugin) (FileServer, error) { +func createImmutableServer(root http.Dir, cfg *Config, logger *zap.Logger) (FileServer, error) { var files map[string]ScannedFile var scanner func(path string, info os.FileInfo, err error) error @@ -120,7 +120,7 @@ func createImmutableServer(s *Plugin) (FileServer, error) { return filepath.Walk(info.Name(), scanner) } - file, openError := s.root.Open(path) + file, openError := root.Open(path) if openError != nil { return openError @@ -128,8 +128,8 @@ func createImmutableServer(s *Plugin) (FileServer, error) { var etag string - if s.cfg.CalculateEtag { - etag = calculateEtag(s.cfg.Weak, file, info.Name()) + if cfg.CalculateEtag { + etag = calculateEtag(cfg.Weak, file, info.Name()) } files[path] = ScannedFile{ @@ -142,7 +142,7 @@ func createImmutableServer(s *Plugin) (FileServer, error) { return nil } - err := filepath.Walk(s.cfg.Dir, scanner) + err := filepath.Walk(string(root), scanner) if err != nil { return nil, err @@ -152,7 +152,7 @@ func createImmutableServer(s *Plugin) (FileServer, error) { file, ok := files[fp] if ok { // else no such file, show error in logs only in debug mode - s.log.Debug("no such file or directory") + logger.Debug("no such file or directory") // pass request to the worker next.ServeHTTP(w, r) return @@ -163,14 +163,14 @@ func createImmutableServer(s *Plugin) (FileServer, error) { SetEtag(w, file.etag) } - if s.cfg.Request != nil { - for k, v := range s.cfg.Request { + if cfg.Request != nil { + for k, v := range cfg.Request { r.Header.Add(k, v) } } - if s.cfg.Response != nil { - for k, v := range s.cfg.Response { + if cfg.Response != nil { + for k, v := range cfg.Response { w.Header().Set(k, v) } } @@ -257,14 +257,14 @@ func (s *Plugin) Init(cfg Configurer, log Logger) error { var server FileServer if s.cfg.Immutable { - immutableServer, err := createImmutableServer(s) + immutableServer, err := createImmutableServer(s.root, s.cfg, s.log) if err != nil { return errors.E(op, err) } server = immutableServer } else { - server = createMutableServer(s) + server = createMutableServer(s.root, s.cfg, s.log) } s.fileServer = server