summaryrefslogtreecommitdiff
path: root/plugins/http/tests
diff options
context:
space:
mode:
authorValery Piashchynski <[email protected]>2020-11-19 21:34:22 +0300
committerValery Piashchynski <[email protected]>2020-11-19 21:34:22 +0300
commitbc0be9c17220220ae9b40b65e874701edaecd8ce (patch)
tree6ee032eee330f8bc8824e426bab1846c9479333c /plugins/http/tests
parent729c19af8c410a28b7b46c134fd7fe9608cd73b1 (diff)
Add SSL tests
Diffstat (limited to 'plugins/http/tests')
-rw-r--r--plugins/http/tests/config_test.go330
-rw-r--r--plugins/http/tests/configs/.rr-fcgi.yaml35
-rw-r--r--plugins/http/tests/configs/.rr-h2c.yaml35
-rw-r--r--plugins/http/tests/configs/.rr-handler-echo.yaml0
-rw-r--r--plugins/http/tests/configs/.rr-ssl.yaml35
-rw-r--r--plugins/http/tests/fcgi_test.go104
-rw-r--r--plugins/http/tests/h2c_test.go81
-rw-r--r--plugins/http/tests/http_test.go227
-rw-r--r--plugins/http/tests/plugin_test.go759
-rw-r--r--plugins/http/tests/response_test.go163
-rw-r--r--plugins/http/tests/rpc_test.go222
-rw-r--r--plugins/http/tests/ssl_test.go178
-rw-r--r--plugins/http/tests/uploads_config_test.go26
13 files changed, 2144 insertions, 51 deletions
diff --git a/plugins/http/tests/config_test.go b/plugins/http/tests/config_test.go
new file mode 100644
index 00000000..068bd66e
--- /dev/null
+++ b/plugins/http/tests/config_test.go
@@ -0,0 +1,330 @@
+package tests
+
+//import (
+// "os"
+// "testing"
+// "time"
+//
+// json "github.com/json-iterator/go"
+// "github.com/stretchr/testify/assert"
+//)
+//
+//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{`{"address": "localhost:8080"}`}
+// 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))
+//}
+//
+//func Test_Config_Valid(t *testing.T) {
+// cfg := &Config{
+// Address: ":8080",
+// MaxRequestSize: 1024,
+// HTTP2: &HTTP2Config{
+// Enabled: true,
+// },
+// Uploads: &UploadsConfig{
+// Dir: os.TempDir(),
+// Forbid: []string{".go"},
+// },
+// Workers: &roadrunner.ServerConfig{
+// Command: "php tests/client.php echo pipes",
+// Relay: "pipes",
+// Pool: &roadrunner.Config{
+// NumWorkers: 1,
+// AllocateTimeout: time.Second,
+// DestroyTimeout: time.Second,
+// },
+// },
+// }
+//
+// assert.NoError(t, cfg.Valid())
+//}
+//
+//func Test_Trusted_Subnets(t *testing.T) {
+// cfg := &Config{
+// Address: ":8080",
+// MaxRequestSize: 1024,
+// Uploads: &UploadsConfig{
+// Dir: os.TempDir(),
+// Forbid: []string{".go"},
+// },
+// HTTP2: &HTTP2Config{
+// Enabled: true,
+// },
+// TrustedSubnets: []string{"200.1.0.0/16"},
+// Workers: &roadrunner.ServerConfig{
+// Command: "php tests/client.php echo pipes",
+// Relay: "pipes",
+// Pool: &roadrunner.Config{
+// NumWorkers: 1,
+// AllocateTimeout: time.Second,
+// DestroyTimeout: time.Second,
+// },
+// },
+// }
+//
+// assert.NoError(t, cfg.parseCIDRs())
+//
+// assert.True(t, cfg.IsTrusted("200.1.0.10"))
+// assert.False(t, cfg.IsTrusted("127.0.0.0.1"))
+//}
+//
+//func Test_Trusted_Subnets_Err(t *testing.T) {
+// cfg := &Config{
+// Address: ":8080",
+// MaxRequestSize: 1024,
+// Uploads: &UploadsConfig{
+// Dir: os.TempDir(),
+// Forbid: []string{".go"},
+// },
+// HTTP2: &HTTP2Config{
+// Enabled: true,
+// },
+// TrustedSubnets: []string{"200.1.0.0"},
+// Workers: &roadrunner.ServerConfig{
+// Command: "php tests/client.php echo pipes",
+// Relay: "pipes",
+// Pool: &roadrunner.Config{
+// NumWorkers: 1,
+// AllocateTimeout: time.Second,
+// DestroyTimeout: time.Second,
+// },
+// },
+// }
+//
+// assert.Error(t, cfg.parseCIDRs())
+//}
+//
+//func Test_Config_Valid_SSL(t *testing.T) {
+// cfg := &Config{
+// Address: ":8080",
+// SSL: SSLConfig{
+// Cert: "fixtures/server.crt",
+// Key: "fixtures/server.key",
+// },
+// MaxRequestSize: 1024,
+// Uploads: &UploadsConfig{
+// Dir: os.TempDir(),
+// Forbid: []string{".go"},
+// },
+// HTTP2: &HTTP2Config{
+// Enabled: true,
+// },
+// Workers: &roadrunner.ServerConfig{
+// Command: "php tests/client.php echo pipes",
+// Relay: "pipes",
+// Pool: &roadrunner.Config{
+// NumWorkers: 1,
+// AllocateTimeout: time.Second,
+// DestroyTimeout: time.Second,
+// },
+// },
+// }
+//
+// assert.Error(t, cfg.Hydrate(&testCfg{httpCfg: "{}"}))
+//
+// assert.NoError(t, cfg.Valid())
+// assert.True(t, cfg.EnableTLS())
+// assert.Equal(t, 443, cfg.SSL.Port)
+//}
+//
+//func Test_Config_SSL_No_key(t *testing.T) {
+// cfg := &Config{
+// Address: ":8080",
+// SSL: SSLConfig{
+// Cert: "fixtures/server.crt",
+// },
+// MaxRequestSize: 1024,
+// Uploads: &UploadsConfig{
+// Dir: os.TempDir(),
+// Forbid: []string{".go"},
+// },
+// HTTP2: &HTTP2Config{
+// Enabled: true,
+// },
+// Workers: &roadrunner.ServerConfig{
+// Command: "php tests/client.php echo pipes",
+// Relay: "pipes",
+// Pool: &roadrunner.Config{
+// NumWorkers: 1,
+// AllocateTimeout: time.Second,
+// DestroyTimeout: time.Second,
+// },
+// },
+// }
+//
+// assert.Error(t, cfg.Valid())
+//}
+//
+//func Test_Config_SSL_No_Cert(t *testing.T) {
+// cfg := &Config{
+// Address: ":8080",
+// SSL: SSLConfig{
+// Key: "fixtures/server.key",
+// },
+// MaxRequestSize: 1024,
+// Uploads: &UploadsConfig{
+// Dir: os.TempDir(),
+// Forbid: []string{".go"},
+// },
+// HTTP2: &HTTP2Config{
+// Enabled: true,
+// },
+// Workers: &roadrunner.ServerConfig{
+// Command: "php tests/client.php echo pipes",
+// Relay: "pipes",
+// Pool: &roadrunner.Config{
+// NumWorkers: 1,
+// AllocateTimeout: time.Second,
+// DestroyTimeout: time.Second,
+// },
+// },
+// }
+//
+// assert.Error(t, cfg.Valid())
+//}
+//
+//func Test_Config_NoUploads(t *testing.T) {
+// cfg := &Config{
+// Address: ":8080",
+// MaxRequestSize: 1024,
+// HTTP2: &HTTP2Config{
+// Enabled: true,
+// },
+// Workers: &roadrunner.ServerConfig{
+// Command: "php tests/client.php echo pipes",
+// Relay: "pipes",
+// Pool: &roadrunner.Config{
+// NumWorkers: 1,
+// AllocateTimeout: time.Second,
+// DestroyTimeout: time.Second,
+// },
+// },
+// }
+//
+// assert.Error(t, cfg.Valid())
+//}
+//
+//func Test_Config_NoHTTP2(t *testing.T) {
+// cfg := &Config{
+// Address: ":8080",
+// MaxRequestSize: 1024,
+// Uploads: &UploadsConfig{
+// Dir: os.TempDir(),
+// Forbid: []string{".go"},
+// },
+// Workers: &roadrunner.ServerConfig{
+// Command: "php tests/client.php echo pipes",
+// Relay: "pipes",
+// Pool: &roadrunner.Config{
+// NumWorkers: 0,
+// AllocateTimeout: time.Second,
+// DestroyTimeout: time.Second,
+// },
+// },
+// }
+//
+// assert.Error(t, cfg.Valid())
+//}
+//
+//func Test_Config_NoWorkers(t *testing.T) {
+// cfg := &Config{
+// Address: ":8080",
+// MaxRequestSize: 1024,
+// HTTP2: &HTTP2Config{
+// Enabled: true,
+// },
+// Uploads: &UploadsConfig{
+// Dir: os.TempDir(),
+// Forbid: []string{".go"},
+// },
+// }
+//
+// assert.Error(t, cfg.Valid())
+//}
+//
+//func Test_Config_NoPool(t *testing.T) {
+// cfg := &Config{
+// Address: ":8080",
+// MaxRequestSize: 1024,
+// Uploads: &UploadsConfig{
+// Dir: os.TempDir(),
+// Forbid: []string{".go"},
+// },
+// HTTP2: &HTTP2Config{
+// Enabled: true,
+// },
+// Workers: &roadrunner.ServerConfig{
+// Command: "php tests/client.php echo pipes",
+// Relay: "pipes",
+// Pool: &roadrunner.Config{
+// NumWorkers: 0,
+// AllocateTimeout: time.Second,
+// DestroyTimeout: time.Second,
+// },
+// },
+// }
+//
+// assert.Error(t, cfg.Valid())
+//}
+//
+//func Test_Config_DeadPool(t *testing.T) {
+// cfg := &Config{
+// Address: ":8080",
+// MaxRequestSize: 1024,
+// Uploads: &UploadsConfig{
+// Dir: os.TempDir(),
+// Forbid: []string{".go"},
+// },
+// HTTP2: &HTTP2Config{
+// Enabled: true,
+// },
+// Workers: &roadrunner.ServerConfig{
+// Command: "php tests/client.php echo pipes",
+// Relay: "pipes",
+// },
+// }
+//
+// assert.Error(t, cfg.Valid())
+//}
+//
+//func Test_Config_InvalidAddress(t *testing.T) {
+// cfg := &Config{
+// Address: "unexpected_address",
+// MaxRequestSize: 1024,
+// Uploads: &UploadsConfig{
+// Dir: os.TempDir(),
+// Forbid: []string{".go"},
+// },
+// HTTP2: &HTTP2Config{
+// Enabled: true,
+// },
+// Workers: &roadrunner.ServerConfig{
+// Command: "php tests/client.php echo pipes",
+// Relay: "pipes",
+// Pool: &roadrunner.Config{
+// NumWorkers: 1,
+// AllocateTimeout: time.Second,
+// DestroyTimeout: time.Second,
+// },
+// },
+// }
+//
+// assert.Error(t, cfg.Valid())
+//}
diff --git a/plugins/http/tests/configs/.rr-fcgi.yaml b/plugins/http/tests/configs/.rr-fcgi.yaml
new file mode 100644
index 00000000..cdfdd969
--- /dev/null
+++ b/plugins/http/tests/configs/.rr-fcgi.yaml
@@ -0,0 +1,35 @@
+server:
+ command: "php ../../../tests/http/client.php echo pipes"
+ user: ""
+ group: ""
+ env:
+ "RR_HTTP": "true"
+ relay: "pipes"
+ relayTimeout: "20s"
+
+http:
+ debug: true
+ address: :8080
+ 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: 1
+ maxJobs: 0
+ allocateTimeout: 60s
+ destroyTimeout: 60s
+
+ ssl:
+ port: 8888
+ redirect: false
+ 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 \ No newline at end of file
diff --git a/plugins/http/tests/configs/.rr-h2c.yaml b/plugins/http/tests/configs/.rr-h2c.yaml
new file mode 100644
index 00000000..cdfdd969
--- /dev/null
+++ b/plugins/http/tests/configs/.rr-h2c.yaml
@@ -0,0 +1,35 @@
+server:
+ command: "php ../../../tests/http/client.php echo pipes"
+ user: ""
+ group: ""
+ env:
+ "RR_HTTP": "true"
+ relay: "pipes"
+ relayTimeout: "20s"
+
+http:
+ debug: true
+ address: :8080
+ 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: 1
+ maxJobs: 0
+ allocateTimeout: 60s
+ destroyTimeout: 60s
+
+ ssl:
+ port: 8888
+ redirect: false
+ 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 \ No newline at end of file
diff --git a/plugins/http/tests/configs/.rr-handler-echo.yaml b/plugins/http/tests/configs/.rr-handler-echo.yaml
deleted file mode 100644
index e69de29b..00000000
--- a/plugins/http/tests/configs/.rr-handler-echo.yaml
+++ /dev/null
diff --git a/plugins/http/tests/configs/.rr-ssl.yaml b/plugins/http/tests/configs/.rr-ssl.yaml
new file mode 100644
index 00000000..cdfdd969
--- /dev/null
+++ b/plugins/http/tests/configs/.rr-ssl.yaml
@@ -0,0 +1,35 @@
+server:
+ command: "php ../../../tests/http/client.php echo pipes"
+ user: ""
+ group: ""
+ env:
+ "RR_HTTP": "true"
+ relay: "pipes"
+ relayTimeout: "20s"
+
+http:
+ debug: true
+ address: :8080
+ 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: 1
+ maxJobs: 0
+ allocateTimeout: 60s
+ destroyTimeout: 60s
+
+ ssl:
+ port: 8888
+ redirect: false
+ 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 \ No newline at end of file
diff --git a/plugins/http/tests/fcgi_test.go b/plugins/http/tests/fcgi_test.go
new file mode 100644
index 00000000..0a0b2b41
--- /dev/null
+++ b/plugins/http/tests/fcgi_test.go
@@ -0,0 +1,104 @@
+package tests
+
+//
+//import (
+// "io/ioutil"
+// "net/http/fcgi"
+// "net/http/httptest"
+// "testing"
+// "time"
+//
+// "github.com/stretchr/testify/assert"
+//)
+//
+//func Test_FCGI_Service_Echo(t *testing.T) {
+// logger, _ := test.NewNullLogger()
+// logger.SetLevel(logrus.DebugLevel)
+//
+// c := service.NewContainer(logger)
+// c.Register(ID, &Service{})
+//
+// assert.NoError(t, c.Init(&testCfg{httpCfg: `{
+// "fcgi": {
+// "address": "tcp://0.0.0.0:6082"
+// },
+// "workers":{
+// "command": "php ../../tests/http/client.php echo pipes",
+// "pool": {"numWorkers": 1}
+// }
+// }`}))
+//
+// s, st := c.Get(ID)
+// assert.NotNil(t, s)
+// assert.Equal(t, service.StatusOK, st)
+//
+// // should do nothing
+// s.(*Service).Stop()
+//
+// go func() { assert.NoError(t, c.Serve()) }()
+// time.Sleep(time.Second * 1)
+//
+// fcgiConnFactory := gofast.SimpleConnFactory("tcp", "0.0.0.0:6082")
+//
+// fcgiHandler := gofast.NewHandler(
+// gofast.BasicParamsMap(gofast.BasicSession),
+// gofast.SimpleClientFactory(fcgiConnFactory, 0),
+// )
+//
+// w := httptest.NewRecorder()
+// req := httptest.NewRequest("GET", "http://site.local/?hello=world", nil)
+// fcgiHandler.ServeHTTP(w, req)
+//
+// body, err := ioutil.ReadAll(w.Result().Body)
+//
+// assert.NoError(t, err)
+// assert.Equal(t, 201, w.Result().StatusCode)
+// assert.Equal(t, "WORLD", string(body))
+// c.Stop()
+//}
+//
+//func Test_FCGI_Service_Request_Uri(t *testing.T) {
+// logger, _ := test.NewNullLogger()
+// logger.SetLevel(logrus.DebugLevel)
+//
+// c := service.NewContainer(logger)
+// c.Register(ID, &Service{})
+//
+// assert.NoError(t, c.Init(&testCfg{httpCfg: `{
+// "fcgi": {
+// "address": "tcp://0.0.0.0:6083"
+// },
+// "workers":{
+// "command": "php ../../tests/http/client.php request-uri pipes",
+// "pool": {"numWorkers": 1}
+// }
+// }`}))
+//
+// s, st := c.Get(ID)
+// assert.NotNil(t, s)
+// assert.Equal(t, service.StatusOK, st)
+//
+// // should do nothing
+// s.(*Service).Stop()
+//
+// go func() { assert.NoError(t, c.Serve()) }()
+// time.Sleep(time.Second * 1)
+//
+// fcgiConnFactory := gofast.SimpleConnFactory("tcp", "0.0.0.0:6083")
+//
+// fcgiHandler := gofast.NewHandler(
+// gofast.BasicParamsMap(gofast.BasicSession),
+// gofast.SimpleClientFactory(fcgiConnFactory, 0),
+// )
+//
+// w := httptest.NewRecorder()
+// req := httptest.NewRequest("GET", "http://site.local/hello-world", nil)
+// fcgiHandler.ServeHTTP(w, req)
+//
+// body, err := ioutil.ReadAll(w.Result().Body)
+//
+// assert.NoError(t, err)
+// assert.Equal(t, 200, w.Result().StatusCode)
+// assert.Equal(t, "http://site.local/hello-world", string(body))
+// c.Stop()
+//}
diff --git a/plugins/http/tests/h2c_test.go b/plugins/http/tests/h2c_test.go
new file mode 100644
index 00000000..c85ce942
--- /dev/null
+++ b/plugins/http/tests/h2c_test.go
@@ -0,0 +1,81 @@
+package tests
+
+//
+//import (
+// "net/http"
+// "testing"
+// "time"
+//
+// "github.com/cenkalti/backoff/v4"
+// "github.com/stretchr/testify/assert"
+//)
+//
+//func Test_Service_H2C(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(ID, &Service{})
+//
+// err := c.Init(&testCfg{httpCfg: `{
+// "address": ":6029",
+// "http2": {"h2c":true},
+// "workers":{
+// "command": "php ../../tests/http/client.php echo pipes",
+// "relay": "pipes",
+// "pool": {
+// "numWorkers": 1
+// }
+// }
+// }`})
+// if err != nil {
+// return err
+// }
+//
+// s, st := c.Get(ID)
+// assert.NotNil(t, s)
+// assert.Equal(t, service.StatusOK, st)
+//
+// // should do nothing
+// s.(*Service).Stop()
+//
+// go func() {
+// err := c.Serve()
+// if err != nil {
+// t.Errorf("error serving: %v", err)
+// }
+// }()
+// time.Sleep(time.Millisecond * 100)
+// defer c.Stop()
+//
+// req, err := http.NewRequest("PRI", "http://localhost:6029?hello=world", nil)
+// if err != nil {
+// return err
+// }
+//
+// req.Header.Add("Upgrade", "h2c")
+// req.Header.Add("Connection", "HTTP2-Settings")
+// req.Header.Add("HTTP2-Settings", "")
+//
+// r, err2 := http.DefaultClient.Do(req)
+// if err2 != nil {
+// return err2
+// }
+//
+// assert.Equal(t, "101 Switching Protocols", r.Status)
+//
+// err3 := r.Body.Close()
+// if err3 != nil {
+// return err3
+// }
+// return nil
+// }, bkoff)
+//
+// if err != nil {
+// t.Fatal(err)
+// }
+//}
diff --git a/plugins/http/tests/http_test.go b/plugins/http/tests/http_test.go
index ae9f2bf2..4f43ca1e 100644
--- a/plugins/http/tests/http_test.go
+++ b/plugins/http/tests/http_test.go
@@ -2,8 +2,11 @@ package tests
import (
"bytes"
+ "crypto/tls"
"io/ioutil"
+ "net"
"net/http"
+ "net/http/httptest"
"os"
"os/signal"
"syscall"
@@ -20,6 +23,14 @@ import (
"github.com/stretchr/testify/assert"
)
+var sslClient = &http.Client{
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{
+ InsecureSkipVerify: true,
+ },
+ },
+}
+
func TestHTTPInit(t *testing.T) {
cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.DebugLevel), endure.Visualize(endure.StdOut, ""))
assert.NoError(t, err)
@@ -49,7 +60,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 * 10)
+ tt := time.NewTimer(time.Second * 1000)
for {
select {
case e := <-ch:
@@ -75,61 +86,175 @@ 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",
- //}
+func TestSSL(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-ssl.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.Second * 1000)
+ 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("SSLEcho", sslEcho)
+}
+
+func sslEcho(t *testing.T) {
+ req, err := http.NewRequest("GET", "https://localhost:8888?hello=world", nil)
+ assert.NoError(t, err)
+
+ r, err := sslClient.Do(req)
+ assert.NoError(t, err)
+
+ b, err := ioutil.ReadAll(r.Body)
+ assert.NoError(t, err)
+
+ assert.NoError(t, err)
+ assert.Equal(t, 201, r.StatusCode)
+ assert.Equal(t, "WORLD", string(b))
+
+ err2 := r.Body.Close()
+ if err2 != nil {
+ t.Errorf("fail to close the Body: error %v", err2)
+ }
+}
+
+func TestFastCGI(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-fcgi.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.Second * 1000)
+ 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("FastCGIEcho", fcgiEcho)
+}
+
+func fcgiEcho(t *testing.T) {
+ conn, err := net.Dial("tcp", "0.0.0.0:6920")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = conn.Write([]byte("data"))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ //fcgiConnFactory := gofast.SimpleConnFactory("tcp", "0.0.0.0:6920")
//
- //err = cont.RegisterAll(
- // cfg,
- // &rpcPlugin.Plugin{},
- // &logger.ZapLogger{},
- // &server.Plugin{},
- // &httpPlugin.Plugin{},
+ //fcgiHandler := gofast.NewHandler(
+ // gofast.BasicParamsMap(gofast.BasicSession),
+ // gofast.SimpleClientFactory(fcgiConnFactory, 0),
//)
- //assert.NoError(t, err)
- //
- //err = cont.Init()
+
+ w := httptest.NewRecorder()
+ //req := httptest.NewRequest("GET", "http://site.local/?hello=world", nil)
+ //err = fcgi.Serve(, req)
//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
- // }
- // }
- //}()
+ //fcgiHandler.ServeHTTP(w, req)
+
+ body, err := ioutil.ReadAll(w.Result().Body)
+
+ assert.NoError(t, err)
+ assert.Equal(t, 201, w.Result().StatusCode)
+ assert.Equal(t, "WORLD", string(body))
}
func get(url string) (string, *http.Response, error) {
diff --git a/plugins/http/tests/plugin_test.go b/plugins/http/tests/plugin_test.go
new file mode 100644
index 00000000..852e5545
--- /dev/null
+++ b/plugins/http/tests/plugin_test.go
@@ -0,0 +1,759 @@
+package tests
+
+//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"
+// "github.com/spiral/roadrunner/service"
+// "github.com/spiral/roadrunner/service/env"
+// "github.com/spiral/roadrunner/service/rpc"
+// "github.com/stretchr/testify/assert"
+// "io/ioutil"
+// "net/http"
+// "os"
+// "testing"
+// "time"
+//)
+//
+//type testCfg struct {
+// httpCfg string
+// rpcCfg string
+// envCfg string
+// target string
+//}
+//
+//func (cfg *testCfg) Get(name string) service.Config {
+// if name == ID {
+// if cfg.httpCfg == "" {
+// return nil
+// }
+//
+// return &testCfg{target: cfg.httpCfg}
+// }
+//
+// if name == rpc.ID {
+// return &testCfg{target: cfg.rpcCfg}
+// }
+//
+// if name == env.ID {
+// return &testCfg{target: cfg.envCfg}
+// }
+//
+// return nil
+//}
+//func (cfg *testCfg) Unmarshal(out interface{}) error {
+// j := json.ConfigCompatibleWithStandardLibrary
+// return j.Unmarshal([]byte(cfg.target), out)
+//}
+//
+//func Test_Service_NoConfig(t *testing.T) {
+// logger, _ := test.NewNullLogger()
+// logger.SetLevel(logrus.DebugLevel)
+//
+// c := service.NewContainer(logger)
+// c.Register(ID, &Service{})
+//
+// err := c.Init(&testCfg{httpCfg: `{"Enable":true}`})
+// assert.Error(t, err)
+//
+// s, st := c.Get(ID)
+// assert.NotNil(t, s)
+// assert.Equal(t, service.StatusInactive, st)
+//}
+//
+//func Test_Service_Configure_Disable(t *testing.T) {
+// logger, _ := test.NewNullLogger()
+// logger.SetLevel(logrus.DebugLevel)
+//
+// c := service.NewContainer(logger)
+// c.Register(ID, &Service{})
+//
+// assert.NoError(t, c.Init(&testCfg{}))
+//
+// s, st := c.Get(ID)
+// assert.NotNil(t, s)
+// assert.Equal(t, service.StatusInactive, st)
+//}
+//
+//func Test_Service_Configure_Enable(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(ID, &Service{})
+//
+// err := c.Init(&testCfg{httpCfg: `{
+// "enable": true,
+// "address": ":8070",
+// "maxRequestSize": 1024,
+// "uploads": {
+// "dir": ` + tmpDir() + `,
+// "forbid": []
+// },
+// "workers":{
+// "command": "php ../../tests/http/client.php echo pipes",
+// "relay": "pipes",
+// "pool": {
+// "numWorkers": 1,
+// "allocateTimeout": 10000000,
+// "destroyTimeout": 10000000
+// }
+// }
+// }`})
+// if err != nil {
+// return err
+// }
+//
+// s, st := c.Get(ID)
+// assert.NotNil(t, s)
+// assert.Equal(t, service.StatusOK, st)
+//
+// return nil
+// }, bkoff)
+//
+// if err != nil {
+// t.Fatal(err)
+// }
+//
+//}
+//
+//func Test_Service_Echo(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(ID, &Service{})
+//
+// err := c.Init(&testCfg{httpCfg: `{
+// "enable": true,
+// "address": ":6536",
+// "maxRequestSize": 1024,
+// "uploads": {
+// "dir": ` + tmpDir() + `,
+// "forbid": []
+// },
+// "workers":{
+// "command": "php ../../tests/http/client.php echo pipes",
+// "relay": "pipes",
+// "pool": {
+// "numWorkers": 1,
+// "allocateTimeout": 10000000,
+// "destroyTimeout": 10000000
+// }
+// }
+// }`})
+// if err != nil {
+// return err
+// }
+//
+// s, st := c.Get(ID)
+// assert.NotNil(t, s)
+// assert.Equal(t, service.StatusOK, st)
+//
+// // should do nothing
+// s.(*Service).Stop()
+//
+// go func() {
+// err := c.Serve()
+// if err != nil {
+// t.Errorf("serve error: %v", err)
+// }
+// }()
+//
+// time.Sleep(time.Millisecond * 100)
+//
+// req, err := http.NewRequest("GET", "http://localhost:6536?hello=world", nil)
+// if err != nil {
+// c.Stop()
+// return err
+// }
+//
+// r, err := http.DefaultClient.Do(req)
+// if err != nil {
+// c.Stop()
+// return err
+// }
+// b, err := ioutil.ReadAll(r.Body)
+// if err != nil {
+// c.Stop()
+// return err
+// }
+// assert.Equal(t, 201, r.StatusCode)
+// assert.Equal(t, "WORLD", string(b))
+//
+// err = r.Body.Close()
+// if err != nil {
+// c.Stop()
+// return err
+// }
+//
+// c.Stop()
+// return nil
+// }, bkoff)
+//
+// if err != nil {
+// t.Fatal(err)
+// }
+//}
+//
+//func Test_Service_Env(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(env.ID, env.NewService(map[string]string{"pool": "test"}))
+// c.Register(ID, &Service{})
+//
+// err := c.Init(&testCfg{httpCfg: `{
+// "enable": true,
+// "address": ":10031",
+// "maxRequestSize": 1024,
+// "uploads": {
+// "dir": ` + tmpDir() + `,
+// "forbid": []
+// },
+// "workers":{
+// "command": "php ../../tests/http/client.php env pipes",
+// "relay": "pipes",
+// "pool": {
+// "numWorkers": 1,
+// "allocateTimeout": 10000000,
+// "destroyTimeout": 10000000
+// }
+// }
+// }`, envCfg: `{"env_key":"ENV_VALUE"}`})
+// if err != nil {
+// return err
+// }
+//
+// s, st := c.Get(ID)
+// assert.NotNil(t, s)
+// assert.Equal(t, service.StatusOK, st)
+//
+// // should do nothing
+// s.(*Service).Stop()
+//
+// go func() {
+// err := c.Serve()
+// if err != nil {
+// t.Errorf("serve error: %v", err)
+// }
+// }()
+//
+// time.Sleep(time.Millisecond * 500)
+//
+// req, err := http.NewRequest("GET", "http://localhost:10031", nil)
+// if err != nil {
+// c.Stop()
+// return err
+// }
+//
+// r, err := http.DefaultClient.Do(req)
+// if err != nil {
+// c.Stop()
+// return err
+// }
+//
+// b, err := ioutil.ReadAll(r.Body)
+// if err != nil {
+// c.Stop()
+// return err
+// }
+//
+// assert.Equal(t, 200, r.StatusCode)
+// assert.Equal(t, "ENV_VALUE", string(b))
+//
+// err = r.Body.Close()
+// if err != nil {
+// c.Stop()
+// return err
+// }
+//
+// c.Stop()
+// return nil
+// }, bkoff)
+//
+// if err != nil {
+// t.Fatal(err)
+// }
+//
+//}
+//
+//func Test_Service_ErrorEcho(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(ID, &Service{})
+//
+// err := c.Init(&testCfg{httpCfg: `{
+// "enable": true,
+// "address": ":6030",
+// "maxRequestSize": 1024,
+// "uploads": {
+// "dir": ` + tmpDir() + `,
+// "forbid": []
+// },
+// "workers":{
+// "command": "php ../../tests/http/client.php echoerr pipes",
+// "relay": "pipes",
+// "pool": {
+// "numWorkers": 1,
+// "allocateTimeout": 10000000,
+// "destroyTimeout": 10000000
+// }
+// }
+// }`})
+// if err != nil {
+// return err
+// }
+//
+// s, st := c.Get(ID)
+// assert.NotNil(t, s)
+// assert.Equal(t, service.StatusOK, st)
+//
+// goterr := make(chan interface{})
+// s.(*Service).AddListener(func(event int, ctx interface{}) {
+// if event == roadrunner.EventStderrOutput {
+// if string(ctx.([]byte)) == "WORLD\n" {
+// goterr <- nil
+// }
+// }
+// })
+//
+// go func() {
+// err := c.Serve()
+// if err != nil {
+// t.Errorf("serve error: %v", err)
+// }
+// }()
+//
+// time.Sleep(time.Millisecond * 500)
+//
+// req, err := http.NewRequest("GET", "http://localhost:6030?hello=world", nil)
+// if err != nil {
+// c.Stop()
+// return err
+// }
+//
+// r, err := http.DefaultClient.Do(req)
+// if err != nil {
+// c.Stop()
+// return err
+// }
+//
+// b, err := ioutil.ReadAll(r.Body)
+// if err != nil {
+// c.Stop()
+// return err
+// }
+//
+// <-goterr
+//
+// assert.Equal(t, 201, r.StatusCode)
+// assert.Equal(t, "WORLD", string(b))
+// err = r.Body.Close()
+// if err != nil {
+// c.Stop()
+// return err
+// }
+//
+// c.Stop()
+//
+// return nil
+// }, bkoff)
+//
+// if err != nil {
+// t.Fatal(err)
+// }
+//}
+//
+//func Test_Service_Middleware(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(ID, &Service{})
+//
+// err := c.Init(&testCfg{httpCfg: `{
+// "enable": true,
+// "address": ":6032",
+// "maxRequestSize": 1024,
+// "uploads": {
+// "dir": ` + tmpDir() + `,
+// "forbid": []
+// },
+// "workers":{
+// "command": "php ../../tests/http/client.php echo pipes",
+// "relay": "pipes",
+// "pool": {
+// "numWorkers": 1,
+// "allocateTimeout": 10000000,
+// "destroyTimeout": 10000000
+// }
+// }
+// }`})
+// if err != nil {
+// return err
+// }
+//
+// s, st := c.Get(ID)
+// assert.NotNil(t, s)
+// assert.Equal(t, service.StatusOK, st)
+//
+// s.(*Service).AddMiddleware(func(f http.HandlerFunc) http.HandlerFunc {
+// return func(w http.ResponseWriter, r *http.Request) {
+// if r.URL.Path == "/halt" {
+// w.WriteHeader(500)
+// _, err := w.Write([]byte("halted"))
+// if err != nil {
+// t.Errorf("error writing the data to the http reply: error %v", err)
+// }
+// } else {
+// f(w, r)
+// }
+// }
+// })
+//
+// go func() {
+// err := c.Serve()
+// if err != nil {
+// t.Errorf("serve error: %v", err)
+// }
+// }()
+// time.Sleep(time.Millisecond * 500)
+//
+// req, err := http.NewRequest("GET", "http://localhost:6032?hello=world", nil)
+// if err != nil {
+// c.Stop()
+// return err
+// }
+//
+// r, err := http.DefaultClient.Do(req)
+// if err != nil {
+// c.Stop()
+// return err
+// }
+//
+// b, err := ioutil.ReadAll(r.Body)
+// if err != nil {
+// c.Stop()
+// return err
+// }
+//
+// assert.Equal(t, 201, r.StatusCode)
+// assert.Equal(t, "WORLD", string(b))
+//
+// err = r.Body.Close()
+// if err != nil {
+// c.Stop()
+// return err
+// }
+//
+// req, err = http.NewRequest("GET", "http://localhost:6032/halt", nil)
+// if err != nil {
+// c.Stop()
+// return err
+// }
+//
+// r, err = http.DefaultClient.Do(req)
+// if err != nil {
+// c.Stop()
+// return err
+// }
+// b, err = ioutil.ReadAll(r.Body)
+// if err != nil {
+// c.Stop()
+// return err
+// }
+//
+// assert.Equal(t, 500, r.StatusCode)
+// assert.Equal(t, "halted", string(b))
+//
+// err = r.Body.Close()
+// if err != nil {
+// c.Stop()
+// return err
+// }
+// c.Stop()
+//
+// return nil
+// }, bkoff)
+//
+// if err != nil {
+// t.Fatal(err)
+// }
+//
+//}
+//
+//func Test_Service_Listener(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(ID, &Service{})
+//
+// err := c.Init(&testCfg{httpCfg: `{
+// "enable": true,
+// "address": ":6033",
+// "maxRequestSize": 1024,
+// "uploads": {
+// "dir": ` + tmpDir() + `,
+// "forbid": []
+// },
+// "workers":{
+// "command": "php ../../tests/http/client.php echo pipes",
+// "relay": "pipes",
+// "pool": {
+// "numWorkers": 1,
+// "allocateTimeout": 10000000,
+// "destroyTimeout": 10000000
+// }
+// }
+// }`})
+// if err != nil {
+// return err
+// }
+//
+// s, st := c.Get(ID)
+// assert.NotNil(t, s)
+// assert.Equal(t, service.StatusOK, st)
+//
+// stop := make(chan interface{})
+// s.(*Service).AddListener(func(event int, ctx interface{}) {
+// if event == roadrunner.EventServerStart {
+// stop <- nil
+// }
+// })
+//
+// go func() {
+// err := c.Serve()
+// if err != nil {
+// t.Errorf("serve error: %v", err)
+// }
+// }()
+// time.Sleep(time.Millisecond * 500)
+//
+// c.Stop()
+// assert.True(t, true)
+//
+// return nil
+// }, bkoff)
+//
+// if err != nil {
+// t.Fatal(err)
+// }
+//}
+//
+//func Test_Service_Error(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(ID, &Service{})
+//
+// err := c.Init(&testCfg{httpCfg: `{
+// "enable": true,
+// "address": ":6034",
+// "maxRequestSize": 1024,
+// "uploads": {
+// "dir": ` + tmpDir() + `,
+// "forbid": []
+// },
+// "workers":{
+// "command": "php ../../tests/http/client.php echo pipes",
+// "relay": "---",
+// "pool": {
+// "numWorkers": 1,
+// "allocateTimeout": 10000000,
+// "destroyTimeout": 10000000
+// }
+// }
+// }`})
+// if err != nil {
+// return err
+// }
+//
+// // assert error
+// err = c.Serve()
+// if err == nil {
+// return err
+// }
+//
+// return nil
+// }, bkoff)
+//
+// if err != nil {
+// t.Fatal(err)
+// }
+//}
+//
+//func Test_Service_Error2(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(ID, &Service{})
+//
+// err := c.Init(&testCfg{httpCfg: `{
+// "enable": true,
+// "address": ":6035",
+// "maxRequestSize": 1024,
+// "uploads": {
+// "dir": ` + tmpDir() + `,
+// "forbid": []
+// },
+// "workers":{
+// "command": "php ../../tests/http/client.php broken pipes",
+// "relay": "pipes",
+// "pool": {
+// "numWorkers": 1,
+// "allocateTimeout": 10000000,
+// "destroyTimeout": 10000000
+// }
+// }
+// }`})
+// if err != nil {
+// return err
+// }
+//
+// // assert error
+// err = c.Serve()
+// if err == nil {
+// return err
+// }
+//
+// return nil
+// }, bkoff)
+//
+// if err != nil {
+// t.Fatal(err)
+// }
+//}
+//
+//func Test_Service_Error3(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(ID, &Service{})
+//
+// err := c.Init(&testCfg{httpCfg: `{
+// "enable": true,
+// "address": ":6036",
+// "maxRequestSize": 1024,
+// "uploads": {
+// "dir": ` + tmpDir() + `,
+// "forbid": []
+// },
+// "workers"
+// "command": "php ../../tests/http/client.php broken pipes",
+// "relay": "pipes",
+// "pool": {
+// "numWorkers": 1,
+// "allocateTimeout": 10000000,
+// "destroyTimeout": 10000000
+// }
+// }
+// }`})
+// // assert error
+// if err == nil {
+// return err
+// }
+//
+// return nil
+// }, bkoff)
+//
+// if err != nil {
+// t.Fatal(err)
+// }
+//
+//}
+//
+//func Test_Service_Error4(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(ID, &Service{})
+//
+// err := c.Init(&testCfg{httpCfg: `{
+// "enable": true,
+// "address": "----",
+// "maxRequestSize": 1024,
+// "uploads": {
+// "dir": ` + tmpDir() + `,
+// "forbid": []
+// },
+// "workers":{
+// "command": "php ../../tests/http/client.php broken pipes",
+// "relay": "pipes",
+// "pool": {
+// "numWorkers": 1,
+// "allocateTimeout": 10000000,
+// "destroyTimeout": 10000000
+// }
+// }
+// }`})
+// // assert error
+// if err != nil {
+// return nil
+// }
+//
+// return err
+// }, bkoff)
+//
+// if err != nil {
+// t.Fatal(err)
+// }
+//}
+//
+//func tmpDir() string {
+// p := os.TempDir()
+// j := json.ConfigCompatibleWithStandardLibrary
+// r, _ := j.Marshal(p)
+//
+// return string(r)
+//}
diff --git a/plugins/http/tests/response_test.go b/plugins/http/tests/response_test.go
new file mode 100644
index 00000000..2bfe7d56
--- /dev/null
+++ b/plugins/http/tests/response_test.go
@@ -0,0 +1,163 @@
+package tests
+
+import (
+ "bytes"
+ "errors"
+ "net/http"
+ "testing"
+
+ "github.com/spiral/roadrunner/v2"
+ http2 "github.com/spiral/roadrunner/v2/plugins/http"
+ "github.com/stretchr/testify/assert"
+)
+
+type testWriter struct {
+ h http.Header
+ buf bytes.Buffer
+ wroteHeader bool
+ code int
+ err error
+ pushErr error
+ pushes []string
+}
+
+func (tw *testWriter) Header() http.Header { return tw.h }
+
+func (tw *testWriter) Write(p []byte) (int, error) {
+ if !tw.wroteHeader {
+ tw.WriteHeader(http.StatusOK)
+ }
+
+ n, e := tw.buf.Write(p)
+ if e == nil {
+ e = tw.err
+ }
+
+ return n, e
+}
+
+func (tw *testWriter) WriteHeader(code int) { tw.wroteHeader = true; tw.code = code }
+
+func (tw *testWriter) Push(target string, opts *http.PushOptions) error {
+ tw.pushes = append(tw.pushes, target)
+
+ return tw.pushErr
+}
+
+func TestNewResponse_Error(t *testing.T) {
+ r, err := http2.NewResponse(roadrunner.Payload{Context: []byte(`invalid payload`)})
+ assert.Error(t, err)
+ assert.Nil(t, r)
+}
+
+func TestNewResponse_Write(t *testing.T) {
+ r, err := http2.NewResponse(roadrunner.Payload{
+ Context: []byte(`{"headers":{"key":["value"]},"status": 301}`),
+ Body: []byte(`sample body`),
+ })
+
+ assert.NoError(t, err)
+ assert.NotNil(t, r)
+
+ w := &testWriter{h: http.Header(make(map[string][]string))}
+ assert.NoError(t, r.Write(w))
+
+ assert.Equal(t, 301, w.code)
+ assert.Equal(t, "value", w.h.Get("key"))
+ assert.Equal(t, "sample body", w.buf.String())
+}
+
+func TestNewResponse_Stream(t *testing.T) {
+ r, err := http2.NewResponse(roadrunner.Payload{
+ Context: []byte(`{"headers":{"key":["value"]},"status": 301}`),
+ })
+
+ // r is pointer, so, it might be nil
+ if r == nil {
+ t.Fatal("response is nil")
+ }
+
+ r.Body = &bytes.Buffer{}
+ r.Body.(*bytes.Buffer).WriteString("hello world")
+
+ assert.NoError(t, err)
+ assert.NotNil(t, r)
+
+ w := &testWriter{h: http.Header(make(map[string][]string))}
+ assert.NoError(t, r.Write(w))
+
+ assert.Equal(t, 301, w.code)
+ assert.Equal(t, "value", w.h.Get("key"))
+ assert.Equal(t, "hello world", w.buf.String())
+}
+
+func TestNewResponse_StreamError(t *testing.T) {
+ r, err := http2.NewResponse(roadrunner.Payload{
+ Context: []byte(`{"headers":{"key":["value"]},"status": 301}`),
+ })
+
+ // r is pointer, so, it might be nil
+ if r == nil {
+ t.Fatal("response is nil")
+ }
+
+ r.Body = &bytes.Buffer{}
+ r.Body.(*bytes.Buffer).WriteString("hello world")
+
+ assert.NoError(t, err)
+ assert.NotNil(t, r)
+
+ w := &testWriter{h: http.Header(make(map[string][]string)), err: errors.New("error")}
+ assert.Error(t, r.Write(w))
+}
+
+func TestWrite_HandlesPush(t *testing.T) {
+ r, err := http2.NewResponse(roadrunner.Payload{
+ Context: []byte(`{"headers":{"Http2-Push":["/test.js"],"content-type":["text/html"]},"status": 200}`),
+ })
+
+ assert.NoError(t, err)
+ assert.NotNil(t, r)
+
+ w := &testWriter{h: http.Header(make(map[string][]string))}
+ assert.NoError(t, r.Write(w))
+
+ assert.Nil(t, w.h["Http2-Push"])
+ assert.Equal(t, []string{"/test.js"}, w.pushes)
+}
+
+func TestWrite_HandlesTrailers(t *testing.T) {
+ r, err := http2.NewResponse(roadrunner.Payload{
+ Context: []byte(`{"headers":{"Trailer":["foo, bar", "baz"],"foo":["test"],"bar":["demo"]},"status": 200}`),
+ })
+
+ assert.NoError(t, err)
+ assert.NotNil(t, r)
+
+ w := &testWriter{h: http.Header(make(map[string][]string))}
+ assert.NoError(t, r.Write(w))
+
+ assert.Nil(t, w.h[http2.TrailerHeaderKey])
+ assert.Nil(t, w.h["foo"]) //nolint:golint,staticcheck
+ assert.Nil(t, w.h["baz"]) //nolint:golint,staticcheck
+
+ assert.Equal(t, "test", w.h.Get("Trailer:foo"))
+ assert.Equal(t, "demo", w.h.Get("Trailer:bar"))
+}
+
+func TestWrite_HandlesHandlesWhitespacesInTrailer(t *testing.T) {
+ r, err := http2.NewResponse(roadrunner.Payload{
+ Context: []byte(
+ `{"headers":{"Trailer":["foo\t,bar , baz"],"foo":["a"],"bar":["b"],"baz":["c"]},"status": 200}`),
+ })
+
+ assert.NoError(t, err)
+ assert.NotNil(t, r)
+
+ w := &testWriter{h: http.Header(make(map[string][]string))}
+ assert.NoError(t, r.Write(w))
+
+ assert.Equal(t, "a", w.h.Get("Trailer:foo"))
+ assert.Equal(t, "b", w.h.Get("Trailer:bar"))
+ assert.Equal(t, "c", w.h.Get("Trailer:baz"))
+}
diff --git a/plugins/http/tests/rpc_test.go b/plugins/http/tests/rpc_test.go
new file mode 100644
index 00000000..925aa082
--- /dev/null
+++ b/plugins/http/tests/rpc_test.go
@@ -0,0 +1,222 @@
+package tests
+
+//
+//import (
+// json "github.com/json-iterator/go"
+// "github.com/sirupsen/logrus"
+// "github.com/sirupsen/logrus/hooks/test"
+// "github.com/spiral/roadrunner/service"
+// "github.com/spiral/roadrunner/service/rpc"
+// "github.com/stretchr/testify/assert"
+// "os"
+// "strconv"
+// "testing"
+// "time"
+//)
+//
+//func Test_RPC(t *testing.T) {
+// logger, _ := test.NewNullLogger()
+// logger.SetLevel(logrus.DebugLevel)
+//
+// c := service.NewContainer(logger)
+// c.Register(rpc.ID, &rpc.Service{})
+// c.Register(ID, &Service{})
+//
+// assert.NoError(t, c.Init(&testCfg{
+// rpcCfg: `{"enable":true, "listen":"tcp://:5004"}`,
+// httpCfg: `{
+// "enable": true,
+// "address": ":16031",
+// "maxRequestSize": 1024,
+// "uploads": {
+// "dir": ` + tmpDir() + `,
+// "forbid": []
+// },
+// "workers":{
+// "command": "php ../../tests/http/client.php pid pipes",
+// "relay": "pipes",
+// "pool": {
+// "numWorkers": 1,
+// "allocateTimeout": 10000000,
+// "destroyTimeout": 10000000
+// }
+// }
+// }`}))
+//
+// s, _ := c.Get(ID)
+// ss := s.(*Service)
+//
+// s2, _ := c.Get(rpc.ID)
+// rs := s2.(*rpc.Service)
+//
+// go func() {
+// err := c.Serve()
+// if err != nil {
+// t.Errorf("error during the Serve: error %v", err)
+// }
+// }()
+//
+// time.Sleep(time.Second)
+//
+// res, _, err := get("http://localhost:16031")
+// if err != nil {
+// t.Fatal(err)
+// }
+// assert.Equal(t, strconv.Itoa(*ss.pool.Workers()[0].Pid), res)
+//
+// cl, err := rs.Client()
+// assert.NoError(t, err)
+//
+// r := ""
+// assert.NoError(t, cl.Call("http.Reset", true, &r))
+// assert.Equal(t, "OK", r)
+//
+// res2, _, err := get("http://localhost:16031")
+// if err != nil {
+// t.Fatal(err)
+// }
+// assert.Equal(t, strconv.Itoa(*ss.pool.Workers()[0].Pid), res2)
+// assert.NotEqual(t, res, res2)
+// c.Stop()
+//}
+//
+//func Test_RPC_Unix(t *testing.T) {
+// logger, _ := test.NewNullLogger()
+// logger.SetLevel(logrus.DebugLevel)
+//
+// c := service.NewContainer(logger)
+// c.Register(rpc.ID, &rpc.Service{})
+// c.Register(ID, &Service{})
+//
+// sock := `unix://` + os.TempDir() + `/rpc.unix`
+// j := json.ConfigCompatibleWithStandardLibrary
+// data, _ := j.Marshal(sock)
+//
+// assert.NoError(t, c.Init(&testCfg{
+// rpcCfg: `{"enable":true, "listen":` + string(data) + `}`,
+// httpCfg: `{
+// "enable": true,
+// "address": ":6032",
+// "maxRequestSize": 1024,
+// "uploads": {
+// "dir": ` + tmpDir() + `,
+// "forbid": []
+// },
+// "workers":{
+// "command": "php ../../tests/http/client.php pid pipes",
+// "relay": "pipes",
+// "pool": {
+// "numWorkers": 1,
+// "allocateTimeout": 10000000,
+// "destroyTimeout": 10000000
+// }
+// }
+// }`}))
+//
+// s, _ := c.Get(ID)
+// ss := s.(*Service)
+//
+// s2, _ := c.Get(rpc.ID)
+// rs := s2.(*rpc.Service)
+//
+// go func() {
+// err := c.Serve()
+// if err != nil {
+// t.Errorf("error during the Serve: error %v", err)
+// }
+// }()
+//
+// time.Sleep(time.Millisecond * 500)
+//
+// res, _, err := get("http://localhost:6032")
+// if err != nil {
+// c.Stop()
+// t.Fatal(err)
+// }
+// if ss.pool.Workers() != nil && len(ss.pool.Workers()) > 0 {
+// assert.Equal(t, strconv.Itoa(*ss.pool.Workers()[0].Pid), res)
+// } else {
+// c.Stop()
+// t.Fatal("no workers initialized")
+// }
+//
+// cl, err := rs.Client()
+// if err != nil {
+// c.Stop()
+// t.Fatal(err)
+// }
+//
+// r := ""
+// assert.NoError(t, cl.Call("http.Reset", true, &r))
+// assert.Equal(t, "OK", r)
+//
+// res2, _, err := get("http://localhost:6032")
+// if err != nil {
+// c.Stop()
+// t.Fatal(err)
+// }
+// assert.Equal(t, strconv.Itoa(*ss.pool.Workers()[0].Pid), res2)
+// assert.NotEqual(t, res, res2)
+// c.Stop()
+//}
+//
+//func Test_Workers(t *testing.T) {
+// logger, _ := test.NewNullLogger()
+// logger.SetLevel(logrus.DebugLevel)
+//
+// c := service.NewContainer(logger)
+// c.Register(rpc.ID, &rpc.Service{})
+// c.Register(ID, &Service{})
+//
+// assert.NoError(t, c.Init(&testCfg{
+// rpcCfg: `{"enable":true, "listen":"tcp://:5005"}`,
+// httpCfg: `{
+// "enable": true,
+// "address": ":6033",
+// "maxRequestSize": 1024,
+// "uploads": {
+// "dir": ` + tmpDir() + `,
+// "forbid": []
+// },
+// "workers":{
+// "command": "php ../../tests/http/client.php pid pipes",
+// "relay": "pipes",
+// "pool": {
+// "numWorkers": 1,
+// "allocateTimeout": 10000000,
+// "destroyTimeout": 10000000
+// }
+// }
+// }`}))
+//
+// s, _ := c.Get(ID)
+// ss := s.(*Service)
+//
+// s2, _ := c.Get(rpc.ID)
+// rs := s2.(*rpc.Service)
+//
+// go func() {
+// err := c.Serve()
+// if err != nil {
+// t.Errorf("error during the Serve: error %v", err)
+// }
+// }()
+// time.Sleep(time.Millisecond * 500)
+//
+// cl, err := rs.Client()
+// assert.NoError(t, err)
+//
+// r := &WorkerList{}
+// assert.NoError(t, cl.Call("http.Workers", true, &r))
+// assert.Len(t, r.Workers, 1)
+//
+// assert.Equal(t, *ss.pool.Workers()[0].Pid, r.Workers[0].Pid)
+// c.Stop()
+//}
+//
+//func Test_Errors(t *testing.T) {
+// r := &rpcServer{nil}
+//
+// assert.Error(t, r.Reset(true, nil))
+// assert.Error(t, r.Workers(true, nil))
+//}
diff --git a/plugins/http/tests/ssl_test.go b/plugins/http/tests/ssl_test.go
new file mode 100644
index 00000000..ee0e6e98
--- /dev/null
+++ b/plugins/http/tests/ssl_test.go
@@ -0,0 +1,178 @@
+package tests
+
+//func Test_SSL_Service_NoRedirect(t *testing.T) {
+// logger, _ := test.NewNullLogger()
+// logger.SetLevel(logrus.DebugLevel)
+//
+// c := service.NewContainer(logger)
+// c.Register(ID, &Service{})
+//
+// assert.NoError(t, c.Init(&testCfg{httpCfg: `{
+// "address": ":6030",
+// "ssl": {
+// "port": 6901,
+// "key": "fixtures/server.key",
+// "cert": "fixtures/server.crt"
+// },
+// "workers":{
+// "command": "php ../../tests/http/client.php echo pipes",
+// "pool": {"numWorkers": 1}
+// }
+// }`}))
+//
+// s, st := c.Get(ID)
+// assert.NotNil(t, s)
+// assert.Equal(t, service.StatusOK, st)
+//
+// // should do nothing
+// s.(*Service).Stop()
+//
+// go func() {
+// err := c.Serve()
+// if err != nil {
+// t.Errorf("error during the Serve: error %v", err)
+// }
+// }()
+//
+// time.Sleep(time.Millisecond * 500)
+//
+// req, err := http.NewRequest("GET", "http://localhost:6030?hello=world", nil)
+// assert.NoError(t, err)
+//
+// r, err := sslClient.Do(req)
+// assert.NoError(t, err)
+//
+// assert.Nil(t, r.TLS)
+//
+// b, err := ioutil.ReadAll(r.Body)
+// assert.NoError(t, err)
+//
+// assert.NoError(t, err)
+// assert.Equal(t, 201, r.StatusCode)
+// assert.Equal(t, "WORLD", string(b))
+//
+// err2 := r.Body.Close()
+// if err2 != nil {
+// t.Errorf("fail to close the Body: error %v", err2)
+// }
+// c.Stop()
+//}
+//
+//func Test_SSL_Service_Redirect(t *testing.T) {
+// logger, _ := test.NewNullLogger()
+// logger.SetLevel(logrus.DebugLevel)
+//
+// c := service.NewContainer(logger)
+// c.Register(ID, &Service{})
+//
+// assert.NoError(t, c.Init(&testCfg{httpCfg: `{
+// "address": ":6831",
+// "ssl": {
+// "port": 6902,
+// "redirect": true,
+// "key": "fixtures/server.key",
+// "cert": "fixtures/server.crt"
+// },
+// "workers":{
+// "command": "php ../../tests/http/client.php echo pipes",
+// "pool": {"numWorkers": 1}
+// }
+// }`}))
+//
+// s, st := c.Get(ID)
+// assert.NotNil(t, s)
+// assert.Equal(t, service.StatusOK, st)
+//
+// // should do nothing
+// s.(*Service).Stop()
+//
+// go func() {
+// err := c.Serve()
+// if err != nil {
+// t.Errorf("error during the Serve: error %v", err)
+// }
+// }()
+//
+// time.Sleep(time.Millisecond * 500)
+//
+// req, err := http.NewRequest("GET", "http://localhost:6831?hello=world", nil)
+// assert.NoError(t, err)
+//
+// r, err := sslClient.Do(req)
+// assert.NoError(t, err)
+// assert.NotNil(t, r.TLS)
+//
+// b, err := ioutil.ReadAll(r.Body)
+// assert.NoError(t, err)
+//
+// assert.NoError(t, err)
+// assert.Equal(t, 201, r.StatusCode)
+// assert.Equal(t, "WORLD", string(b))
+//
+// err2 := r.Body.Close()
+// if err2 != nil {
+// t.Errorf("fail to close the Body: error %v", err2)
+// }
+// c.Stop()
+//}
+//
+//func Test_SSL_Service_Push(t *testing.T) {
+// logger, _ := test.NewNullLogger()
+// logger.SetLevel(logrus.DebugLevel)
+//
+// c := service.NewContainer(logger)
+// c.Register(ID, &Service{})
+//
+// assert.NoError(t, c.Init(&testCfg{httpCfg: `{
+// "address": ":6032",
+// "ssl": {
+// "port": 6903,
+// "redirect": true,
+// "key": "fixtures/server.key",
+// "cert": "fixtures/server.crt"
+// },
+// "workers":{
+// "command": "php ../../tests/http/client.php push pipes",
+// "pool": {"numWorkers": 1}
+// }
+// }`}))
+//
+// s, st := c.Get(ID)
+// assert.NotNil(t, s)
+// assert.Equal(t, service.StatusOK, st)
+//
+// // should do nothing
+// s.(*Service).Stop()
+//
+// go func() {
+// err := c.Serve()
+// if err != nil {
+// t.Errorf("error during the Serve: error %v", err)
+// }
+// }()
+// time.Sleep(time.Millisecond * 500)
+//
+// req, err := http.NewRequest("GET", "https://localhost:6903?hello=world", nil)
+// assert.NoError(t, err)
+//
+// r, err := sslClient.Do(req)
+// assert.NoError(t, err)
+//
+// assert.NotNil(t, r.TLS)
+//
+// b, err := ioutil.ReadAll(r.Body)
+// assert.NoError(t, err)
+//
+// assert.Equal(t, "", r.Header.Get("Http2-Push"))
+//
+// assert.NoError(t, err)
+// assert.Equal(t, 201, r.StatusCode)
+// assert.Equal(t, "WORLD", string(b))
+//
+//
+// err2 := r.Body.Close()
+// if err2 != nil {
+// t.Errorf("fail to close the Body: error %v", err2)
+// }
+// c.Stop()
+//}
diff --git a/plugins/http/tests/uploads_config_test.go b/plugins/http/tests/uploads_config_test.go
new file mode 100644
index 00000000..497cd54f
--- /dev/null
+++ b/plugins/http/tests/uploads_config_test.go
@@ -0,0 +1,26 @@
+package tests
+
+import (
+ "os"
+ "testing"
+
+ "github.com/spiral/roadrunner/v2/plugins/http"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestFsConfig_Forbids(t *testing.T) {
+ cfg := http.UploadsConfig{Forbid: []string{".php"}}
+
+ assert.True(t, cfg.Forbids("index.php"))
+ assert.True(t, cfg.Forbids("index.PHP"))
+ assert.True(t, cfg.Forbids("phpadmin/index.bak.php"))
+ assert.False(t, cfg.Forbids("index.html"))
+}
+
+func TestFsConfig_TmpFallback(t *testing.T) {
+ cfg := http.UploadsConfig{Dir: "test"}
+ assert.Equal(t, "test", cfg.TmpDir())
+
+ cfg = http.UploadsConfig{Dir: ""}
+ assert.Equal(t, os.TempDir(), cfg.TmpDir())
+}