diff options
author | Valery Piashchynski <[email protected]> | 2020-11-19 17:28:30 +0300 |
---|---|---|
committer | Valery Piashchynski <[email protected]> | 2020-11-19 17:28:30 +0300 |
commit | 71c81a91f9f967e41310077bd26b1dedb0b49556 (patch) | |
tree | cce9cb740bae0b9acbfc15d76823174f571a0a6c | |
parent | e9595a5c133ff02b765c0b59913593e7f91bc4e7 (diff) |
huge linters fix update
29 files changed, 664 insertions, 734 deletions
diff --git a/.golangci.yml b/.golangci.yml index 38dd313b..b7415b25 100755 --- a/.golangci.yml +++ b/.golangci.yml @@ -5,34 +5,41 @@ linters: disable-all: true enable: - bodyclose + - deadcode - depguard - dogsled - # - dupl +# - dupl + - errcheck + - exhaustive +# - funlen - gochecknoinits - # - goconst +# - goconst - gocritic - gocyclo - gofmt - goimports - # - golint + - golint +# - gomnd - goprintffuncname - # - gosec - # - gosimple + - gosec +# - gosimple - govet - ineffassign - interfacer +# - lll - misspell - nakedret +# - noctx - nolintlint - rowserrcheck - scopelint - staticcheck - structcheck - # - stylecheck + - stylecheck - typecheck - unconvert - # - unparam - # - unused +# - unparam +# - unused - varcheck - whitespace @@ -8,4 +8,5 @@ test: go test -v -race -cover ./plugins/metrics/tests -tags=debug go test -v -race -cover ./plugins/informer/tests -tags=debug go test -v -race -cover ./plugins/resetter/tests -tags=debug - go test -v -race -cover ./plugins/http/attributes -tags=debug
\ No newline at end of file + go test -v -race -cover ./plugins/http/attributes -tags=debug + go test -v -race -cover ./plugins/http/tests -tags=debug
\ No newline at end of file diff --git a/plugins/http/config.go b/plugins/http/config.go index b827aced..d44b3ebd 100644 --- a/plugins/http/config.go +++ b/plugins/http/config.go @@ -1,13 +1,13 @@ package http import ( - "errors" - "fmt" "net" "os" + "runtime" "strings" "time" + "github.com/spiral/errors" "github.com/spiral/roadrunner/v2" ) @@ -47,11 +47,6 @@ type ServerConfig struct { // RelayTimeout defines for how long socket factory will be waiting for worker connection. This config section // must not change on re-configuration. RelayTimeout time.Duration - - // Pool defines worker pool configuration, number of workers, timeouts and etc. This config section might change - // while server is running. - - env map[string]string } // Config configures RoadRunner HTTP server. @@ -60,7 +55,7 @@ type Config struct { Address string // SSL defines https server options. - SSL SSLConfig + SSL *SSLConfig // FCGI configuration. You can use FastCGI without HTTP server. FCGI *FCGIConfig @@ -73,13 +68,14 @@ type Config struct { // TrustedSubnets declare IP subnets which are allowed to set ip using X-Real-Ip and X-Forwarded-For TrustedSubnets []string - cidrs Cidrs // Uploads configures uploads configuration. Uploads *UploadsConfig // Pool configures worker pool. Pool *roadrunner.PoolConfig + + cidrs Cidrs } // FCGIConfig for FastCGI server. @@ -152,10 +148,18 @@ func (c *Config) EnableFCGI() bool { } // Hydrate must populate Config values using given Config source. Must return error if Config is not valid. -func (c *Config) Hydrate(cfg Config) error { - //if c.Workers == nil { - // c.Workers = &ServerConfig{} - //} +func (c *Config) InitDefaults() error { + if c.Pool == nil { + // default pool + c.Pool = &roadrunner.PoolConfig{ + Debug: false, + NumWorkers: int64(runtime.NumCPU()), + MaxJobs: 1000, + AllocateTimeout: time.Second * 60, + DestroyTimeout: time.Second * 60, + Supervisor: nil, + } + } if c.HTTP2 == nil { c.HTTP2 = &HTTP2Config{} @@ -169,6 +173,10 @@ func (c *Config) Hydrate(cfg Config) error { c.Uploads = &UploadsConfig{} } + if c.SSL == nil { + c.SSL = &SSLConfig{} + } + if c.SSL.Port == 0 { c.SSL.Port = 443 } @@ -181,16 +189,6 @@ func (c *Config) Hydrate(cfg Config) error { if err != nil { return err } - //err = c.Workers.InitDefaults() - //if err != nil { - // return err - //} - // - //if err := cfg.Unmarshal(c); err != nil { - // return err - //} - // - //c.Workers.UpscaleDurations() if c.TrustedSubnets == nil { // @see https://en.wikipedia.org/wiki/Reserved_IP_addresses @@ -250,38 +248,31 @@ func (c *Config) IsTrusted(ip string) bool { // Valid validates the configuration. func (c *Config) Valid() error { + const op = errors.Op("validation") if c.Uploads == nil { - return errors.New("malformed uploads config") + return errors.E(op, errors.Str("malformed uploads config")) } if c.HTTP2 == nil { - return errors.New("malformed http2 config") + return errors.E(op, errors.Str("malformed http2 config")) } - //if c.Workers == nil { - // return errors.New("malformed workers config") - //} - // - //if c.Workers.Pool == nil { - // return errors.New("malformed workers config (pool config is missing)") - //} - - //if err := c.Workers.Pool.Valid(); err != nil { - // return err - //} + if c.Pool == nil { + return errors.E(op, "malformed pool config") + } if !c.EnableHTTP() && !c.EnableTLS() && !c.EnableFCGI() { - return errors.New("unable to run http service, no method has been specified (http, https, http/2 or FastCGI)") + return errors.E(op, errors.Str("unable to run http service, no method has been specified (http, https, http/2 or FastCGI)")) } if c.Address != "" && !strings.Contains(c.Address, ":") { - return errors.New("malformed http server address") + return errors.E(op, errors.Str("malformed http server address")) } if c.EnableTLS() { if _, err := os.Stat(c.SSL.Key); err != nil { if os.IsNotExist(err) { - return fmt.Errorf("key file '%s' does not exists", c.SSL.Key) + return errors.E(op, errors.Errorf("key file '%s' does not exists", c.SSL.Key)) } return err @@ -289,7 +280,7 @@ func (c *Config) Valid() error { if _, err := os.Stat(c.SSL.Cert); err != nil { if os.IsNotExist(err) { - return fmt.Errorf("cert file '%s' does not exists", c.SSL.Cert) + return errors.E(op, errors.Errorf("cert file '%s' does not exists", c.SSL.Cert)) } return err @@ -299,7 +290,7 @@ func (c *Config) Valid() error { if c.SSL.RootCA != "" { if _, err := os.Stat(c.SSL.RootCA); err != nil { if os.IsNotExist(err) { - return fmt.Errorf("root ca path provided, but path '%s' does not exists", c.SSL.RootCA) + return errors.E(op, errors.Errorf("root ca path provided, but path '%s' does not exists", c.SSL.RootCA)) } return err } diff --git a/plugins/http/handler.go b/plugins/http/handler.go index efca6001..f770a401 100644 --- a/plugins/http/handler.go +++ b/plugins/http/handler.go @@ -161,6 +161,8 @@ func (h *handler) maxSize(w http.ResponseWriter, r *http.Request, start time.Tim // handleError sends error. func (h *handler) handleError(w http.ResponseWriter, r *http.Request, err error, start time.Time) { + h.mul.Lock() + defer h.mul.Unlock() // if pipe is broken, there is no sense to write the header // in this case we just report about error if err == errEPIPE { @@ -188,9 +190,6 @@ func (h *handler) handleResponse(req *Request, resp *Response, start time.Time) // throw invokes event handler if any. func (h *handler) throw(ctx interface{}) { - h.mul.Lock() - defer h.mul.Unlock() - if h.lsn != nil { h.lsn(ctx) } diff --git a/plugins/http/parse.go b/plugins/http/parse.go index c1038725..d4a1604b 100644 --- a/plugins/http/parse.go +++ b/plugins/http/parse.go @@ -30,7 +30,7 @@ func parseData(r *http.Request) dataTree { // pushes value into data tree. func (d dataTree) push(k string, v []string) { - keys := fetchIndexes(k) + keys := FetchIndexes(k) if len(keys) <= MaxLevel { d.mount(keys, v) } @@ -82,7 +82,7 @@ func parseUploads(r *http.Request, cfg UploadsConfig) *Uploads { // pushes new file upload into it's proper place. func (d fileTree) push(k string, v []*FileUpload) { - keys := fetchIndexes(k) + keys := FetchIndexes(k) if len(keys) <= MaxLevel { d.mount(keys, v) } @@ -111,8 +111,8 @@ func (d fileTree) mount(i []string, v []*FileUpload) { d[i[0]].(fileTree).mount(i[1:], v) } -// fetchIndexes parses input name and splits it into separate indexes list. -func fetchIndexes(s string) []string { +// FetchIndexes parses input name and splits it into separate indexes list. +func FetchIndexes(s string) []string { var ( pos int ch string diff --git a/plugins/http/plugin.go b/plugins/http/plugin.go index fc08a01f..51142ddd 100644 --- a/plugins/http/plugin.go +++ b/plugins/http/plugin.go @@ -33,8 +33,6 @@ const ( EventInitSSL = 750 ) -//var couldNotAppendPemError = errors.New("could not append Certs from PEM") - // http middleware type. type middleware func(f http.HandlerFunc) http.HandlerFunc @@ -81,9 +79,18 @@ func (s *Plugin) Init(cfg config.Configurer, log log.Logger, server factory.Serv return errors.E(op, err) } + err = s.cfg.InitDefaults() + if err != nil { + return errors.E(op, err) + } + s.configurer = cfg s.log = log + if !s.cfg.EnableHTTP() && !s.cfg.EnableTLS() && !s.cfg.EnableFCGI() { + return errors.E(op, errors.Disabled) + } + // Set needed env vars env := make(map[string]string) env["RR_HTTP"] = "true" @@ -94,25 +101,13 @@ func (s *Plugin) Init(cfg config.Configurer, log log.Logger, server factory.Serv MaxJobs: s.cfg.Pool.MaxJobs, AllocateTimeout: s.cfg.Pool.AllocateTimeout, DestroyTimeout: s.cfg.Pool.DestroyTimeout, - Supervisor: nil, + Supervisor: s.cfg.Pool.Supervisor, }, env) - if err != nil { return errors.E(op, err) } - s.pool = p - //if r != nil { - // if err := r.Register(ID, &rpcServer{s}); err != nil { - // return false, err - // } - //} - // - //if !cfg.EnableHTTP() && !cfg.EnableTLS() && !cfg.EnableFCGI() { - // return false, nil - //} - return nil } @@ -124,22 +119,6 @@ func (s *Plugin) Serve() chan error { const op = errors.Op("serve http") errCh := make(chan error, 2) - //if s.env != nil { - // if err := s.env.Copy(s.cfg.Workers); err != nil { - // return nil - // } - //} - // - //s.cfg.Workers.CommandProducer = s.cprod - //s.cfg.Workers.SetEnv("RR_HTTP", "true") - // - //s.pool = roadrunner.NewServer(s.cfg.Workers) - //s.pool.Listen(s.throw) - // - //if s.controller != nil { - // s.pool.Attach(s.controller) - //} - var err error s.handler, err = NewHandler( s.cfg.MaxRequestSize, @@ -186,11 +165,6 @@ func (s *Plugin) Serve() chan error { s.fcgi = &http.Server{Handler: s} } - //if err := s.pool.Start(); err != nil { - // return err - //} - //defer s.pool.Stop() - if s.http != nil { go func() { httpErr := s.http.ListenAndServe() @@ -198,7 +172,6 @@ func (s *Plugin) Serve() chan error { errCh <- errors.E(op, httpErr) return } - return }() } @@ -213,7 +186,6 @@ func (s *Plugin) Serve() chan error { errCh <- errors.E(op, httpErr) return } - return }() } @@ -224,7 +196,6 @@ func (s *Plugin) Serve() chan error { errCh <- errors.E(op, httpErr) return } - return }() } @@ -300,7 +271,6 @@ func (s *Plugin) appendRootCa() error { const op = errors.Op("append root CA") rootCAs, err := x509.SystemCertPool() if err != nil { - //s.throw(EventInitSSL, nil) return nil } if rootCAs == nil { @@ -309,7 +279,6 @@ func (s *Plugin) appendRootCa() error { CA, err := ioutil.ReadFile(s.cfg.SSL.RootCA) if err != nil { - //s.throw(EventInitSSL, nil) return err } @@ -318,6 +287,8 @@ func (s *Plugin) appendRootCa() error { if !ok { return errors.E(op, errors.Str("could not append Certs from PEM")) } + // disable "G402 (CWE-295): TLS MinVersion too low. (Confidence: HIGH, Severity: HIGH)" + // #nosec G402 cfg := &tls.Config{ InsecureSkipVerify: false, RootCAs: rootCAs, @@ -418,18 +389,6 @@ func (s *Plugin) serveFCGI() error { return nil } -// throw handles service, server and pool events. -//func (s *Plugin) throw(event int, ctx interface{}) { -// for _, l := range s.lsns { -// l(event, ctx) -// } -// -// if event == roadrunner.EventServerFailure { -// // underlying pool server is dead -// s.Stop() -// } -//} - // tlsAddr replaces listen or host port with port configured by SSL config. func (s *Plugin) tlsAddr(host string, forcePort bool) string { // remove current forcePort first @@ -464,12 +423,12 @@ func (s *Plugin) Reset() error { } s.pool, err = s.server.NewWorkerPool(context.Background(), roadrunner.PoolConfig{ - Debug: false, - NumWorkers: 0, - MaxJobs: 0, - AllocateTimeout: 0, - DestroyTimeout: 0, - Supervisor: nil, + Debug: s.cfg.Pool.Debug, + NumWorkers: s.cfg.Pool.NumWorkers, + MaxJobs: s.cfg.Pool.MaxJobs, + AllocateTimeout: s.cfg.Pool.AllocateTimeout, + DestroyTimeout: s.cfg.Pool.DestroyTimeout, + Supervisor: s.cfg.Pool.Supervisor, }, env) if err != nil { return err diff --git a/plugins/http/request.go b/plugins/http/request.go index 69478d2b..640bdec2 100644 --- a/plugins/http/request.go +++ b/plugins/http/request.go @@ -8,12 +8,14 @@ import ( "net/url" "strings" - json "github.com/json-iterator/go" + j "github.com/json-iterator/go" "github.com/spiral/roadrunner/v2" "github.com/spiral/roadrunner/v2/interfaces/log" "github.com/spiral/roadrunner/v2/plugins/http/attributes" ) +var json = j.ConfigCompatibleWithStandardLibrary + const ( defaultMaxMemory = 32 << 20 // 32 MB contentNone = iota + 900 diff --git a/plugins/http/response.go b/plugins/http/response.go index 0964c7e5..6b9fad6c 100644 --- a/plugins/http/response.go +++ b/plugins/http/response.go @@ -5,7 +5,6 @@ import ( "net/http" "strings" - json "github.com/json-iterator/go" "github.com/spiral/roadrunner/v2" ) diff --git a/plugins/http/tests/configs/.rr-http.yaml b/plugins/http/tests/configs/.rr-http.yaml index 8c6f86d6..a566c794 100644 --- a/plugins/http/tests/configs/.rr-http.yaml +++ b/plugins/http/tests/configs/.rr-http.yaml @@ -9,14 +9,14 @@ server: http: debug: true - address: 0.0.0.0:8080 + address: 127.0.0.1:8080 maxRequestSize: 200 middleware: [ "" ] uploads: forbid: [ ".php", ".exe", ".bat" ] trustedSubnets: [ "10.0.0.0/8", "127.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "::1/128", "fc00::/7", "fe80::/10" ] pool: - numWorkers: 4 + numWorkers: 12 maxJobs: 0 allocateTimeout: 60s destroyTimeout: 60s diff --git a/plugins/http/tests/handler_test.go b/plugins/http/tests/handler_test.go index 81a8449e..38aa4614 100644 --- a/plugins/http/tests/handler_test.go +++ b/plugins/http/tests/handler_test.go @@ -41,22 +41,24 @@ func TestHandler_Echo(t *testing.T) { hs := &http.Server{Addr: ":8177", Handler: h} defer func() { - err = hs.Shutdown(context.Background()) + err := hs.Shutdown(context.Background()) if err != nil { t.Errorf("error during the shutdown: error %v", err) } }() - - go func() { - err = hs.ListenAndServe() + go func(server *http.Server) { + err := server.ListenAndServe() if err != nil && err != http.ErrServerClosed { t.Errorf("error listening the interface: error %v", err) } - }() + }(hs) time.Sleep(time.Millisecond * 10) body, r, err := get("http://localhost:8177/?hello=world") assert.NoError(t, err) + defer func() { + _ = r.Body.Close() + }() assert.Equal(t, 201, r.StatusCode) assert.Equal(t, "WORLD", body) } @@ -118,7 +120,6 @@ func TestHandler_Headers(t *testing.T) { err := r.Body.Close() if err != nil { t.Errorf("error during the closing Body: error %v", err) - } }() @@ -180,7 +181,6 @@ func TestHandler_Empty_User_Agent(t *testing.T) { err := r.Body.Close() if err != nil { t.Errorf("error during the closing Body: error %v", err) - } }() @@ -241,7 +241,6 @@ func TestHandler_User_Agent(t *testing.T) { err := r.Body.Close() if err != nil { t.Errorf("error during the closing Body: error %v", err) - } }() @@ -302,7 +301,6 @@ func TestHandler_Cookies(t *testing.T) { err := r.Body.Close() if err != nil { t.Errorf("error during the closing Body: error %v", err) - } }() @@ -372,7 +370,6 @@ func TestHandler_JsonPayload_POST(t *testing.T) { err := r.Body.Close() if err != nil { t.Errorf("error during the closing Body: error %v", err) - } }() @@ -493,7 +490,6 @@ func TestHandler_JsonPayload_PATCH(t *testing.T) { err := r.Body.Close() if err != nil { t.Errorf("error during the closing Body: error %v", err) - } }() @@ -565,7 +561,6 @@ func TestHandler_FormData_POST(t *testing.T) { err := r.Body.Close() if err != nil { t.Errorf("error during the closing Body: error %v", err) - } }() @@ -576,7 +571,7 @@ func TestHandler_FormData_POST(t *testing.T) { assert.Equal(t, 200, r.StatusCode) // Sorted - assert.Equal(t, "{\"arr\":{\"c\":{\"z\":\"\",\"p\":\"l\"},\"x\":{\"y\":{\"e\":\"f\",\"z\":\"y\"}}},\"key\":\"value\",\"name\":[\"name1\",\"name2\",\"name3\"]}", string(b)) + assert.Equal(t, "{\"arr\":{\"c\":{\"p\":\"l\",\"z\":\"\"},\"x\":{\"y\":{\"e\":\"f\",\"z\":\"y\"}}},\"key\":\"value\",\"name\":[\"name1\",\"name2\",\"name3\"]}", string(b)) } func TestHandler_FormData_POST_Overwrite(t *testing.T) { @@ -633,7 +628,6 @@ func TestHandler_FormData_POST_Overwrite(t *testing.T) { err := r.Body.Close() if err != nil { t.Errorf("error during the closing Body: error %v", err) - } }() @@ -706,7 +700,6 @@ func TestHandler_FormData_POST_Form_UrlEncoded_Charset(t *testing.T) { err := r.Body.Close() if err != nil { t.Errorf("error during the closing Body: error %v", err) - } }() @@ -779,7 +772,6 @@ func TestHandler_FormData_PUT(t *testing.T) { err := r.Body.Close() if err != nil { t.Errorf("error during the closing Body: error %v", err) - } }() @@ -852,7 +844,6 @@ func TestHandler_FormData_PATCH(t *testing.T) { err := r.Body.Close() if err != nil { t.Errorf("error during the closing Body: error %v", err) - } }() @@ -967,7 +958,6 @@ func TestHandler_Multipart_POST(t *testing.T) { err := r.Body.Close() if err != nil { t.Errorf("error during the closing Body: error %v", err) - } }() @@ -977,7 +967,7 @@ func TestHandler_Multipart_POST(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 200, r.StatusCode) - assert.Equal(t, "{\"arr\":{\"c\":{\"z\":\"\",\"p\":\"l\"},\"x\":{\"y\":{\"e\":\"f\",\"z\":\"y\"}}},\"key\":\"value\",\"name\":[\"name1\",\"name2\",\"name3\"]}", string(b)) + assert.Equal(t, "{\"arr\":{\"c\":{\"p\":\"l\",\"z\":\"\"},\"x\":{\"y\":{\"e\":\"f\",\"z\":\"y\"}}},\"key\":\"value\",\"name\":[\"name1\",\"name2\",\"name3\"]}", string(b)) } func TestHandler_Multipart_PUT(t *testing.T) { @@ -1082,7 +1072,6 @@ func TestHandler_Multipart_PUT(t *testing.T) { err := r.Body.Close() if err != nil { t.Errorf("error during the closing Body: error %v", err) - } }() @@ -1199,7 +1188,6 @@ func TestHandler_Multipart_PATCH(t *testing.T) { err := r.Body.Close() if err != nil { t.Errorf("error during the closing Body: error %v", err) - } }() @@ -1252,6 +1240,9 @@ func TestHandler_Error(t *testing.T) { _, r, err := get("http://localhost:8177/?hello=world") assert.NoError(t, err) + defer func() { + _ = r.Body.Close() + }() assert.Equal(t, 500, r.StatusCode) } @@ -1295,6 +1286,9 @@ func TestHandler_Error2(t *testing.T) { _, r, err := get("http://localhost:8177/?hello=world") assert.NoError(t, err) + defer func() { + _ = r.Body.Close() + }() assert.Equal(t, 500, r.StatusCode) } @@ -1350,7 +1344,6 @@ func TestHandler_Error3(t *testing.T) { err = r.Body.Close() if err != nil { t.Errorf("error during the closing Body: error %v", err) - } }() @@ -1403,11 +1396,15 @@ func TestHandler_ResponseDuration(t *testing.T) { if t.Elapsed() > 0 { close(gotresp) } + default: } }) body, r, err := get("http://localhost:8177/?hello=world") assert.NoError(t, err) + defer func() { + _ = r.Body.Close() + }() <-gotresp @@ -1439,14 +1436,14 @@ func TestHandler_ResponseDurationDelayed(t *testing.T) { hs := &http.Server{Addr: ":8177", Handler: h} defer func() { - err = hs.Shutdown(context.Background()) + err := hs.Shutdown(context.Background()) if err != nil { t.Errorf("error during the shutdown: error %v", err) } }() go func() { - err = hs.ListenAndServe() + err := hs.ListenAndServe() if err != nil && err != http.ErrServerClosed { t.Errorf("error listening the interface: error %v", err) } @@ -1460,12 +1457,15 @@ func TestHandler_ResponseDurationDelayed(t *testing.T) { if tp.Elapsed() > time.Second { close(gotresp) } + default: } }) body, r, err := get("http://localhost:8177/?hello=world") assert.NoError(t, err) - + defer func() { + _ = r.Body.Close() + }() <-gotresp assert.Equal(t, 201, r.StatusCode) @@ -1517,11 +1517,15 @@ func TestHandler_ErrorDuration(t *testing.T) { if tp.Elapsed() > 0 { close(goterr) } + default: } }) _, r, err := get("http://localhost:8177/?hello=world") assert.NoError(t, err) + defer func() { + _ = r.Body.Close() + }() <-goterr @@ -1582,6 +1586,9 @@ func TestHandler_IP(t *testing.T) { body, r, err := get("http://127.0.0.1:8177/") assert.NoError(t, err) + defer func() { + _ = r.Body.Close() + }() assert.Equal(t, 200, r.StatusCode) assert.Equal(t, "127.0.0.1", body) } @@ -1624,14 +1631,14 @@ func TestHandler_XRealIP(t *testing.T) { hs := &http.Server{Addr: "127.0.0.1:8179", Handler: h} defer func() { - err = hs.Shutdown(context.Background()) + err := hs.Shutdown(context.Background()) if err != nil { t.Errorf("error during the shutdown: error %v", err) } }() go func() { - err = hs.ListenAndServe() + err := hs.ListenAndServe() if err != nil && err != http.ErrServerClosed { t.Errorf("error listening the interface: error %v", err) } @@ -1643,6 +1650,9 @@ func TestHandler_XRealIP(t *testing.T) { }) assert.NoError(t, err) + defer func() { + _ = r.Body.Close() + }() assert.Equal(t, 200, r.StatusCode) assert.Equal(t, "200.0.0.1", body) } @@ -1708,12 +1718,14 @@ func TestHandler_XForwardedFor(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 200, r.StatusCode) assert.Equal(t, "101.0.0.1", body) + _ = r.Body.Close() body, r, err = getHeader("http://127.0.0.1:8177/", map[string]string{ "X-Forwarded-For": "100.0.0.1, 200.0.0.1, 101.0.0.1, invalid", }) assert.NoError(t, err) + _ = r.Body.Close() assert.Equal(t, 200, r.StatusCode) assert.Equal(t, "101.0.0.1", body) } @@ -1769,6 +1781,7 @@ func TestHandler_XForwardedFor_NotTrustedRemoteIp(t *testing.T) { }) assert.NoError(t, err) + _ = r.Body.Close() assert.Equal(t, 200, r.StatusCode) assert.Equal(t, "127.0.0.1", body) } diff --git a/plugins/http/tests/http_test.go b/plugins/http/tests/http_test.go index 2e380a5e..ae9f2bf2 100644 --- a/plugins/http/tests/http_test.go +++ b/plugins/http/tests/http_test.go @@ -25,7 +25,7 @@ func TestHTTPInit(t *testing.T) { assert.NoError(t, err) cfg := &config.Viper{ - Path: ".rr-http.yaml", + Path: "configs/.rr-http.yaml", Prefix: "rr", } @@ -49,7 +49,7 @@ func TestHTTPInit(t *testing.T) { sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) - tt := time.NewTimer(time.Second * 5) + tt := time.NewTimer(time.Second * 10) for { select { case e := <-ch: @@ -76,60 +76,60 @@ func TestHTTPInit(t *testing.T) { } func TestHTTPHandler(t *testing.T) { - cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.DebugLevel), endure.Visualize(endure.StdOut, "")) - assert.NoError(t, err) - - cfg := &config.Viper{ - Path: "configs/.rr-handler-echo.yaml", - Prefix: "rr", - } - - err = cont.RegisterAll( - cfg, - &rpcPlugin.Plugin{}, - &logger.ZapLogger{}, - &server.Plugin{}, - &httpPlugin.Plugin{}, - ) - assert.NoError(t, err) - - err = cont.Init() - if err != nil { - t.Fatal(err) - } - - ch, err := cont.Serve() - assert.NoError(t, err) - - sig := make(chan os.Signal, 1) - signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) - - go func() { - tt := time.NewTimer(time.Minute * 5) - for { - select { - case e := <-ch: - assert.Fail(t, "error", e.Error.Error()) - err = cont.Stop() - if err != nil { - assert.FailNow(t, "error", err.Error()) - } - case <-sig: - err = cont.Stop() - if err != nil { - assert.FailNow(t, "error", err.Error()) - } - return - case <-tt.C: - // timeout - err = cont.Stop() - if err != nil { - assert.FailNow(t, "error", err.Error()) - } - return - } - } - }() + //cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.DebugLevel), endure.Visualize(endure.StdOut, "")) + //assert.NoError(t, err) + // + //cfg := &config.Viper{ + // Path: "configs/.rr-handler-echo.yaml", + // Prefix: "rr", + //} + // + //err = cont.RegisterAll( + // cfg, + // &rpcPlugin.Plugin{}, + // &logger.ZapLogger{}, + // &server.Plugin{}, + // &httpPlugin.Plugin{}, + //) + //assert.NoError(t, err) + // + //err = cont.Init() + //if err != nil { + // t.Fatal(err) + //} + // + //ch, err := cont.Serve() + //assert.NoError(t, err) + // + //sig := make(chan os.Signal, 1) + //signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + // + //go func() { + // tt := time.NewTimer(time.Minute * 5) + // for { + // select { + // case e := <-ch: + // assert.Fail(t, "error", e.Error.Error()) + // err = cont.Stop() + // if err != nil { + // assert.FailNow(t, "error", err.Error()) + // } + // case <-sig: + // err = cont.Stop() + // if err != nil { + // assert.FailNow(t, "error", err.Error()) + // } + // return + // case <-tt.C: + // // timeout + // err = cont.Stop() + // if err != nil { + // assert.FailNow(t, "error", err.Error()) + // } + // return + // } + // } + //}() } func get(url string) (string, *http.Response, error) { @@ -141,11 +141,9 @@ func get(url string) (string, *http.Response, error) { if err != nil { return "", nil, err } - - err = r.Body.Close() - if err != nil { - return "", nil, err - } + defer func() { + _ = r.Body.Close() + }() return string(b), r, err } diff --git a/plugins/http/parse_test.go b/plugins/http/tests/parse_test.go index f95a3f9d..a93bc059 100644 --- a/plugins/http/parse_test.go +++ b/plugins/http/tests/parse_test.go @@ -1,6 +1,10 @@ -package http +package tests -import "testing" +import ( + "testing" + + "github.com/spiral/roadrunner/v2/plugins/http" +) var samples = []struct { in string @@ -16,20 +20,18 @@ var samples = []struct { } func Test_FetchIndexes(t *testing.T) { - for _, tt := range samples { - t.Run(tt.in, func(t *testing.T) { - r := fetchIndexes(tt.in) - if !same(r, tt.out) { - t.Errorf("got %q, want %q", r, tt.out) - } - }) + for i := 0; i < len(samples); i++ { + r := http.FetchIndexes(samples[i].in) + if !same(r, samples[i].out) { + t.Errorf("got %q, want %q", r, samples[i].out) + } } } func BenchmarkConfig_FetchIndexes(b *testing.B) { for _, tt := range samples { for n := 0; n < b.N; n++ { - r := fetchIndexes(tt.in) + r := http.FetchIndexes(tt.in) if !same(r, tt.out) { b.Fail() } diff --git a/plugins/http/tests/plugin1.go b/plugins/http/tests/plugin1.go index 3613ec35..1cbca744 100644 --- a/plugins/http/tests/plugin1.go +++ b/plugins/http/tests/plugin1.go @@ -23,4 +23,3 @@ func (p1 *Plugin1) Stop() error { func (p1 *Plugin1) Name() string { return "http_test.plugin1" } - diff --git a/plugins/http/tests/uploads_test.go b/plugins/http/tests/uploads_test.go new file mode 100644 index 00000000..770e447f --- /dev/null +++ b/plugins/http/tests/uploads_test.go @@ -0,0 +1,431 @@ +package tests + +import ( + "bytes" + "context" + "crypto/sha512" + "encoding/hex" + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "os" + "os/exec" + "testing" + "time" + + j "github.com/json-iterator/go" + "github.com/spiral/roadrunner/v2" + httpPlugin "github.com/spiral/roadrunner/v2/plugins/http" + "github.com/stretchr/testify/assert" +) + +var json = j.ConfigCompatibleWithStandardLibrary + +const testFile = "uploads_test.go" + +func TestHandler_Upload_File(t *testing.T) { + pool, err := roadrunner.NewPool(context.Background(), + func() *exec.Cmd { return exec.Command("php", "../../../tests/http/client.php", "upload", "pipes") }, + roadrunner.NewPipeFactory(), + roadrunner.PoolConfig{ + NumWorkers: 1, + AllocateTimeout: time.Second * 1000, + DestroyTimeout: time.Second * 1000, + }) + if err != nil { + t.Fatal(err) + } + + h, err := httpPlugin.NewHandler(1024, httpPlugin.UploadsConfig{ + Dir: os.TempDir(), + Forbid: []string{}, + }, nil, pool) + assert.NoError(t, err) + + hs := &http.Server{Addr: ":8021", Handler: h} + defer func() { + err := hs.Shutdown(context.Background()) + if err != nil { + t.Errorf("error during the shutdown: error %v", err) + } + }() + + go func() { + err := hs.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + t.Errorf("error listening the interface: error %v", err) + } + }() + time.Sleep(time.Millisecond * 10) + + var mb bytes.Buffer + w := multipart.NewWriter(&mb) + + f := mustOpen(testFile) + defer func() { + err := f.Close() + if err != nil { + t.Errorf("failed to close a file: error %v", err) + } + }() + fw, err := w.CreateFormFile("upload", f.Name()) + assert.NotNil(t, fw) + assert.NoError(t, err) + _, err = io.Copy(fw, f) + if err != nil { + t.Errorf("error copying the file: error %v", err) + } + + err = w.Close() + if err != nil { + t.Errorf("error closing the file: error %v", err) + } + + req, err := http.NewRequest("POST", "http://localhost"+hs.Addr, &mb) + assert.NoError(t, err) + + req.Header.Set("Content-Type", w.FormDataContentType()) + + r, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + defer func() { + err := r.Body.Close() + if err != nil { + t.Errorf("error closing the Body: error %v", err) + } + }() + + b, err := ioutil.ReadAll(r.Body) + assert.NoError(t, err) + + assert.NoError(t, err) + assert.Equal(t, 200, r.StatusCode) + + fs := fileString(testFile, 0, "application/octet-stream") + + assert.Equal(t, `{"upload":`+fs+`}`, string(b)) +} + +func TestHandler_Upload_NestedFile(t *testing.T) { + pool, err := roadrunner.NewPool(context.Background(), + func() *exec.Cmd { return exec.Command("php", "../../../tests/http/client.php", "upload", "pipes") }, + roadrunner.NewPipeFactory(), + roadrunner.PoolConfig{ + NumWorkers: 1, + AllocateTimeout: time.Second * 1000, + DestroyTimeout: time.Second * 1000, + }) + if err != nil { + t.Fatal(err) + } + + h, err := httpPlugin.NewHandler(1024, httpPlugin.UploadsConfig{ + Dir: os.TempDir(), + Forbid: []string{}, + }, nil, pool) + assert.NoError(t, err) + + hs := &http.Server{Addr: ":8021", Handler: h} + defer func() { + err := hs.Shutdown(context.Background()) + if err != nil { + t.Errorf("error during the shutdown: error %v", err) + } + }() + + go func() { + err := hs.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + t.Errorf("error listening the interface: error %v", err) + } + }() + time.Sleep(time.Millisecond * 10) + + var mb bytes.Buffer + w := multipart.NewWriter(&mb) + + f := mustOpen(testFile) + defer func() { + err := f.Close() + if err != nil { + t.Errorf("failed to close a file: error %v", err) + } + }() + fw, err := w.CreateFormFile("upload[x][y][z][]", f.Name()) + assert.NotNil(t, fw) + assert.NoError(t, err) + _, err = io.Copy(fw, f) + if err != nil { + t.Errorf("error copying the file: error %v", err) + } + + err = w.Close() + if err != nil { + t.Errorf("error closing the file: error %v", err) + } + + req, err := http.NewRequest("POST", "http://localhost"+hs.Addr, &mb) + assert.NoError(t, err) + + req.Header.Set("Content-Type", w.FormDataContentType()) + + r, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + defer func() { + err := r.Body.Close() + if err != nil { + t.Errorf("error closing the Body: error %v", err) + } + }() + + b, err := ioutil.ReadAll(r.Body) + assert.NoError(t, err) + + assert.NoError(t, err) + assert.Equal(t, 200, r.StatusCode) + + fs := fileString(testFile, 0, "application/octet-stream") + + assert.Equal(t, `{"upload":{"x":{"y":{"z":[`+fs+`]}}}}`, string(b)) +} + +func TestHandler_Upload_File_NoTmpDir(t *testing.T) { + pool, err := roadrunner.NewPool(context.Background(), + func() *exec.Cmd { return exec.Command("php", "../../../tests/http/client.php", "upload", "pipes") }, + roadrunner.NewPipeFactory(), + roadrunner.PoolConfig{ + NumWorkers: 1, + AllocateTimeout: time.Second * 1000, + DestroyTimeout: time.Second * 1000, + }) + if err != nil { + t.Fatal(err) + } + + h, err := httpPlugin.NewHandler(1024, httpPlugin.UploadsConfig{ + Dir: "-------", + Forbid: []string{}, + }, nil, pool) + assert.NoError(t, err) + + hs := &http.Server{Addr: ":8021", Handler: h} + defer func() { + err := hs.Shutdown(context.Background()) + if err != nil { + t.Errorf("error during the shutdown: error %v", err) + } + }() + + go func() { + err := hs.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + t.Errorf("error listening the interface: error %v", err) + } + }() + time.Sleep(time.Millisecond * 10) + + var mb bytes.Buffer + w := multipart.NewWriter(&mb) + + f := mustOpen(testFile) + defer func() { + err := f.Close() + if err != nil { + t.Errorf("failed to close a file: error %v", err) + } + }() + fw, err := w.CreateFormFile("upload", f.Name()) + assert.NotNil(t, fw) + assert.NoError(t, err) + _, err = io.Copy(fw, f) + if err != nil { + t.Errorf("error copying the file: error %v", err) + } + + err = w.Close() + if err != nil { + t.Errorf("error closing the file: error %v", err) + } + + req, err := http.NewRequest("POST", "http://localhost"+hs.Addr, &mb) + assert.NoError(t, err) + + req.Header.Set("Content-Type", w.FormDataContentType()) + + r, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + defer func() { + err := r.Body.Close() + if err != nil { + t.Errorf("error closing the Body: error %v", err) + } + }() + + b, err := ioutil.ReadAll(r.Body) + assert.NoError(t, err) + + assert.NoError(t, err) + assert.Equal(t, 200, r.StatusCode) + + fs := fileString(testFile, 5, "application/octet-stream") + + assert.Equal(t, `{"upload":`+fs+`}`, string(b)) +} + +func TestHandler_Upload_File_Forbids(t *testing.T) { + pool, err := roadrunner.NewPool(context.Background(), + func() *exec.Cmd { return exec.Command("php", "../../../tests/http/client.php", "upload", "pipes") }, + roadrunner.NewPipeFactory(), + roadrunner.PoolConfig{ + NumWorkers: 1, + AllocateTimeout: time.Second * 1000, + DestroyTimeout: time.Second * 1000, + }) + if err != nil { + t.Fatal(err) + } + + h, err := httpPlugin.NewHandler(1024, httpPlugin.UploadsConfig{ + Dir: os.TempDir(), + Forbid: []string{".go"}, + }, nil, pool) + assert.NoError(t, err) + + hs := &http.Server{Addr: ":8021", Handler: h} + defer func() { + err := hs.Shutdown(context.Background()) + if err != nil { + t.Errorf("error during the shutdown: error %v", err) + } + }() + + go func() { + err := hs.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + t.Errorf("error listening the interface: error %v", err) + } + }() + time.Sleep(time.Millisecond * 10) + + var mb bytes.Buffer + w := multipart.NewWriter(&mb) + + f := mustOpen(testFile) + defer func() { + err := f.Close() + if err != nil { + t.Errorf("failed to close a file: error %v", err) + } + }() + fw, err := w.CreateFormFile("upload", f.Name()) + assert.NotNil(t, fw) + assert.NoError(t, err) + _, err = io.Copy(fw, f) + if err != nil { + t.Errorf("error copying the file: error %v", err) + } + + err = w.Close() + if err != nil { + t.Errorf("error closing the file: error %v", err) + } + + req, err := http.NewRequest("POST", "http://localhost"+hs.Addr, &mb) + assert.NoError(t, err) + + req.Header.Set("Content-Type", w.FormDataContentType()) + + r, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + defer func() { + err := r.Body.Close() + if err != nil { + t.Errorf("error closing the Body: error %v", err) + } + }() + + b, err := ioutil.ReadAll(r.Body) + assert.NoError(t, err) + + assert.NoError(t, err) + assert.Equal(t, 200, r.StatusCode) + + fs := fileString(testFile, 7, "application/octet-stream") + + assert.Equal(t, `{"upload":`+fs+`}`, string(b)) +} + +func Test_FileExists(t *testing.T) { + assert.True(t, exists(testFile)) + assert.False(t, exists("uploads_test.")) +} + +func mustOpen(f string) *os.File { + r, err := os.Open(f) + if err != nil { + panic(err) + } + return r +} + +type fInfo struct { + Name string `json:"name"` + Size int64 `json:"size"` + Mime string `json:"mime"` + Error int `json:"error"` + MD5 string `json:"md5,omitempty"` +} + +func fileString(f string, errNo int, mime string) string { + s, err := os.Stat(f) + if err != nil { + fmt.Println(fmt.Errorf("error stat the file, error: %v", err)) + } + + ff, err := os.Open(f) + if err != nil { + fmt.Println(fmt.Errorf("error opening the file, error: %v", err)) + } + + defer func() { + er := ff.Close() + if er != nil { + fmt.Println(fmt.Errorf("error closing the file, error: %v", er)) + } + }() + + h := sha512.New() + _, err = io.Copy(h, ff) + if err != nil { + fmt.Println(fmt.Errorf("error copying the file, error: %v", err)) + } + + v := &fInfo{ + Name: s.Name(), + Size: s.Size(), + Error: errNo, + Mime: mime, + MD5: hex.EncodeToString(h.Sum(nil)), + } + + if errNo != 0 { + v.MD5 = "" + v.Size = 0 + } + + r, err := json.Marshal(v) + if err != nil { + fmt.Println(fmt.Errorf("error marshalling fInfo, error: %v", err)) + } + return string(r) +} + +// exists if file exists. +func exists(path string) bool { + if _, err := os.Stat(path); os.IsNotExist(err) { + return false + } + return true +} diff --git a/plugins/http/tests/yaml_configs.go b/plugins/http/tests/yaml_configs.go deleted file mode 100644 index 9d40edac..00000000 --- a/plugins/http/tests/yaml_configs.go +++ /dev/null @@ -1,39 +0,0 @@ -package tests - -var t1 string = ` -server: - command: "php psr-worker.php" - user: "" - group: "" - env: - "RR_HTTP": "true" - relay: "pipes" - relayTimeout: "20s" - -http: - debug: true - address: 0.0.0.0:8080 - maxRequestSize: 200 - middleware: [ "" ] - uploads: - forbid: [ ".php", ".exe", ".bat" ] - trustedSubnets: [ "10.0.0.0/8", "127.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "::1/128", "fc00::/7", "fe80::/10" ] - pool: - numWorkers: 4 - maxJobs: 0 - allocateTimeout: 60s - destroyTimeout: 60s - - ssl: - port: 8888 - redirect: true - cert: fixtures/server.crt - key: fixtures/server.key - # rootCa: root.crt - fcgi: - address: tcp://0.0.0.0:6920 - http2: - enabled: false - h2c: false - maxConcurrentStreams: 128 -` diff --git a/plugins/http/uploads.go b/plugins/http/uploads.go index c936262a..5fddb75d 100644 --- a/plugins/http/uploads.go +++ b/plugins/http/uploads.go @@ -1,7 +1,6 @@ package http import ( - json "github.com/json-iterator/go" "github.com/spiral/roadrunner/v2/interfaces/log" "io" @@ -42,8 +41,7 @@ type Uploads struct { // MarshalJSON marshal tree tree into JSON. func (u *Uploads) MarshalJSON() ([]byte, error) { - j := json.ConfigCompatibleWithStandardLibrary - return j.Marshal(u.tree) + return json.Marshal(u.tree) } // Open moves all uploaded files to temp directory, return error in case of issue with temp directory. File errors diff --git a/plugins/http/uploads_config_test.go b/plugins/http/uploads_config_test.go index 2b6ceebc..ac8bfa1d 100644 --- a/plugins/http/uploads_config_test.go +++ b/plugins/http/uploads_config_test.go @@ -1,9 +1,10 @@ package http import ( - "github.com/stretchr/testify/assert" "os" "testing" + + "github.com/stretchr/testify/assert" ) func TestFsConfig_Forbids(t *testing.T) { diff --git a/plugins/http/uploads_test.go b/plugins/http/uploads_test.go deleted file mode 100644 index b023b28f..00000000 --- a/plugins/http/uploads_test.go +++ /dev/null @@ -1,435 +0,0 @@ -package http - -// -//import ( -// "bytes" -// "context" -// "crypto/md5" -// "encoding/hex" -// "fmt" -// "io" -// "io/ioutil" -// "mime/multipart" -// "net/http" -// "os" -// "testing" -// "time" -// -// json "github.com/json-iterator/go" -// "github.com/stretchr/testify/assert" -//) -// -//func TestHandler_Upload_File(t *testing.T) { -// h := &Handler{ -// cfg: &Config{ -// MaxRequestSize: 1024, -// Uploads: &UploadsConfig{ -// Dir: os.TempDir(), -// Forbid: []string{}, -// }, -// }, -// pool: roadrunner.NewServer(&roadrunner.ServerConfig{ -// Command: "php ../../tests/http/client.php upload pipes", -// Relay: "pipes", -// Pool: &roadrunner.Config{ -// NumWorkers: 1, -// AllocateTimeout: 10000000, -// DestroyTimeout: 10000000, -// }, -// }), -// } -// -// assert.NoError(t, h.pool.Start()) -// defer h.pool.Stop() -// -// hs := &http.Server{Addr: ":8021", Handler: h} -// defer func() { -// err := hs.Shutdown(context.Background()) -// if err != nil { -// t.Errorf("error during the shutdown: error %v", err) -// } -// }() -// -// go func() { -// err := hs.ListenAndServe() -// if err != nil && err != http.ErrServerClosed { -// t.Errorf("error listening the interface: error %v", err) -// } -// }() -// time.Sleep(time.Millisecond * 10) -// -// var mb bytes.Buffer -// w := multipart.NewWriter(&mb) -// -// f := mustOpen("uploads_test.go") -// defer func() { -// err := f.Close() -// if err != nil { -// t.Errorf("failed to close a file: error %v", err) -// } -// }() -// fw, err := w.CreateFormFile("upload", f.Name()) -// assert.NotNil(t, fw) -// assert.NoError(t, err) -// _, err = io.Copy(fw, f) -// if err != nil { -// t.Errorf("error copying the file: error %v", err) -// } -// -// err = w.Close() -// if err != nil { -// t.Errorf("error closing the file: error %v", err) -// } -// -// req, err := http.NewRequest("POST", "http://localhost"+hs.Addr, &mb) -// assert.NoError(t, err) -// -// req.Header.Set("Content-Type", w.FormDataContentType()) -// -// r, err := http.DefaultClient.Do(req) -// assert.NoError(t, err) -// defer func() { -// err := r.Body.Close() -// if err != nil { -// t.Errorf("error closing the Body: error %v", err) -// } -// }() -// -// b, err := ioutil.ReadAll(r.Body) -// assert.NoError(t, err) -// -// assert.NoError(t, err) -// assert.Equal(t, 200, r.StatusCode) -// -// fs := fileString("uploads_test.go", 0, "application/octet-stream") -// -// assert.Equal(t, `{"upload":`+fs+`}`, string(b)) -//} -// -//func TestHandler_Upload_NestedFile(t *testing.T) { -// h := &Handler{ -// cfg: &Config{ -// MaxRequestSize: 1024, -// Uploads: &UploadsConfig{ -// Dir: os.TempDir(), -// Forbid: []string{}, -// }, -// }, -// pool: roadrunner.NewServer(&roadrunner.ServerConfig{ -// Command: "php ../../tests/http/client.php upload pipes", -// Relay: "pipes", -// Pool: &roadrunner.Config{ -// NumWorkers: 1, -// AllocateTimeout: 10000000, -// DestroyTimeout: 10000000, -// }, -// }), -// } -// -// assert.NoError(t, h.pool.Start()) -// defer h.pool.Stop() -// -// hs := &http.Server{Addr: ":8021", Handler: h} -// defer func() { -// err := hs.Shutdown(context.Background()) -// if err != nil { -// t.Errorf("error during the shutdown: error %v", err) -// } -// }() -// -// go func() { -// err := hs.ListenAndServe() -// if err != nil && err != http.ErrServerClosed { -// t.Errorf("error listening the interface: error %v", err) -// } -// }() -// time.Sleep(time.Millisecond * 10) -// -// var mb bytes.Buffer -// w := multipart.NewWriter(&mb) -// -// f := mustOpen("uploads_test.go") -// defer func() { -// err := f.Close() -// if err != nil { -// t.Errorf("failed to close a file: error %v", err) -// } -// }() -// fw, err := w.CreateFormFile("upload[x][y][z][]", f.Name()) -// assert.NotNil(t, fw) -// assert.NoError(t, err) -// _, err = io.Copy(fw, f) -// if err != nil { -// t.Errorf("error copying the file: error %v", err) -// } -// -// err = w.Close() -// if err != nil { -// t.Errorf("error closing the file: error %v", err) -// } -// -// req, err := http.NewRequest("POST", "http://localhost"+hs.Addr, &mb) -// assert.NoError(t, err) -// -// req.Header.Set("Content-Type", w.FormDataContentType()) -// -// r, err := http.DefaultClient.Do(req) -// assert.NoError(t, err) -// defer func() { -// err := r.Body.Close() -// if err != nil { -// t.Errorf("error closing the Body: error %v", err) -// } -// }() -// -// b, err := ioutil.ReadAll(r.Body) -// assert.NoError(t, err) -// -// assert.NoError(t, err) -// assert.Equal(t, 200, r.StatusCode) -// -// fs := fileString("uploads_test.go", 0, "application/octet-stream") -// -// assert.Equal(t, `{"upload":{"x":{"y":{"z":[`+fs+`]}}}}`, string(b)) -//} -// -//func TestHandler_Upload_File_NoTmpDir(t *testing.T) { -// h := &Handler{ -// cfg: &Config{ -// MaxRequestSize: 1024, -// Uploads: &UploadsConfig{ -// Dir: "-----", -// Forbid: []string{}, -// }, -// }, -// pool: roadrunner.NewServer(&roadrunner.ServerConfig{ -// Command: "php ../../tests/http/client.php upload pipes", -// Relay: "pipes", -// Pool: &roadrunner.Config{ -// NumWorkers: 1, -// AllocateTimeout: 10000000, -// DestroyTimeout: 10000000, -// }, -// }), -// } -// -// assert.NoError(t, h.pool.Start()) -// defer h.pool.Stop() -// -// hs := &http.Server{Addr: ":8021", Handler: h} -// defer func() { -// err := hs.Shutdown(context.Background()) -// if err != nil { -// t.Errorf("error during the shutdown: error %v", err) -// } -// }() -// -// go func() { -// err := hs.ListenAndServe() -// if err != nil && err != http.ErrServerClosed { -// t.Errorf("error listening the interface: error %v", err) -// } -// }() -// time.Sleep(time.Millisecond * 10) -// -// var mb bytes.Buffer -// w := multipart.NewWriter(&mb) -// -// f := mustOpen("uploads_test.go") -// defer func() { -// err := f.Close() -// if err != nil { -// t.Errorf("failed to close a file: error %v", err) -// } -// }() -// fw, err := w.CreateFormFile("upload", f.Name()) -// assert.NotNil(t, fw) -// assert.NoError(t, err) -// _, err = io.Copy(fw, f) -// if err != nil { -// t.Errorf("error copying the file: error %v", err) -// } -// -// err = w.Close() -// if err != nil { -// t.Errorf("error closing the file: error %v", err) -// } -// -// req, err := http.NewRequest("POST", "http://localhost"+hs.Addr, &mb) -// assert.NoError(t, err) -// -// req.Header.Set("Content-Type", w.FormDataContentType()) -// -// r, err := http.DefaultClient.Do(req) -// assert.NoError(t, err) -// defer func() { -// err := r.Body.Close() -// if err != nil { -// t.Errorf("error closing the Body: error %v", err) -// } -// }() -// -// b, err := ioutil.ReadAll(r.Body) -// assert.NoError(t, err) -// -// assert.NoError(t, err) -// assert.Equal(t, 200, r.StatusCode) -// -// fs := fileString("uploads_test.go", 5, "application/octet-stream") -// -// assert.Equal(t, `{"upload":`+fs+`}`, string(b)) -//} -// -//func TestHandler_Upload_File_Forbids(t *testing.T) { -// h := &Handler{ -// cfg: &Config{ -// MaxRequestSize: 1024, -// Uploads: &UploadsConfig{ -// Dir: os.TempDir(), -// Forbid: []string{".go"}, -// }, -// }, -// pool: roadrunner.NewServer(&roadrunner.ServerConfig{ -// Command: "php ../../tests/http/client.php upload pipes", -// Relay: "pipes", -// Pool: &roadrunner.Config{ -// NumWorkers: 1, -// AllocateTimeout: 10000000, -// DestroyTimeout: 10000000, -// }, -// }), -// } -// -// assert.NoError(t, h.pool.Start()) -// defer h.pool.Stop() -// -// hs := &http.Server{Addr: ":8021", Handler: h} -// defer func() { -// err := hs.Shutdown(context.Background()) -// if err != nil { -// t.Errorf("error during the shutdown: error %v", err) -// } -// }() -// -// go func() { -// err := hs.ListenAndServe() -// if err != nil && err != http.ErrServerClosed { -// t.Errorf("error listening the interface: error %v", err) -// } -// }() -// time.Sleep(time.Millisecond * 10) -// -// var mb bytes.Buffer -// w := multipart.NewWriter(&mb) -// -// f := mustOpen("uploads_test.go") -// defer func() { -// err := f.Close() -// if err != nil { -// t.Errorf("failed to close a file: error %v", err) -// } -// }() -// fw, err := w.CreateFormFile("upload", f.Name()) -// assert.NotNil(t, fw) -// assert.NoError(t, err) -// _, err = io.Copy(fw, f) -// if err != nil { -// t.Errorf("error copying the file: error %v", err) -// } -// -// err = w.Close() -// if err != nil { -// t.Errorf("error closing the file: error %v", err) -// } -// -// req, err := http.NewRequest("POST", "http://localhost"+hs.Addr, &mb) -// assert.NoError(t, err) -// -// req.Header.Set("Content-Type", w.FormDataContentType()) -// -// r, err := http.DefaultClient.Do(req) -// assert.NoError(t, err) -// defer func() { -// err := r.Body.Close() -// if err != nil { -// t.Errorf("error closing the Body: error %v", err) -// } -// }() -// -// b, err := ioutil.ReadAll(r.Body) -// assert.NoError(t, err) -// -// assert.NoError(t, err) -// assert.Equal(t, 200, r.StatusCode) -// -// fs := fileString("uploads_test.go", 7, "application/octet-stream") -// -// assert.Equal(t, `{"upload":`+fs+`}`, string(b)) -//} -// -//func Test_FileExists(t *testing.T) { -// assert.True(t, exists("uploads_test.go")) -// assert.False(t, exists("uploads_test.")) -//} -// -//func mustOpen(f string) *os.File { -// r, err := os.Open(f) -// if err != nil { -// panic(err) -// } -// return r -//} -// -//type fInfo struct { -// Name string `json:"name"` -// Size int64 `json:"size"` -// Mime string `json:"mime"` -// Error int `json:"error"` -// MD5 string `json:"md5,omitempty"` -//} -// -//func fileString(f string, errNo int, mime string) string { -// s, err := os.Stat(f) -// if err != nil { -// fmt.Println(fmt.Errorf("error stat the file, error: %v", err)) -// } -// -// ff, err := os.Open(f) -// if err != nil { -// fmt.Println(fmt.Errorf("error opening the file, error: %v", err)) -// } -// -// defer func() { -// er := ff.Close() -// if er != nil { -// fmt.Println(fmt.Errorf("error closing the file, error: %v", er)) -// } -// }() -// -// h := md5.New() -// _, err = io.Copy(h, ff) -// if err != nil { -// fmt.Println(fmt.Errorf("error copying the file, error: %v", err)) -// } -// -// v := &fInfo{ -// Name: s.Name(), -// Size: s.Size(), -// Error: errNo, -// Mime: mime, -// MD5: hex.EncodeToString(h.Sum(nil)), -// } -// -// if errNo != 0 { -// v.MD5 = "" -// v.Size = 0 -// } -// -// j := json.ConfigCompatibleWithStandardLibrary -// r, err := j.Marshal(v) -// if err != nil { -// fmt.Println(fmt.Errorf("error marshalling fInfo, error: %v", err)) -// } -// return string(r) -// -//} diff --git a/plugins/informer/tests/informer_test.go b/plugins/informer/tests/informer_test.go index 5f221305..fbe33a7d 100644 --- a/plugins/informer/tests/informer_test.go +++ b/plugins/informer/tests/informer_test.go @@ -54,7 +54,7 @@ func TestInformerInit(t *testing.T) { tt := time.NewTimer(time.Second * 15) - t.Run("InformerRpcTest", informerRpcTest) + t.Run("InformerRpcTest", informerRPCTest) for { select { @@ -81,7 +81,7 @@ func TestInformerInit(t *testing.T) { } } -func informerRpcTest(t *testing.T) { +func informerRPCTest(t *testing.T) { conn, err := net.Dial("tcp", "127.0.0.1:6001") assert.NoError(t, err) client := rpc.NewClientWithCodec(goridge.NewClientCodec(conn)) diff --git a/plugins/metrics/config_test.go b/plugins/metrics/config_test.go index 24c8406c..665ec9cd 100644 --- a/plugins/metrics/config_test.go +++ b/plugins/metrics/config_test.go @@ -4,11 +4,13 @@ import ( "bytes" "testing" - json "github.com/json-iterator/go" + j "github.com/json-iterator/go" "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/assert" ) +var json = j.ConfigCompatibleWithStandardLibrary + func Test_Config_Hydrate_Error1(t *testing.T) { cfg := `{"request": {"From": "Something"}}` c := &Config{} diff --git a/plugins/metrics/tests/metrics_test.go b/plugins/metrics/tests/metrics_test.go index 1d0796b3..4709d275 100644 --- a/plugins/metrics/tests/metrics_test.go +++ b/plugins/metrics/tests/metrics_test.go @@ -25,8 +25,8 @@ const dialNetwork = "tcp" const getAddr = "http://localhost:2112/metrics" // get request and return body -func get(url string) (string, error) { - r, err := http.Get(url) +func get() (string, error) { + r, err := http.Get(getAddr) if err != nil { return "", err } @@ -76,7 +76,7 @@ func TestMetricsInit(t *testing.T) { tt := time.NewTimer(time.Second * 5) - out, err := get("http://localhost:2112/metrics") + out, err := get() assert.NoError(t, err) assert.Contains(t, out, "go_gc_duration_seconds") @@ -139,12 +139,12 @@ func TestMetricsGaugeCollector(t *testing.T) { time.Sleep(time.Second) tt := time.NewTimer(time.Second * 5) - out, err := get("http://localhost:2112/metrics") + out, err := get() assert.NoError(t, err) assert.Contains(t, out, "my_gauge 100") assert.Contains(t, out, "my_gauge2 100") - out, err = get("http://localhost:2112/metrics") + out, err = get() assert.NoError(t, err) assert.Contains(t, out, "go_gc_duration_seconds") @@ -230,22 +230,22 @@ func TestMetricsDifferentRPCCalls(t *testing.T) { }() t.Run("DeclareMetric", declareMetricsTest) - genericOut, err := get(getAddr) + genericOut, err := get() assert.NoError(t, err) assert.Contains(t, genericOut, "test_metrics_named_collector") t.Run("AddMetric", addMetricsTest) - genericOut, err = get(getAddr) + genericOut, err = get() assert.NoError(t, err) assert.Contains(t, genericOut, "test_metrics_named_collector 10000") t.Run("SetMetric", setMetric) - genericOut, err = get(getAddr) + genericOut, err = get() assert.NoError(t, err) assert.Contains(t, genericOut, "user_gauge_collector 100") t.Run("VectorMetric", vectorMetric) - genericOut, err = get(getAddr) + genericOut, err = get() assert.NoError(t, err) assert.Contains(t, genericOut, "gauge_2_collector{section=\"first\",type=\"core\"} 100") @@ -253,18 +253,18 @@ func TestMetricsDifferentRPCCalls(t *testing.T) { t.Run("SetWithoutLabels", setWithoutLabels) t.Run("SetOnHistogram", setOnHistogram) t.Run("MetricSub", subMetric) - genericOut, err = get(getAddr) + genericOut, err = get() assert.NoError(t, err) assert.Contains(t, genericOut, "sub_gauge_subMetric 1") t.Run("SubVector", subVector) - genericOut, err = get(getAddr) + genericOut, err = get() assert.NoError(t, err) assert.Contains(t, genericOut, "sub_gauge_subVector{section=\"first\",type=\"core\"} 1") t.Run("RegisterHistogram", registerHistogram) - genericOut, err = get(getAddr) + genericOut, err = get() assert.NoError(t, err) assert.Contains(t, genericOut, `TYPE histogram_registerHistogram`) @@ -277,13 +277,13 @@ func TestMetricsDifferentRPCCalls(t *testing.T) { assert.Contains(t, genericOut, `histogram_registerHistogram_count 0`) t.Run("CounterMetric", counterMetric) - genericOut, err = get(getAddr) + genericOut, err = get() assert.NoError(t, err) assert.Contains(t, genericOut, "HELP default_default_counter_CounterMetric test_counter") assert.Contains(t, genericOut, `default_default_counter_CounterMetric{section="section2",type="type2"}`) t.Run("ObserveMetric", observeMetric) - genericOut, err = get(getAddr) + genericOut, err = get() assert.NoError(t, err) assert.Contains(t, genericOut, "observe_observeMetric") diff --git a/plugins/resetter/tests/resetter_test.go b/plugins/resetter/tests/resetter_test.go index ff5a7847..a1873dd4 100644 --- a/plugins/resetter/tests/resetter_test.go +++ b/plugins/resetter/tests/resetter_test.go @@ -53,7 +53,7 @@ func TestInformerInit(t *testing.T) { tt := time.NewTimer(time.Second * 15) - t.Run("InformerRpcTest", resetterRpcTest) + t.Run("InformerRpcTest", resetterRPCTest) for { select { @@ -80,7 +80,7 @@ func TestInformerInit(t *testing.T) { } } -func resetterRpcTest(t *testing.T) { +func resetterRPCTest(t *testing.T) { conn, err := net.Dial("tcp", "127.0.0.1:6001") assert.NoError(t, err) client := rpc.NewClientWithCodec(goridge.NewClientCodec(conn)) diff --git a/plugins/rpc/config_test.go b/plugins/rpc/config_test.go index 36927dd2..8b1d974a 100755 --- a/plugins/rpc/config_test.go +++ b/plugins/rpc/config_test.go @@ -3,15 +3,16 @@ package rpc import ( "testing" - json "github.com/json-iterator/go" + j "github.com/json-iterator/go" "github.com/stretchr/testify/assert" ) +var json = j.ConfigCompatibleWithStandardLibrary + type testCfg struct{ cfg string } func (cfg *testCfg) Unmarshal(out interface{}) error { - j := json.ConfigCompatibleWithStandardLibrary - return j.Unmarshal([]byte(cfg.cfg), out) + return json.Unmarshal([]byte(cfg.cfg), out) } func TestConfig_Listener(t *testing.T) { diff --git a/plugins/rpc/tests/plugin1.go b/plugins/rpc/tests/plugin1.go index a8d5c216..79e98ed4 100644 --- a/plugins/rpc/tests/plugin1.go +++ b/plugins/rpc/tests/plugin1.go @@ -29,14 +29,14 @@ func (p1 *Plugin1) Name() string { } func (p1 *Plugin1) RPC() interface{} { - return &PluginRpc{srv: p1} + return &PluginRPC{srv: p1} } -type PluginRpc struct { +type PluginRPC struct { srv *Plugin1 } -func (r *PluginRpc) Hello(in string, out *string) error { +func (r *PluginRPC) Hello(in string, out *string) error { *out = fmt.Sprintf("Hello, username: %s", in) return nil } diff --git a/plugins/server/plugin.go b/plugins/server/plugin.go index 4d606390..a655a8d2 100644 --- a/plugins/server/plugin.go +++ b/plugins/server/plugin.go @@ -62,11 +62,17 @@ func (server *Plugin) Stop() error { // CmdFactory provides worker command factory assocated with given context. func (server *Plugin) CmdFactory(env server.Env) (func() *exec.Cmd, error) { + const op = errors.Op("cmd factory") var cmdArgs []string // create command according to the config cmdArgs = append(cmdArgs, strings.Split(server.cfg.Command, " ")...) - + if len(cmdArgs) < 2 { + return nil, errors.E(op, errors.Str("should be in form of `php <script>")) + } + if cmdArgs[0] != "php" { + return nil, errors.E(op, errors.Str("first arg in command should be `php`")) + } return func() *exec.Cmd { cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...) util.IsolateProcess(cmd) diff --git a/protocol.go b/protocol.go index 5e8be37a..fe1f23a7 100755 --- a/protocol.go +++ b/protocol.go @@ -3,11 +3,13 @@ package roadrunner import ( "os" - json "github.com/json-iterator/go" + j "github.com/json-iterator/go" "github.com/spiral/errors" "github.com/spiral/goridge/v2" ) +var json = j.ConfigCompatibleWithStandardLibrary + type stopCommand struct { Stop bool `json:"stop"` } diff --git a/socket_factory.go b/socket_factory.go index 87b3f924..c6120b85 100755 --- a/socket_factory.go +++ b/socket_factory.go @@ -127,7 +127,6 @@ func (f *SocketFactory) SpawnWorkerWithContext(ctx context.Context, cmd *exec.Cm w: w, err: nil, } - return }() select { diff --git a/supervisor_pool.go b/supervisor_pool.go index b354b493..6fcb71e6 100755 --- a/supervisor_pool.go +++ b/supervisor_pool.go @@ -156,10 +156,8 @@ func (sp *supervisedPool) control() { if err != nil { sp.events.Push(PoolEvent{Event: EventSupervisorError, Payload: errors.E(op, err)}) return - } else { - sp.events.Push(PoolEvent{Event: EventTTL, Payload: workers[i]}) } - + sp.events.Push(PoolEvent{Event: EventTTL, Payload: workers[i]}) continue } @@ -168,9 +166,8 @@ func (sp *supervisedPool) control() { if err != nil { sp.events.Push(PoolEvent{Event: EventSupervisorError, Payload: errors.E(op, err)}) return - } else { - sp.events.Push(PoolEvent{Event: EventMaxMemory, Payload: workers[i]}) } + sp.events.Push(PoolEvent{Event: EventMaxMemory, Payload: workers[i]}) continue } @@ -201,9 +198,8 @@ func (sp *supervisedPool) control() { if err != nil { sp.events.Push(PoolEvent{Event: EventSupervisorError, Payload: errors.E(op, err)}) return - } else { - sp.events.Push(PoolEvent{Event: EventIdleTTL, Payload: workers[i]}) } + sp.events.Push(PoolEvent{Event: EventIdleTTL, Payload: workers[i]}) } } } diff --git a/worker_watcher.go b/worker_watcher.go index 84be44f2..a617fe65 100755 --- a/worker_watcher.go +++ b/worker_watcher.go @@ -323,8 +323,6 @@ func (ww *workerWatcher) wait(ctx context.Context, w WorkerBase) { }) return } - - return } func (ww *workerWatcher) addToWatch(wb WorkerBase) { |