summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorValery Piashchynski <[email protected]>2020-11-27 16:08:27 +0300
committerValery Piashchynski <[email protected]>2020-11-27 16:08:27 +0300
commit6abfe73b5f7b66a7be094486e29ab3f2ab1ce940 (patch)
tree888b5eb96ab396ef53f77ce3944ed62af9cc878d
parentb5020bfce6b5362400cb9b578fe32c1a6ed5d61a (diff)
Gzip plugin
-rwxr-xr-x.github/workflows/ci-build.yml3
-rwxr-xr-x.rr.yaml40
-rw-r--r--Makefile3
-rwxr-xr-xgo.mod1
-rwxr-xr-xgo.sum2
-rw-r--r--plugins/gzip/plugin.go26
-rw-r--r--plugins/gzip/tests/configs/.rr-http-middlewareNotExist.yaml22
-rw-r--r--plugins/gzip/tests/configs/.rr-http-withGzip.yaml22
-rw-r--r--plugins/gzip/tests/plugin_test.go170
-rw-r--r--plugins/gzip/tests/psr-worker.php (renamed from plugins/http/tests/psr-worker.php)2
-rw-r--r--plugins/http/config.go20
-rw-r--r--plugins/http/plugin.go30
-rw-r--r--plugins/http/tests/configs/.rr-echoErr.yaml2
-rw-r--r--plugins/http/tests/configs/.rr-http.yaml2
14 files changed, 308 insertions, 37 deletions
diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml
index 49d790f9..374f690a 100755
--- a/.github/workflows/ci-build.yml
+++ b/.github/workflows/ci-build.yml
@@ -76,12 +76,13 @@ jobs:
go test -v -race ./plugins/resetter/tests -tags=debug -coverprofile=informer.txt -covermode=atomic
go test -v -race ./plugins/http/attributes -tags=debug -coverprofile=attributes.txt -covermode=atomic
go test -v -race ./plugins/http/tests -tags=debug -coverprofile=http_tests.txt -covermode=atomic
+ go test -v -race ./plugins/gzip/tests -tags=debug -coverprofile=gzip.txt -covermode=atomic
- name: Run code coverage
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
- files: lib.txt, rpc_config.txt, rpc.txt, plugin_config.txt, logger.txt, server.txt, metrics.txt, informer.txt attributes.txt http_tests.txt
+ files: gzip.txt, lib.txt, rpc_config.txt, rpc.txt, plugin_config.txt, logger.txt, server.txt, metrics.txt, informer.txt attributes.txt http_tests.txt
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false
diff --git a/.rr.yaml b/.rr.yaml
index 81e1b0c4..5caff422 100755
--- a/.rr.yaml
+++ b/.rr.yaml
@@ -1,7 +1,39 @@
-app:
- commmand: "php app.php"
+rpc:
+ listen: tcp://127.0.0.1:6001
+ disabled: false
+
+server:
+ command: "php ../../../tests/http/client.php echo pipes"
user: ""
group: ""
env:
- DEBUG: true
- APP_KEY: "..." \ No newline at end of file
+ "RR_HTTP": "true"
+ relay: "pipes"
+ relayTimeout: "20s"
+
+http:
+ debug: true
+ address: 127.0.0.1:18903
+ maxRequestSize: 1024
+ 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: 2
+ maxJobs: 0
+ allocateTimeout: 60s
+ destroyTimeout: 60s
+
+ ssl:
+ port: 8892
+ redirect: false
+ cert: fixtures/server.crt
+ key: fixtures/server.key
+ # rootCa: root.crt
+ fcgi:
+ address: tcp://0.0.0.0:7921
+ http2:
+ enabled: false
+ h2c: false
+ maxConcurrentStreams: 128 \ No newline at end of file
diff --git a/Makefile b/Makefile
index 165802f8..4797d130 100644
--- a/Makefile
+++ b/Makefile
@@ -10,4 +10,5 @@ test:
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
- go test -v -race -cover ./plugins/http/tests -tags=debug \ No newline at end of file
+ go test -v -race -cover ./plugins/http/tests -tags=debug
+ go test -v -race -cover ./plugins/gzip/tests -tags=debug \ No newline at end of file
diff --git a/go.mod b/go.mod
index 042819cb..8eb59574 100755
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/spiral/roadrunner/v2
go 1.15
require (
+ github.com/NYTimes/gziphandler v1.1.1
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
github.com/fatih/color v1.10.0
github.com/go-ole/go-ole v1.2.4 // indirect
diff --git a/go.sum b/go.sum
index da6b35bf..7762cc67 100755
--- a/go.sum
+++ b/go.sum
@@ -14,6 +14,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
+github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
diff --git a/plugins/gzip/plugin.go b/plugins/gzip/plugin.go
new file mode 100644
index 00000000..f5a0f4ea
--- /dev/null
+++ b/plugins/gzip/plugin.go
@@ -0,0 +1,26 @@
+package gzip
+
+import (
+ "net/http"
+
+ "github.com/NYTimes/gziphandler"
+)
+
+const PluginName = "gzip"
+
+type Gzip struct {
+}
+
+func (g *Gzip) Init() error {
+ return nil
+}
+
+func (g *Gzip) Middleware(next http.Handler) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ gziphandler.GzipHandler(next).ServeHTTP(w, r)
+ }
+}
+
+func (g *Gzip) Name() string {
+ return PluginName
+}
diff --git a/plugins/gzip/tests/configs/.rr-http-middlewareNotExist.yaml b/plugins/gzip/tests/configs/.rr-http-middlewareNotExist.yaml
new file mode 100644
index 00000000..df02c043
--- /dev/null
+++ b/plugins/gzip/tests/configs/.rr-http-middlewareNotExist.yaml
@@ -0,0 +1,22 @@
+server:
+ command: "php psr-worker.php"
+ user: ""
+ group: ""
+ env:
+ "RR_HTTP": "true"
+ relay: "pipes"
+ relayTimeout: "20s"
+
+http:
+ debug: true
+ address: 127.0.0.1:18103
+ maxRequestSize: 1024
+ middleware: [ "gzip", "foo" ]
+ 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: 2
+ maxJobs: 0
+ allocateTimeout: 60s
+ destroyTimeout: 60s \ No newline at end of file
diff --git a/plugins/gzip/tests/configs/.rr-http-withGzip.yaml b/plugins/gzip/tests/configs/.rr-http-withGzip.yaml
new file mode 100644
index 00000000..b91a9aad
--- /dev/null
+++ b/plugins/gzip/tests/configs/.rr-http-withGzip.yaml
@@ -0,0 +1,22 @@
+server:
+ command: "php psr-worker.php"
+ user: ""
+ group: ""
+ env:
+ "RR_HTTP": "true"
+ relay: "pipes"
+ relayTimeout: "20s"
+
+http:
+ debug: true
+ address: 127.0.0.1:18953
+ maxRequestSize: 1024
+ middleware: [ "gzip" ]
+ 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: 2
+ maxJobs: 0
+ allocateTimeout: 60s
+ destroyTimeout: 60s \ No newline at end of file
diff --git a/plugins/gzip/tests/plugin_test.go b/plugins/gzip/tests/plugin_test.go
new file mode 100644
index 00000000..c7f12643
--- /dev/null
+++ b/plugins/gzip/tests/plugin_test.go
@@ -0,0 +1,170 @@
+package tests
+
+import (
+ "net/http"
+ "os"
+ "os/signal"
+ "sync"
+ "syscall"
+ "testing"
+ "time"
+
+ "github.com/golang/mock/gomock"
+ "github.com/spiral/endure"
+ "github.com/spiral/roadrunner/v2/mocks"
+ "github.com/spiral/roadrunner/v2/plugins/config"
+ "github.com/spiral/roadrunner/v2/plugins/gzip"
+ httpPlugin "github.com/spiral/roadrunner/v2/plugins/http"
+ "github.com/spiral/roadrunner/v2/plugins/logger"
+ "github.com/spiral/roadrunner/v2/plugins/server"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGzipPlugin(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-http-withGzip.yaml",
+ Prefix: "rr",
+ }
+
+ err = cont.RegisterAll(
+ cfg,
+ &logger.ZapLogger{},
+ &server.Plugin{},
+ &httpPlugin.Plugin{},
+ &gzip.Gzip{},
+ )
+ 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)
+
+ wg := &sync.WaitGroup{}
+ wg.Add(1)
+
+ go func() {
+ tt := time.NewTimer(time.Second * 10)
+ defer wg.Done()
+ 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
+ }
+ }
+ }()
+
+ t.Run("GzipCheckHeader", headerCheck)
+ wg.Wait()
+}
+
+func headerCheck(t *testing.T) {
+ req, err := http.NewRequest("GET", "http://localhost:18953", nil)
+ assert.NoError(t, err)
+ client := &http.Client{
+ Transport: &http.Transport{
+ DisableCompression: false,
+ },
+ }
+
+ r, err := client.Do(req)
+ assert.NoError(t, err)
+ assert.True(t, r.Uncompressed)
+
+ err = r.Body.Close()
+ assert.NoError(t, err)
+}
+
+func TestMiddlewareNotExist(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-http-middlewareNotExist.yaml",
+ Prefix: "rr",
+ }
+
+ controller := gomock.NewController(t)
+ mockLogger := mocks.NewMockLogger(controller)
+
+ mockLogger.EXPECT().Warn("requested middleware does not exist", "requested", "foo")
+
+ err = cont.RegisterAll(
+ cfg,
+ mockLogger,
+ &server.Plugin{},
+ &httpPlugin.Plugin{},
+ &gzip.Gzip{},
+ )
+ 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)
+
+ wg := &sync.WaitGroup{}
+ wg.Add(1)
+
+ go func() {
+ tt := time.NewTimer(time.Second * 5)
+ defer wg.Done()
+ 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
+ }
+ }
+ }()
+
+ wg.Wait()
+}
diff --git a/plugins/http/tests/psr-worker.php b/plugins/gzip/tests/psr-worker.php
index 65fc6bde..ed936bde 100644
--- a/plugins/http/tests/psr-worker.php
+++ b/plugins/gzip/tests/psr-worker.php
@@ -14,7 +14,7 @@ $psr7 = new RoadRunner\PSR7Client($worker);
while ($req = $psr7->acceptRequest()) {
try {
$resp = new \Zend\Diactoros\Response();
- $resp->getBody()->write("hello world");
+ $resp->getBody()->write(str_repeat("hello world", 1000));
$psr7->respond($resp);
} catch (\Throwable $e) {
diff --git a/plugins/http/config.go b/plugins/http/config.go
index 3f1fa69e..ae18e4ce 100644
--- a/plugins/http/config.go
+++ b/plugins/http/config.go
@@ -32,23 +32,6 @@ func (c *Cidrs) IsTrusted(ip string) bool {
return false
}
-type ServerConfig struct {
- // Command includes command strings with all the parameters, example: "php worker.php pipes".
- Command string
-
- // User under which process will be started
- User string
-
- // Relay defines connection method and factory to be used to connect to workers:
- // "pipes", "tcp://:6001", "unix://pool.sock"
- // This config section must not change on re-configuration.
- Relay string
-
- // 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
-}
-
// Config configures RoadRunner HTTP server.
type Config struct {
// Port and port to handle as http server.
@@ -78,6 +61,9 @@ type Config struct {
// Env is environment variables passed to the http pool
Env map[string]string
+ Middleware []string
+
+ // slice of net.IPNet
cidrs Cidrs
}
diff --git a/plugins/http/plugin.go b/plugins/http/plugin.go
index 7ce3a70d..19765afc 100644
--- a/plugins/http/plugin.go
+++ b/plugins/http/plugin.go
@@ -13,6 +13,7 @@ import (
"sync"
"github.com/hashicorp/go-multierror"
+ "github.com/spiral/endure"
"github.com/spiral/errors"
"github.com/spiral/roadrunner/v2"
"github.com/spiral/roadrunner/v2/interfaces/log"
@@ -38,6 +39,8 @@ type Middleware interface {
Middleware(f http.Handler) http.HandlerFunc
}
+type middleware map[string]Middleware
+
// Service manages pool, http servers.
type Plugin struct {
sync.Mutex
@@ -48,7 +51,7 @@ type Plugin struct {
cfg *Config
// middlewares to chain
- mdwr []Middleware
+ mdwr middleware
// Event listener to stdout
listener util.EventListener
@@ -87,6 +90,7 @@ func (s *Plugin) Init(cfg config.Configurer, log log.Logger, server factory.Serv
s.configurer = cfg
s.log = log
+ s.mdwr = make(map[string]Middleware)
if !s.cfg.EnableHTTP() && !s.cfg.EnableTLS() && !s.cfg.EnableFCGI() {
return errors.E(op, errors.Disabled)
@@ -333,8 +337,8 @@ func (s *Plugin) Collects() []interface{} {
}
}
-func (s *Plugin) AddMiddleware(m Middleware) {
- s.mdwr = append(s.mdwr, m)
+func (s *Plugin) AddMiddleware(name endure.Named, m Middleware) {
+ s.mdwr[name.Name()] = m
}
func (s *Plugin) redirect(w http.ResponseWriter, r *http.Request) bool {
@@ -498,19 +502,23 @@ func (s *Plugin) tlsAddr(host string, forcePort bool) string {
func (s *Plugin) addMiddlewares() {
if s.http != nil {
- for i := 0; i < len(s.mdwr); i++ {
- s.http.Handler = s.mdwr[i].Middleware(s.http.Handler)
- }
+ applyMiddlewares(s.http, s.mdwr, s.cfg.Middleware, s.log)
}
if s.https != nil {
- for i := 0; i < len(s.mdwr); i++ {
- s.https.Handler = s.mdwr[i].Middleware(s.https.Handler)
- }
+ applyMiddlewares(s.https, s.mdwr, s.cfg.Middleware, s.log)
}
if s.fcgi != nil {
- for i := 0; i < len(s.mdwr); i++ {
- s.fcgi.Handler = s.mdwr[i].Middleware(s.fcgi.Handler)
+ applyMiddlewares(s.fcgi, s.mdwr, s.cfg.Middleware, s.log)
+ }
+}
+
+func applyMiddlewares(server *http.Server, middlewares map[string]Middleware, order []string, log log.Logger) {
+ for i := 0; i < len(order); i++ {
+ if mdwr, ok := middlewares[order[i]]; ok {
+ server.Handler = mdwr.Middleware(server.Handler)
+ } else {
+ log.Warn("requested middleware does not exist", "requested", order[i])
}
}
}
diff --git a/plugins/http/tests/configs/.rr-echoErr.yaml b/plugins/http/tests/configs/.rr-echoErr.yaml
index 696fc0ae..6ecdbb2a 100644
--- a/plugins/http/tests/configs/.rr-echoErr.yaml
+++ b/plugins/http/tests/configs/.rr-echoErr.yaml
@@ -15,7 +15,7 @@ http:
debug: true
address: 127.0.0.1:8080
maxRequestSize: 1024
- middleware: [ "" ]
+ middleware: [ "pluginMiddleware", "pluginMiddleware2" ]
uploads:
forbid: [ "" ]
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" ]
diff --git a/plugins/http/tests/configs/.rr-http.yaml b/plugins/http/tests/configs/.rr-http.yaml
index c907c5e7..7b91f735 100644
--- a/plugins/http/tests/configs/.rr-http.yaml
+++ b/plugins/http/tests/configs/.rr-http.yaml
@@ -15,7 +15,7 @@ http:
debug: true
address: 127.0.0.1:18903
maxRequestSize: 1024
- middleware: [ "" ]
+ middleware: [ "pluginMiddleware", "pluginMiddleware2" ]
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" ]