diff options
author | Valery Piashchynski <[email protected]> | 2021-05-14 09:19:00 +0300 |
---|---|---|
committer | Valery Piashchynski <[email protected]> | 2021-05-14 09:19:00 +0300 |
commit | f76b2392cd5b0c4e9f38736323a29b46f9451f0e (patch) | |
tree | b463fd9267323bec1ed8a63f871686ed99843611 /plugins/http | |
parent | fef96198ee6cc1f23bc869050944aa3071667ae7 (diff) | |
parent | e1ff9daead5033b537296ffb071e551b95af91ab (diff) |
Merge remote-tracking branch 'origin/master' into feature/websockets-plugin
# Conflicts:
# plugins/http/plugin.go
# plugins/static/etag.go
Diffstat (limited to 'plugins/http')
-rw-r--r-- | plugins/http/config/http.go | 21 | ||||
-rw-r--r-- | plugins/http/config/static.go | 58 | ||||
-rw-r--r-- | plugins/http/plugin.go | 59 | ||||
-rw-r--r-- | plugins/http/serve.go | 12 | ||||
-rw-r--r-- | plugins/http/static/etag.go | 67 | ||||
-rw-r--r-- | plugins/http/static/static.go | 88 |
6 files changed, 17 insertions, 288 deletions
diff --git a/plugins/http/config/http.go b/plugins/http/config/http.go index 59735e2e..8b63395f 100644 --- a/plugins/http/config/http.go +++ b/plugins/http/config/http.go @@ -33,9 +33,6 @@ type HTTP struct { // Uploads configures uploads configuration. Uploads *Uploads `mapstructure:"uploads"` - // static configuration - Static *Static `mapstructure:"static"` - // Pool configures worker pool. Pool *poolImpl.Config `mapstructure:"pool"` @@ -103,16 +100,6 @@ func (c *HTTP) InitDefaults() error { c.SSLConfig.Address = "127.0.0.1:443" } - // static files - if c.Static != nil { - if c.Static.Pattern == "" { - c.Static.Pattern = "/static/" - } - if c.Static.Dir == "" { - c.Static.Dir = "." - } - } - err := c.HTTP2Config.InitDefaults() if err != nil { return err @@ -189,13 +176,5 @@ func (c *HTTP) Valid() error { } } - // validate static - if c.Static != nil { - err := c.Static.Valid() - if err != nil { - return errors.E(op, err) - } - } - return nil } diff --git a/plugins/http/config/static.go b/plugins/http/config/static.go deleted file mode 100644 index 4b7b3a9b..00000000 --- a/plugins/http/config/static.go +++ /dev/null @@ -1,58 +0,0 @@ -package config - -import ( - "os" - - "github.com/spiral/errors" -) - -// Static describes file location and controls access to them. -type Static struct { - // Dir contains name of directory to control access to. - // Default - "." - Dir string - - // HTTP pattern, where to serve static files - // for example - `/static/`, `/my-files/static/`, etc - // Default - /static/ - Pattern string - - // CalculateEtag can be true/false and used to calculate etag for the static - CalculateEtag bool `mapstructure:"calculate_etag"` - - // Weak etag `W/` - Weak bool - - // forbid specifies list of file extensions which are forbidden for access. - // example: .php, .exe, .bat, .htaccess and etc. - Forbid []string - - // Allow specifies list of file extensions which are allowed for access. - // example: .php, .exe, .bat, .htaccess and etc. - Allow []string - - // Request headers to add to every static. - Request map[string]string - - // Response headers to add to every static. - Response map[string]string -} - -// Valid returns nil if config is valid. -func (c *Static) Valid() error { - const op = errors.Op("static_plugin_valid") - st, err := os.Stat(c.Dir) - if err != nil { - if os.IsNotExist(err) { - return errors.E(op, errors.Errorf("root directory '%s' does not exists", c.Dir)) - } - - return err - } - - if !st.IsDir() { - return errors.E(op, errors.Errorf("invalid root directory '%s'", c.Dir)) - } - - return nil -} diff --git a/plugins/http/plugin.go b/plugins/http/plugin.go index 3b9c12ea..2b68bbe5 100644 --- a/plugins/http/plugin.go +++ b/plugins/http/plugin.go @@ -5,9 +5,6 @@ import ( "fmt" "log" "net/http" - "os" - "path/filepath" - "strings" "sync" "github.com/hashicorp/go-multierror" @@ -16,11 +13,10 @@ import ( "github.com/spiral/roadrunner/v2/pkg/pool" "github.com/spiral/roadrunner/v2/pkg/process" "github.com/spiral/roadrunner/v2/pkg/worker" - handler "github.com/spiral/roadrunner/v2/pkg/worker_handler" "github.com/spiral/roadrunner/v2/plugins/config" "github.com/spiral/roadrunner/v2/plugins/http/attributes" httpConfig "github.com/spiral/roadrunner/v2/plugins/http/config" - "github.com/spiral/roadrunner/v2/plugins/http/static" + handler "github.com/spiral/roadrunner/v2/plugins/http/worker_handler" "github.com/spiral/roadrunner/v2/plugins/logger" "github.com/spiral/roadrunner/v2/plugins/server" "github.com/spiral/roadrunner/v2/plugins/status" @@ -136,7 +132,7 @@ func (s *Plugin) Serve() chan error { return errCh } -func (s *Plugin) serve(errCh chan error) { //nolint:gocognit +func (s *Plugin) serve(errCh chan error) { var err error const op = errors.Op("http_plugin_serve") s.pool, err = s.server.NewWorkerPool(context.Background(), pool.Config{ @@ -165,56 +161,11 @@ func (s *Plugin) serve(errCh chan error) { //nolint:gocognit s.handler.AddListener(s.logCallback) - // Create new HTTP Multiplexer - mux := http.NewServeMux() - - // if we have static, handler here, create a fileserver - if s.cfg.Static != nil { - h := http.FileServer(static.FS(s.cfg.Static)) - // Static files handler - mux.HandleFunc(s.cfg.Static.Pattern, func(w http.ResponseWriter, r *http.Request) { - if s.cfg.Static.Request != nil { - for k, v := range s.cfg.Static.Request { - r.Header.Add(k, v) - } - } - - if s.cfg.Static.Response != nil { - for k, v := range s.cfg.Static.Response { - w.Header().Set(k, v) - } - } - - // calculate etag for the resource - if s.cfg.Static.CalculateEtag { - // do not allow paths like ../../resource - // only specified folder and resources in it - // https://lgtm.com/rules/1510366186013/ - if strings.Contains(r.URL.Path, "..") { - w.WriteHeader(http.StatusForbidden) - return - } - f, errS := os.Open(filepath.Join(s.cfg.Static.Dir, r.URL.Path)) - if errS != nil { - s.log.Warn("error opening file to calculate the Etag", "provided path", r.URL.Path) - } - - // Set etag value to the ResponseWriter - static.SetEtag(s.cfg.Static, f, w) - } - - h.ServeHTTP(w, r) - }) - } - - // handle main route - mux.HandleFunc("/", s.ServeHTTP) - if s.cfg.EnableHTTP() { if s.cfg.EnableH2C() { - s.http = &http.Server{Handler: h2c.NewHandler(mux, &http2.Server{}), ErrorLog: s.stdLog} + s.http = &http.Server{Handler: h2c.NewHandler(s, &http2.Server{}), ErrorLog: s.stdLog} } else { - s.http = &http.Server{Handler: mux, ErrorLog: s.stdLog} + s.http = &http.Server{Handler: s, ErrorLog: s.stdLog} } } @@ -238,7 +189,7 @@ func (s *Plugin) serve(errCh chan error) { //nolint:gocognit } if s.cfg.EnableFCGI() { - s.fcgi = &http.Server{Handler: mux, ErrorLog: s.stdLog} + s.fcgi = &http.Server{Handler: s, ErrorLog: s.stdLog} } // start http, https and fcgi servers if requested in the config diff --git a/plugins/http/serve.go b/plugins/http/serve.go index 26fccf79..734860f5 100644 --- a/plugins/http/serve.go +++ b/plugins/http/serve.go @@ -231,12 +231,24 @@ func (s *Plugin) tlsAddr(host string, forcePort bool) string { return host } +// static plugin name +const static string = "static" + func applyMiddlewares(server *http.Server, middlewares map[string]Middleware, order []string, log logger.Logger) { for i := len(order) - 1; i >= 0; i-- { + // set static last in the row + if order[i] == static { + continue + } if mdwr, ok := middlewares[order[i]]; ok { server.Handler = mdwr.Middleware(server.Handler) } else { log.Warn("requested middleware does not exist", "requested", order[i]) } } + + // set static if exists + if mdwr, ok := middlewares[static]; ok { + server.Handler = mdwr.Middleware(server.Handler) + } } diff --git a/plugins/http/static/etag.go b/plugins/http/static/etag.go deleted file mode 100644 index c457b95e..00000000 --- a/plugins/http/static/etag.go +++ /dev/null @@ -1,67 +0,0 @@ -package static - -import ( - "hash/crc32" - "io" - "net/http" - "os" - - httpConfig "github.com/spiral/roadrunner/v2/plugins/http/config" - "github.com/spiral/roadrunner/v2/utils" -) - -const etag string = "Etag" - -// weak Etag prefix -var weakPrefix = []byte(`W/`) - -// CRC32 table -var crc32q = crc32.MakeTable(0x48D90782) - -func SetEtag(cfg *httpConfig.Static, f *os.File, w http.ResponseWriter) { - // read the file content - body, err := io.ReadAll(f) - if err != nil { - return - } - - // skip for 0 body - if len(body) == 0 { - return - } - - // preallocate - calculatedEtag := make([]byte, 0, 64) - - // write weak - if cfg.Weak { - calculatedEtag = append(calculatedEtag, weakPrefix...) - } - - calculatedEtag = append(calculatedEtag, '"') - calculatedEtag = appendUint(calculatedEtag, uint32(len(body))) - calculatedEtag = append(calculatedEtag, '-') - calculatedEtag = appendUint(calculatedEtag, crc32.Checksum(body, crc32q)) - calculatedEtag = append(calculatedEtag, '"') - - w.Header().Set(etag, utils.AsString(calculatedEtag)) -} - -// appendUint appends n to dst and returns the extended dst. -func appendUint(dst []byte, n uint32) []byte { - var b [20]byte - buf := b[:] - i := len(buf) - var q uint32 - for n >= 10 { - i-- - q = n / 10 - buf[i] = '0' + byte(n-q*10) - n = q - } - i-- - buf[i] = '0' + byte(n) - - dst = append(dst, buf[i:]...) - return dst -} diff --git a/plugins/http/static/static.go b/plugins/http/static/static.go deleted file mode 100644 index d0278466..00000000 --- a/plugins/http/static/static.go +++ /dev/null @@ -1,88 +0,0 @@ -package static - -import ( - "io/fs" - "net/http" - "path/filepath" - "strings" - - httpConfig "github.com/spiral/roadrunner/v2/plugins/http/config" -) - -type ExtensionFilter struct { - allowed map[string]struct{} - forbidden map[string]struct{} -} - -func NewExtensionFilter(allow, forbid []string) *ExtensionFilter { - ef := &ExtensionFilter{ - allowed: make(map[string]struct{}, len(allow)), - forbidden: make(map[string]struct{}, len(forbid)), - } - - for i := 0; i < len(forbid); i++ { - // skip empty lines - if forbid[i] == "" { - continue - } - ef.forbidden[forbid[i]] = struct{}{} - } - - for i := 0; i < len(allow); i++ { - // skip empty lines - if allow[i] == "" { - continue - } - ef.allowed[allow[i]] = struct{}{} - } - - // check if any forbidden items presented in the allowed - // if presented, delete such items from allowed - for k := range ef.allowed { - if _, ok := ef.forbidden[k]; ok { - delete(ef.allowed, k) - } - } - - return ef -} - -type FileSystem struct { - ef *ExtensionFilter - // embedded - http.FileSystem -} - -// Open wrapper around http.FileSystem Open method, name here is the name of the -func (f FileSystem) Open(name string) (http.File, error) { - file, err := f.FileSystem.Open(name) - if err != nil { - return nil, err - } - - fstat, err := file.Stat() - if err != nil { - return nil, fs.ErrNotExist - } - - if fstat.IsDir() { - return nil, fs.ErrPermission - } - - ext := strings.ToLower(filepath.Ext(fstat.Name())) - if _, ok := f.ef.forbidden[ext]; ok { - return nil, fs.ErrPermission - } - - // if file extension is allowed, append it to the FileInfo slice - if _, ok := f.ef.allowed[ext]; ok { - return file, nil - } - - return nil, fs.ErrNotExist -} - -// FS is a constructor for the http.FileSystem -func FS(config *httpConfig.Static) http.FileSystem { - return FileSystem{NewExtensionFilter(config.Allow, config.Forbid), http.Dir(config.Dir)} -} |