diff options
Diffstat (limited to 'service/headers')
-rw-r--r-- | service/headers/config.go | 41 | ||||
-rw-r--r-- | service/headers/config_test.go | 30 | ||||
-rw-r--r-- | service/headers/service.go | 113 | ||||
-rw-r--r-- | service/headers/service_test.go | 340 |
4 files changed, 0 insertions, 524 deletions
diff --git a/service/headers/config.go b/service/headers/config.go deleted file mode 100644 index f9af1df2..00000000 --- a/service/headers/config.go +++ /dev/null @@ -1,41 +0,0 @@ -package headers - -import "github.com/spiral/roadrunner/service" - -// Config declares headers service configuration. -type Config struct { - // CORS settings. - CORS *CORSConfig - - // Request headers to add to every payload send to PHP. - Request map[string]string - - // Response headers to add to every payload generated by PHP. - Response map[string]string -} - -// CORSConfig headers configuration. -type CORSConfig struct { - // AllowedOrigin: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin - AllowedOrigin string - - // AllowedHeaders: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers - AllowedHeaders string - - // AllowedMethods: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods - AllowedMethods string - - // AllowCredentials https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials - AllowCredentials *bool - - // ExposeHeaders: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers - ExposedHeaders string - - // MaxAge of CORS headers in seconds/ - MaxAge int -} - -// Hydrate service config. -func (c *Config) Hydrate(cfg service.Config) error { - return cfg.Unmarshal(c) -} diff --git a/service/headers/config_test.go b/service/headers/config_test.go deleted file mode 100644 index 6ea02f67..00000000 --- a/service/headers/config_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package headers - -import ( - json "github.com/json-iterator/go" - "github.com/spiral/roadrunner/service" - "github.com/stretchr/testify/assert" - "testing" -) - -type mockCfg struct{ cfg string } - -func (cfg *mockCfg) Get(name string) service.Config { return nil } -func (cfg *mockCfg) Unmarshal(out interface{}) error { - j := json.ConfigCompatibleWithStandardLibrary - return j.Unmarshal([]byte(cfg.cfg), out) -} - -func Test_Config_Hydrate_Error1(t *testing.T) { - cfg := &mockCfg{`{"request": {"From": "Something"}}`} - c := &Config{} - - assert.NoError(t, c.Hydrate(cfg)) -} - -func Test_Config_Hydrate_Error2(t *testing.T) { - cfg := &mockCfg{`{"dir": "/dir/"`} - c := &Config{} - - assert.Error(t, c.Hydrate(cfg)) -} diff --git a/service/headers/service.go b/service/headers/service.go deleted file mode 100644 index 429219d7..00000000 --- a/service/headers/service.go +++ /dev/null @@ -1,113 +0,0 @@ -package headers - -import ( - rrhttp "github.com/spiral/roadrunner/service/http" - "net/http" - "strconv" -) - -// ID contains default service name. -const ID = "headers" - -// Service serves headers files. Potentially convert into middleware? -type Service struct { - // server configuration (location, forbidden files and etc) - cfg *Config -} - -// Init must return configure service and return true if service hasStatus enabled. Must return error in case of -// misconfiguration. Services must not be used without proper configuration pushed first. -func (s *Service) Init(cfg *Config, r *rrhttp.Service) (bool, error) { - if r == nil { - return false, nil - } - - s.cfg = cfg - r.AddMiddleware(s.middleware) - - return true, nil -} - -// middleware must return true if request/response pair is handled within the middleware. -func (s *Service) middleware(f http.HandlerFunc) http.HandlerFunc { - // Define the http.HandlerFunc - return func(w http.ResponseWriter, r *http.Request) { - - 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) - } - } - - if s.cfg.CORS != nil { - if r.Method == http.MethodOptions { - s.preflightRequest(w, r) - return - } - - s.corsHeaders(w, r) - } - - f(w, r) - } -} - -// configure OPTIONS response -func (s *Service) preflightRequest(w http.ResponseWriter, r *http.Request) { - headers := w.Header() - - headers.Add("Vary", "Origin") - headers.Add("Vary", "Access-Control-Request-Method") - headers.Add("Vary", "Access-Control-Request-Headers") - - if s.cfg.CORS.AllowedOrigin != "" { - headers.Set("Access-Control-Allow-Origin", s.cfg.CORS.AllowedOrigin) - } - - if s.cfg.CORS.AllowedHeaders != "" { - headers.Set("Access-Control-Allow-Headers", s.cfg.CORS.AllowedHeaders) - } - - if s.cfg.CORS.AllowedMethods != "" { - headers.Set("Access-Control-Allow-Methods", s.cfg.CORS.AllowedMethods) - } - - if s.cfg.CORS.AllowCredentials != nil { - headers.Set("Access-Control-Allow-Credentials", strconv.FormatBool(*s.cfg.CORS.AllowCredentials)) - } - - if s.cfg.CORS.MaxAge > 0 { - headers.Set("Access-Control-Max-Age", strconv.Itoa(s.cfg.CORS.MaxAge)) - } - - w.WriteHeader(http.StatusOK) -} - -// configure CORS headers -func (s *Service) corsHeaders(w http.ResponseWriter, r *http.Request) { - headers := w.Header() - - headers.Add("Vary", "Origin") - - if s.cfg.CORS.AllowedOrigin != "" { - headers.Set("Access-Control-Allow-Origin", s.cfg.CORS.AllowedOrigin) - } - - if s.cfg.CORS.AllowedHeaders != "" { - headers.Set("Access-Control-Allow-Headers", s.cfg.CORS.AllowedHeaders) - } - - if s.cfg.CORS.ExposedHeaders != "" { - headers.Set("Access-Control-Expose-Headers", s.cfg.CORS.ExposedHeaders) - } - - if s.cfg.CORS.AllowCredentials != nil { - headers.Set("Access-Control-Allow-Credentials", strconv.FormatBool(*s.cfg.CORS.AllowCredentials)) - } -} diff --git a/service/headers/service_test.go b/service/headers/service_test.go deleted file mode 100644 index a67def02..00000000 --- a/service/headers/service_test.go +++ /dev/null @@ -1,340 +0,0 @@ -package headers - -import ( - "github.com/cenkalti/backoff/v4" - json "github.com/json-iterator/go" - "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/test" - "github.com/spiral/roadrunner/service" - rrhttp "github.com/spiral/roadrunner/service/http" - "github.com/stretchr/testify/assert" - "io/ioutil" - "net/http" - "testing" - "time" -) - -type testCfg struct { - httpCfg string - headers string - target string -} - -func (cfg *testCfg) Get(name string) service.Config { - if name == rrhttp.ID { - return &testCfg{target: cfg.httpCfg} - } - - if name == ID { - return &testCfg{target: cfg.headers} - } - return nil -} - -func (cfg *testCfg) Unmarshal(out interface{}) error { - return json.Unmarshal([]byte(cfg.target), out) -} - -func Test_RequestHeaders(t *testing.T) { - bkoff := backoff.NewExponentialBackOff() - bkoff.MaxElapsedTime = time.Second * 15 - - err := backoff.Retry(func() error { - logger, _ := test.NewNullLogger() - logger.SetLevel(logrus.DebugLevel) - - c := service.NewContainer(logger) - c.Register(rrhttp.ID, &rrhttp.Service{}) - c.Register(ID, &Service{}) - - assert.NoError(t, c.Init(&testCfg{ - headers: `{"request":{"input": "custom-header"}}`, - httpCfg: `{ - "enable": true, - "address": ":6078", - "maxRequestSize": 1024, - "workers":{ - "command": "php ../../tests/http/client.php header pipes", - "relay": "pipes", - "pool": { - "numWorkers": 1, - "allocateTimeout": 10000000, - "destroyTimeout": 10000000 - } - } - }`})) - - go func() { - err := c.Serve() - if err != nil { - t.Errorf("error during Serve: error %v", err) - } - }() - - time.Sleep(time.Millisecond * 100) - defer c.Stop() - - req, err := http.NewRequest("GET", "http://localhost:6078?hello=value", nil) - if err != nil { - return err - } - - r, err := http.DefaultClient.Do(req) - if err != nil { - return err - } - - b, err := ioutil.ReadAll(r.Body) - if err != nil { - return err - } - - assert.Equal(t, 200, r.StatusCode) - assert.Equal(t, "CUSTOM-HEADER", string(b)) - - err = r.Body.Close() - if err != nil { - return err - } - - return nil - }, bkoff) - - if err != nil { - t.Fatal(err) - } -} - -func Test_ResponseHeaders(t *testing.T) { - bkoff := backoff.NewExponentialBackOff() - bkoff.MaxElapsedTime = time.Second * 15 - - err := backoff.Retry(func() error { - logger, _ := test.NewNullLogger() - logger.SetLevel(logrus.DebugLevel) - - c := service.NewContainer(logger) - c.Register(rrhttp.ID, &rrhttp.Service{}) - c.Register(ID, &Service{}) - - assert.NoError(t, c.Init(&testCfg{ - headers: `{"response":{"output": "output-header"},"request":{"input": "custom-header"}}`, - httpCfg: `{ - "enable": true, - "address": ":6079", - "maxRequestSize": 1024, - "workers":{ - "command": "php ../../tests/http/client.php header pipes", - "relay": "pipes", - "pool": { - "numWorkers": 1, - "allocateTimeout": 10000000, - "destroyTimeout": 10000000 - } - } - }`})) - - go func() { - err := c.Serve() - if err != nil { - t.Errorf("error during the Serve: error %v", err) - } - }() - time.Sleep(time.Millisecond * 100) - defer c.Stop() - - req, err := http.NewRequest("GET", "http://localhost:6079?hello=value", nil) - if err != nil { - return err - } - - r, err := http.DefaultClient.Do(req) - if err != nil { - return err - } - - assert.Equal(t, "output-header", r.Header.Get("output")) - - b, err := ioutil.ReadAll(r.Body) - if err != nil { - return err - } - assert.Equal(t, 200, r.StatusCode) - assert.Equal(t, "CUSTOM-HEADER", string(b)) - - err = r.Body.Close() - if err != nil { - return err - } - - return nil - }, bkoff) - - if err != nil { - t.Fatal(err) - } -} - -func TestCORS_OPTIONS(t *testing.T) { - bkoff := backoff.NewExponentialBackOff() - bkoff.MaxElapsedTime = time.Second * 15 - - err := backoff.Retry(func() error { - logger, _ := test.NewNullLogger() - logger.SetLevel(logrus.DebugLevel) - - c := service.NewContainer(logger) - c.Register(rrhttp.ID, &rrhttp.Service{}) - c.Register(ID, &Service{}) - - assert.NoError(t, c.Init(&testCfg{ - headers: `{ -"cors":{ - "allowedOrigin": "*", - "allowedHeaders": "*", - "allowedMethods": "GET,POST,PUT,DELETE", - "allowCredentials": true, - "exposedHeaders": "Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma", - "maxAge": 600 -} -}`, - httpCfg: `{ - "enable": true, - "address": ":16379", - "maxRequestSize": 1024, - "workers":{ - "command": "php ../../tests/http/client.php headers pipes", - "relay": "pipes", - "pool": { - "numWorkers": 1, - "allocateTimeout": 10000000, - "destroyTimeout": 10000000 - } - } - }`})) - - go func() { - err := c.Serve() - if err != nil { - t.Errorf("error during the Serve: error %v", err) - } - }() - time.Sleep(time.Millisecond * 100) - defer c.Stop() - - req, err := http.NewRequest("OPTIONS", "http://localhost:16379", nil) - if err != nil { - return err - } - - r, err := http.DefaultClient.Do(req) - if err != nil { - return err - } - - assert.Equal(t, "true", r.Header.Get("Access-Control-Allow-Credentials")) - assert.Equal(t, "*", r.Header.Get("Access-Control-Allow-Headers")) - assert.Equal(t, "GET,POST,PUT,DELETE", r.Header.Get("Access-Control-Allow-Methods")) - assert.Equal(t, "*", r.Header.Get("Access-Control-Allow-Origin")) - assert.Equal(t, "600", r.Header.Get("Access-Control-Max-Age")) - assert.Equal(t, "true", r.Header.Get("Access-Control-Allow-Credentials")) - - _, err = ioutil.ReadAll(r.Body) - if err != nil { - return err - } - assert.Equal(t, 200, r.StatusCode) - - err = r.Body.Close() - if err != nil { - return err - } - - return nil - }, bkoff) - - if err != nil { - t.Fatal(err) - } -} - -func TestCORS_Pass(t *testing.T) { - bkoff := backoff.NewExponentialBackOff() - bkoff.MaxElapsedTime = time.Second * 15 - - err := backoff.Retry(func() error { - logger, _ := test.NewNullLogger() - logger.SetLevel(logrus.DebugLevel) - - c := service.NewContainer(logger) - c.Register(rrhttp.ID, &rrhttp.Service{}) - c.Register(ID, &Service{}) - - assert.NoError(t, c.Init(&testCfg{ - headers: `{ -"cors":{ - "allowedOrigin": "*", - "allowedHeaders": "*", - "allowedMethods": "GET,POST,PUT,DELETE", - "allowCredentials": true, - "exposedHeaders": "Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma", - "maxAge": 600 -} -}`, - httpCfg: `{ - "enable": true, - "address": ":6672", - "maxRequestSize": 1024, - "workers":{ - "command": "php ../../tests/http/client.php headers pipes", - "relay": "pipes", - "pool": { - "numWorkers": 1, - "allocateTimeout": 10000000, - "destroyTimeout": 10000000 - } - } - }`})) - - go func() { - err := c.Serve() - if err != nil { - t.Errorf("error during the Serve: error %v", err) - } - }() - time.Sleep(time.Millisecond * 100) - defer c.Stop() - - req, err := http.NewRequest("GET", "http://localhost:6672", nil) - if err != nil { - return err - } - - r, err := http.DefaultClient.Do(req) - if err != nil { - return err - } - - assert.Equal(t, "true", r.Header.Get("Access-Control-Allow-Credentials")) - assert.Equal(t, "*", r.Header.Get("Access-Control-Allow-Headers")) - assert.Equal(t, "*", r.Header.Get("Access-Control-Allow-Origin")) - assert.Equal(t, "true", r.Header.Get("Access-Control-Allow-Credentials")) - - _, err = ioutil.ReadAll(r.Body) - if err != nil { - return err - } - assert.Equal(t, 200, r.StatusCode) - - err = r.Body.Close() - if err != nil { - return err - } - - return nil - }, bkoff) - - if err != nil { - t.Fatal(err) - } -} |