diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | cmd/rr/main.go | 2 | ||||
-rw-r--r-- | go.mod | 1 | ||||
-rw-r--r-- | service/gzip/config.go | 23 | ||||
-rw-r--r-- | service/gzip/config_test.go | 43 | ||||
-rw-r--r-- | service/gzip/service.go | 32 | ||||
-rw-r--r-- | service/gzip/service_test.go | 123 | ||||
-rw-r--r-- | service/static/service_test.go | 4 | ||||
-rw-r--r-- | tests/gzip-large-file.txt | 19 |
9 files changed, 246 insertions, 2 deletions
@@ -21,6 +21,7 @@ test: go test -v -race -cover ./service/headers go test -v -race -cover ./service/metrics go test -v -race -cover ./service/health + go test -v -race -cover ./service/gzip lint: go fmt ./... golint ./...
\ No newline at end of file diff --git a/cmd/rr/main.go b/cmd/rr/main.go index fc02c4d4..f1cf3840 100644 --- a/cmd/rr/main.go +++ b/cmd/rr/main.go @@ -34,6 +34,7 @@ import ( "github.com/spiral/roadrunner/service/metrics" "github.com/spiral/roadrunner/service/rpc" "github.com/spiral/roadrunner/service/static" + "github.com/spiral/roadrunner/service/gzip" // additional commands and debug handlers _ "github.com/spiral/roadrunner/cmd/rr/http" @@ -49,6 +50,7 @@ func main() { rr.Container.Register(static.ID, &static.Service{}) rr.Container.Register(limit.ID, &limit.Service{}) rr.Container.Register(health.ID, &health.Service{}) + rr.Container.Register(gzip.ID, &gzip.Service{}) // you can register additional commands using cmd.CLI rr.Execute() @@ -2,6 +2,7 @@ module github.com/spiral/roadrunner require ( github.com/BurntSushi/toml v0.3.1 // indirect + github.com/NYTimes/gziphandler v1.1.1 github.com/StackExchange/wmi v0.0.0-20181212234831-e0a55b97c705 // indirect github.com/buger/goterm v0.0.0-20181115115552-c206103e1f37 github.com/dustin/go-humanize v1.0.0 diff --git a/service/gzip/config.go b/service/gzip/config.go new file mode 100644 index 00000000..7da48a67 --- /dev/null +++ b/service/gzip/config.go @@ -0,0 +1,23 @@ +package gzip + +import ( + "github.com/spiral/roadrunner/service" +) + +// Config describes file location and controls access to them. +type Config struct { + // Dir contains name of directory to control access to. + Enable bool +} + +// Hydrate must populate Config values using given Config source. Must return error if Config is not valid. +func (c *Config) Hydrate(cfg service.Config) error { + return cfg.Unmarshal(c) +} + +// InitDefaults sets missing values to their default values. +func (c *Config) InitDefaults() error { + c.Enable = true + + return nil +} diff --git a/service/gzip/config_test.go b/service/gzip/config_test.go new file mode 100644 index 00000000..92bb1cb5 --- /dev/null +++ b/service/gzip/config_test.go @@ -0,0 +1,43 @@ +package gzip + +import ( + "encoding/json" + "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 { return json.Unmarshal([]byte(cfg.cfg), out) } + +func Test_Config_Hydrate(t *testing.T) { + cfg := &mockCfg{`{"enable": true}`} + c := &Config{} + + assert.NoError(t, c.Hydrate(cfg)) +} + +func Test_Config_Hydrate_Error(t *testing.T) { + cfg := &mockCfg{`{"enable": "invalid"}`} + c := &Config{} + + assert.Error(t, c.Hydrate(cfg)) +} + +func Test_Config_Hydrate_Error2(t *testing.T) { + cfg := &mockCfg{`{"enable": 1}`} + c := &Config{} + + assert.Error(t, c.Hydrate(cfg)) +} + +func Test_Config_Defaults(t *testing.T) { + c := &Config{} + err := c.InitDefaults() + if err != nil { + t.Errorf("error during the InitDefaults: error %v", err) + } + assert.Equal(t, true, c.Enable) +} diff --git a/service/gzip/service.go b/service/gzip/service.go new file mode 100644 index 00000000..4c6320ad --- /dev/null +++ b/service/gzip/service.go @@ -0,0 +1,32 @@ +package gzip + +import ( + rrhttp "github.com/spiral/roadrunner/service/http" + "github.com/NYTimes/gziphandler" + "net/http" +) + +// ID contains default service name. +const ID = "gzip" + +type Service struct { + cfg *Config +} + +func (s *Service) Init(cfg *Config, r *rrhttp.Service) (bool, error) { + s.cfg = cfg + + if s.cfg.Enable == false { + return false, nil + } + + r.AddMiddleware(s.middleware) + + return true, nil +} + +func (s *Service) middleware(f http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + gziphandler.GzipHandler(f).ServeHTTP(w, r) + } +} diff --git a/service/gzip/service_test.go b/service/gzip/service_test.go new file mode 100644 index 00000000..6f07a1f9 --- /dev/null +++ b/service/gzip/service_test.go @@ -0,0 +1,123 @@ +package gzip + +import ( + "encoding/json" + "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" + "os" + "testing" + "time" +) + +type testCfg struct { + gzip string + httpCfg string + static 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.gzip} + } + return nil +} +func (cfg *testCfg) Unmarshal(out interface{}) error { + return json.Unmarshal([]byte(cfg.target), out) +} + +func get(url string) (string, *http.Response, error) { + r, err := http.Get(url) + if err != nil { + return "", nil, err + } + + b, err := ioutil.ReadAll(r.Body) + if err != nil { + return "", nil, err + } + + err = r.Body.Close() + if err != nil { + return "", nil, err + } + + return string(b), r, err +} + +func Test_Disabled(t *testing.T) { + logger, _ := test.NewNullLogger() + logger.SetLevel(logrus.DebugLevel) + + c := service.NewContainer(logger) + c.Register(ID, &Service{}) + + assert.NoError(t, c.Init(&testCfg{ + gzip: `{"enable":false}`, + })) + + s, st := c.Get(ID) + assert.NotNil(t, s) + assert.Equal(t, service.StatusInactive, st) +} +// func Test_Files(t *testing.T) { +// 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{ +// gzip: `{"enable":true}`, +// static: `{"enable":true, "dir":"../../tests", "forbid":[]}`, +// httpCfg: `{ +// "enable": true, +// "address": ":6029", +// "maxRequestSize": 1024, +// "uploads": { +// "dir": ` + tmpDir() + `, +// "forbid": [] +// }, +// "workers":{ +// "command": "php ../../tests/http/client.php pid pipes", +// "relay": "pipes", +// "pool": { +// "numWorkers": 1, +// "allocateTimeout": 10000000, +// "destroyTimeout": 10000000 +// } +// } +// }`})) + +// go func() { +// err := c.Serve() +// if err != nil { +// t.Errorf("serve error: %v", err) +// } +// }() +// time.Sleep(time.Millisecond * 1000) +// defer c.Stop() + +// b, _, _ := get("http://localhost:6029/sample.txt") +// assert.Equal(t, "sample", b) +// //header should not contain content-encoding:gzip because content-length < gziphandler.DefaultMinSize +// // b, _, _ := get("http://localhost:6029/gzip-large-file.txt") +// //header should contain content-encoding:gzip because content-length > gziphandler.DefaultMinSize +// } + +func tmpDir() string { + p := os.TempDir() + r, _ := json.Marshal(p) + + return string(r) +} diff --git a/service/static/service_test.go b/service/static/service_test.go index 309804cc..4205650d 100644 --- a/service/static/service_test.go +++ b/service/static/service_test.go @@ -77,9 +77,9 @@ func Test_Files(t *testing.T) { "command": "php ../../tests/http/client.php pid pipes", "relay": "pipes", "pool": { - "numWorkers": 1, + "numWorkers": 1, "allocateTimeout": 10000000, - "destroyTimeout": 10000000 + "destroyTimeout": 10000000 } } }`})) diff --git a/tests/gzip-large-file.txt b/tests/gzip-large-file.txt new file mode 100644 index 00000000..4c3eef8f --- /dev/null +++ b/tests/gzip-large-file.txt @@ -0,0 +1,19 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus sodales consectetur fringilla. Phasellus rhoncus lectus nec diam vehicula euismod. Proin eget mollis libero, at ullamcorper ex. Proin sollicitudin, ligula vitae efficitur iaculis, est arcu iaculis ex, in finibus dolor lorem sit amet orci. Pellentesque placerat fermentum est, sed aliquam nulla aliquam non. Donec non tellus vitae elit iaculis ullamcorper. Sed ultricies arcu vel nunc maximus, nec iaculis eros varius. Duis iaculis a mi sed rutrum. Aliquam aliquet ornare arcu id pulvinar. Maecenas luctus orci at efficitur mollis. Aliquam eu placerat magna, id blandit libero. + +Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Pellentesque mattis nibh in sagittis dictum. Ut eu neque mollis, pharetra nunc in, sodales neque. Aenean viverra commodo arcu, sit amet egestas magna interdum non. Sed pulvinar ornare urna quis sagittis. Duis tempor at augue sed laoreet. Curabitur aliquam at arcu a semper. Sed eu sem ante. Praesent pellentesque magna lacus, eget ultricies elit pharetra et. Sed ut nisl ultricies orci ultricies porttitor nec id dui. Etiam eu ipsum sagittis, maximus lectus non, semper justo. Fusce orci tellus, congue et consequat nec, facilisis accumsan enim. Proin facilisis quis libero a viverra. Curabitur neque magna, euismod in lacinia nec, scelerisque nec ex. + +Donec nec ullamcorper turpis. Praesent ultrices nunc non dignissim auctor. Morbi at congue lacus. Ut ut diam nec magna finibus ullamcorper. Donec auctor est ut tempor euismod. Proin tincidunt ipsum ac leo aliquet finibus. Curabitur laoreet turpis id enim rutrum, quis tincidunt sem maximus. Vivamus quis diam at lorem viverra pretium. Quisque eget diam scelerisque, hendrerit purus at, blandit lacus. Sed quis consectetur lectus, vitae molestie neque. Quisque in nisi augue. + +Proin ultricies maximus tellus, sed malesuada metus posuere at. Vestibulum a purus ut libero tincidunt dictum et vel sapien. In auctor a metus in ullamcorper. Nullam pharetra pharetra ipsum, non gravida massa tempus non. Proin et varius nunc. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque ullamcorper porttitor gravida. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In hac habitasse platea dictumst. Donec sit amet rhoncus turpis. Cras vel dui non felis consectetur varius vitae id turpis. Sed vitae hendrerit diam. Aenean neque turpis, laoreet eget libero a, pellentesque malesuada ipsum. Praesent bibendum ultrices nisi eu ornare. Nulla dignissim eu ipsum non iaculis. + +Ut eget lacus porta, posuere neque a, feugiat mi. Sed vehicula sed dui non imperdiet. Nulla quis fringilla nunc. Nunc ipsum dui, hendrerit eu diam sit amet, vehicula convallis libero. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Pellentesque vel diam id felis ornare lobortis a in metus. Mauris feugiat tellus sit amet felis pellentesque ornare sit amet ac augue. Phasellus ut porta purus. Morbi vestibulum, nunc sed pellentesque rhoncus, nibh turpis varius justo, vel mollis ipsum risus at libero. Praesent eget convallis arcu. Donec placerat justo et odio dignissim interdum. Vestibulum rhoncus faucibus tempor. Quisque ipsum tortor, ullamcorper ut sagittis quis, elementum eget metus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; + +Suspendisse in mi non eros fermentum euismod a a tortor. Morbi tincidunt sagittis arcu, ut cursus urna luctus vel. Curabitur malesuada, dui at rutrum pretium, turpis sem vulputate urna, eget suscipit odio ipsum vitae enim. Nunc pellentesque nibh sed facilisis aliquam. Sed ac est sit amet dolor aliquam volutpat in ac risus. Nulla facilisi. Etiam gravida tincidunt metus vitae consectetur. Phasellus rutrum metus ut porttitor varius. Nullam sollicitudin orci ac vehicula accumsan. Maecenas vitae malesuada est. Nam viverra ante ante, id mollis justo sagittis sed. Ut facilisis, lectus id dignissim volutpat, nisl elit fringilla elit, vel facilisis neque lectus et neque. Etiam erat justo, interdum quis neque at, mattis tristique lorem. Mauris malesuada nibh ante, eget eleifend sapien condimentum efficitur. Suspendisse metus turpis, viverra vitae tortor eget, porttitor scelerisque leo. Aenean ac fermentum tortor, ut dapibus mi. + +Fusce semper velit ac tempor lobortis. Suspendisse venenatis eros est, quis sollicitudin dolor consequat vitae. Vivamus tristique erat quis eros laoreet, et dictum nibh sagittis. Integer iaculis pretium orci non venenatis. Aenean ultricies purus ac dui tempor, ut fringilla turpis feugiat. Suspendisse sodales vestibulum aliquet. Donec elit lectus, porta sit amet mollis eu, porttitor eu sem. Vivamus sit amet nisi vel leo commodo suscipit faucibus porttitor orci. Quisque ut sollicitudin quam, luctus pharetra risus. Ut tempus diam odio, elementum porttitor lacus sollicitudin non. Curabitur eget eleifend nisi. Nullam porttitor sagittis gravida. Donec sodales a lorem vel fermentum. Aliquam ac rhoncus quam. + +Fusce pulvinar imperdiet diam et sollicitudin. Quisque a urna id ex porttitor venenatis sit amet id ligula. In fringilla euismod orci vitae pulvinar. In facilisis non nibh ac rutrum. Quisque aliquam sem sit amet pharetra pellentesque. Donec imperdiet libero sed sapien consequat vehicula. Integer vitae ornare sapien. Vivamus pretium felis at urna gravida, quis luctus sem feugiat. Fusce nisl metus, dictum vitae maximus in, interdum id nunc. Phasellus sodales nibh at mi porta egestas quis nec nisl. + +Fusce vel suscipit erat, quis vulputate quam. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam a odio nulla. Suspendisse ac tincidunt erat. Duis tortor sem, blandit sed arcu vehicula, faucibus vehicula eros. Sed gravida faucibus quam sed condimentum. Donec sed imperdiet sem. Quisque purus mauris, ultrices sit amet nisl ut, luctus convallis enim. Quisque sit amet nisi ut lacus ullamcorper placerat et id dolor. Mauris tincidunt quam id posuere sodales. Integer tristique efficitur varius. Nulla facilisi. Morbi sed odio eget neque ornare congue. Duis dui massa, maximus ac ullamcorper nec, tempus ut magna. Morbi dictum nisl sagittis orci hendrerit, ac consequat odio pulvinar. Ut mollis sodales egestas. + +Maecenas ante libero, aliquam in enim quis, aliquet malesuada mauris. Quisque porta massa id sem lacinia, id blandit diam mollis. Donec rhoncus vestibulum ante. Aliquam id ligula a erat bibendum condimentum congue non eros. Praesent ac lectus convallis, laoreet quam vitae, viverra arcu. Morbi finibus ligula risus, et ornare turpis iaculis eget. Donec placerat metus vel ex mattis tristique. Donec varius dapibus leo eu pulvinar. Fusce et diam mauris. Duis consequat pharetra urna id malesuada.
\ No newline at end of file |