diff options
author | Valery Piashchynski <[email protected]> | 2020-11-19 21:34:22 +0300 |
---|---|---|
committer | Valery Piashchynski <[email protected]> | 2020-11-19 21:34:22 +0300 |
commit | bc0be9c17220220ae9b40b65e874701edaecd8ce (patch) | |
tree | 6ee032eee330f8bc8824e426bab1846c9479333c /plugins/http/tests | |
parent | 729c19af8c410a28b7b46c134fd7fe9608cd73b1 (diff) |
Add SSL tests
Diffstat (limited to 'plugins/http/tests')
-rw-r--r-- | plugins/http/tests/config_test.go | 330 | ||||
-rw-r--r-- | plugins/http/tests/configs/.rr-fcgi.yaml | 35 | ||||
-rw-r--r-- | plugins/http/tests/configs/.rr-h2c.yaml | 35 | ||||
-rw-r--r-- | plugins/http/tests/configs/.rr-handler-echo.yaml | 0 | ||||
-rw-r--r-- | plugins/http/tests/configs/.rr-ssl.yaml | 35 | ||||
-rw-r--r-- | plugins/http/tests/fcgi_test.go | 104 | ||||
-rw-r--r-- | plugins/http/tests/h2c_test.go | 81 | ||||
-rw-r--r-- | plugins/http/tests/http_test.go | 227 | ||||
-rw-r--r-- | plugins/http/tests/plugin_test.go | 759 | ||||
-rw-r--r-- | plugins/http/tests/response_test.go | 163 | ||||
-rw-r--r-- | plugins/http/tests/rpc_test.go | 222 | ||||
-rw-r--r-- | plugins/http/tests/ssl_test.go | 178 | ||||
-rw-r--r-- | plugins/http/tests/uploads_config_test.go | 26 |
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()) +} |