diff options
author | Anton Titov <[email protected]> | 2020-03-02 11:45:50 +0300 |
---|---|---|
committer | GitHub <[email protected]> | 2020-03-02 11:45:50 +0300 |
commit | b74aedd13ef6aa0bb78bc08bb521caa36e099e9d (patch) | |
tree | bb727c3105a082ece946142fde1696ce2e895067 | |
parent | 9960aff462c985f89e1e9387e0b87bab24504d0e (diff) | |
parent | 456c9934a898fbbaf8e92e1b6bd815b6a8ffeffb (diff) |
Merge pull request #260 from spiral/address_already_in_use_error
Address already in use error
-rw-r--r-- | .github/workflows/ci-build.yml | 175 | ||||
-rw-r--r-- | go.mod | 1 | ||||
-rw-r--r-- | go.sum | 2 | ||||
-rw-r--r-- | pipe_factory_test.go | 4 | ||||
-rw-r--r-- | service/headers/service_test.go | 28 | ||||
-rw-r--r-- | service/http/fcgi_test.go | 8 | ||||
-rw-r--r-- | service/http/h2c_test.go | 17 | ||||
-rw-r--r-- | service/http/rpc_test.go | 44 | ||||
-rw-r--r-- | service/http/service_test.go | 55 | ||||
-rw-r--r-- | service/http/ssl_test.go | 48 | ||||
-rw-r--r-- | service/limit/service_test.go | 91 | ||||
-rw-r--r-- | service/reload/config_test.go | 2 | ||||
-rw-r--r-- | service/reload/watcher_test.go | 3 | ||||
-rw-r--r-- | service/rpc/config_test.go | 2 | ||||
-rw-r--r-- | service/static/service_test.go | 21 | ||||
-rw-r--r-- | socket_factory_test.go | 20 | ||||
-rw-r--r-- | static_pool_test.go | 30 | ||||
-rw-r--r-- | util/network.go | 15 | ||||
-rw-r--r-- | util/network_test.go | 7 | ||||
-rw-r--r-- | util/network_windows.go | 33 | ||||
-rw-r--r-- | util/network_windows_test.go | 16 | ||||
-rw-r--r-- | worker_test.go | 15 |
22 files changed, 382 insertions, 255 deletions
diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index b0977521..7c28ca76 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -4,100 +4,101 @@ on: [push, pull_request] jobs: build: - name: Build (PHP ${{ matrix.php }}, Go ${{ matrix.go }}) - runs-on: ubuntu-latest + name: Build (PHP ${{ matrix.php }}, Go ${{ matrix.go }}, OS ${{ matrix.os }}) + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: php: [7.1, 7.2, 7.3, 7.4] - go: [1.12, 1.13] + go: [1.13, 1.14] + os: [ubuntu-latest, macOS-latest] env: GO111MODULE: on steps: - - name: Set up Go ${{ matrix.go }} - uses: actions/setup-go@v1 - with: - go-version: ${{ matrix.go }} - - - name: Set up PHP ${{ matrix.php }} - uses: shivammathur/setup-php@v1 - with: - php-version: ${{ matrix.php }} - extensions: dom - coverage: xdebug - - - name: Check out code - uses: actions/checkout@v2 - with: - fetch-depth: 1 - - - name: Show versions - run: php -v && composer -V && go version - - - name: Debug if needed - env: - DEBUG: ${{ secrets.DEBUG }} - run: if [[ "$DEBUG" == "true" ]]; then env && go env; fi - - - name: Syntax check only (lint) - run: find ./src/ -name "*.php" -print0 | xargs -0 -n1 -P8 php -l - - - name: Get Composer Cache Directory # Docs: <https://github.com/actions/cache/blob/master/examples.md#php---composer> - id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" - - - name: Cache dependencies # Docs: <https://github.com/actions/cache/blob/master/examples.md#php---composer> - uses: actions/cache@v1 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} - restore-keys: ${{ runner.os }}-composer- - - - name: Install Composer dependencies - run: composer install --prefer-dist --no-interaction --no-suggest # --prefer-source - - - name: Analyze PHP sources - run: composer analyze - - - name: Install Go dependencies - run: go mod download - - - name: Download binary roadrunner - run: php ./bin/rr get-binary - - - name: Run golang tests - run: | - go test -race -v -coverprofile=lib.txt -covermode=atomic - go test ./util -race -v -coverprofile=util.txt -covermode=atomic - go test ./service -race -v -coverprofile=service.txt -covermode=atomic - go test ./service/env -race -v -coverprofile=env.txt -covermode=atomic - go test ./service/rpc -race -v -coverprofile=rpc.txt -covermode=atomic - go test ./service/http -race -v -coverprofile=http.txt -covermode=atomic - go test ./service/static -race -v -coverprofile=static.txt -covermode=atomic - go test ./service/limit -race -v -coverprofile=limit.txt -covermode=atomic - go test ./service/headers -race -v -coverprofile=headers.txt -covermode=atomic - go test ./service/metrics -race -v -coverprofile=metrics.txt -covermode=atomic - go test ./service/health -race -v -coverprofile=health.txt -covermode=atomic - - - name: Run code coverage - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - run: | - if [[ "$CODECOV_TOKEN" != "" ]]; then - curl https://codecov.io/bash -o codecov-bash - chmod +x codecov-bash - ./codecov-bash -f lib.txt - ./codecov-bash -f util.txt - ./codecov-bash -f service.txt - ./codecov-bash -f env.txt - ./codecov-bash -f rpc.txt - ./codecov-bash -f http.txt - ./codecov-bash -f static.txt - ./codecov-bash -f limit.txt - ./codecov-bash -f headers.txt - ./codecov-bash -f metrics.txt - ./codecov-bash -f health.txt - fi + - name: Set up Go ${{ matrix.go }} + uses: actions/setup-go@v1 + with: + go-version: ${{ matrix.go }} + + - name: Set up PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v1 + with: + php-version: ${{ matrix.php }} + extensions: dom + coverage: xdebug + + - name: Check out code + uses: actions/checkout@v2 + with: + fetch-depth: 1 + + - name: Show versions + run: php -v ; composer -V ; go version + + - name: Debug if needed + env: + DEBUG: ${{ secrets.DEBUG }} + run: if [[ "$DEBUG" == "true" ]]; then env && go env; fi + + - name: Syntax check only (lint) + run: find ./src/ -name "*.php" -print0 | xargs -0 -n1 -P8 php -l + + - name: Get Composer Cache Directory # Docs: <https://github.com/actions/cache/blob/master/examples.md#php---composer> + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies # Docs: <https://github.com/actions/cache/blob/master/examples.md#php---composer> + uses: actions/cache@v1 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install Composer dependencies + run: composer install --prefer-dist --no-interaction --no-suggest # --prefer-source + + - name: Analyze PHP sources + run: composer analyze + + - name: Install Go dependencies + run: go mod download + + - name: Download binary roadrunner + run: php ./bin/rr get-binary + + - name: Run golang tests + run: | + go test -race -v -coverprofile=lib.txt -covermode=atomic + go test ./util -race -v -coverprofile=util.txt -covermode=atomic + go test ./service -race -v -coverprofile=service.txt -covermode=atomic + go test ./service/env -race -v -coverprofile=env.txt -covermode=atomic + go test ./service/rpc -race -v -coverprofile=rpc.txt -covermode=atomic + go test ./service/http -race -v -coverprofile=http.txt -covermode=atomic + go test ./service/static -race -v -coverprofile=static.txt -covermode=atomic + go test ./service/limit -race -v -coverprofile=limit.txt -covermode=atomic + go test ./service/headers -race -v -coverprofile=headers.txt -covermode=atomic + go test ./service/metrics -race -v -coverprofile=metrics.txt -covermode=atomic + go test ./service/health -race -v -coverprofile=health.txt -covermode=atomic + + - name: Run code coverage + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + run: | + if [[ "$CODECOV_TOKEN" != "" ]]; then + curl https://codecov.io/bash -o codecov-bash + chmod +x codecov-bash + ./codecov-bash -f lib.txt + ./codecov-bash -f util.txt + ./codecov-bash -f service.txt + ./codecov-bash -f env.txt + ./codecov-bash -f rpc.txt + ./codecov-bash -f http.txt + ./codecov-bash -f static.txt + ./codecov-bash -f limit.txt + ./codecov-bash -f headers.txt + ./codecov-bash -f metrics.txt + ./codecov-bash -f health.txt + fi golangci-check: name: runner / golangci-lint @@ -19,6 +19,7 @@ require ( github.com/spf13/viper v1.6.2 github.com/spiral/goridge v2.1.4+incompatible github.com/stretchr/testify v1.5.1 + github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a github.com/yookoala/gofast v0.4.0 golang.org/x/net v0.0.0-20200222125558-5a598a2470a0 ) @@ -194,6 +194,8 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yookoala/gofast v0.4.0 h1:dLBjghcsbbZNOEHN8N1X/gh9S6srmJed4WQfG7DlKwo= diff --git a/pipe_factory_test.go b/pipe_factory_test.go index 27d1f74d..14cf1272 100644 --- a/pipe_factory_test.go +++ b/pipe_factory_test.go @@ -4,6 +4,7 @@ import ( "github.com/stretchr/testify/assert" "os/exec" "testing" + "time" ) func Test_Pipe_Start(t *testing.T) { @@ -108,8 +109,9 @@ func Test_Pipe_Broken(t *testing.T) { assert.Contains(t, err.Error(), "undefined_function()") }() defer func() { + time.Sleep(time.Second) err := w.Stop() - assert.Error(t, err) + assert.NoError(t, err) }() res, err := w.Exec(&Payload{Body: []byte("hello")}) diff --git a/service/headers/service_test.go b/service/headers/service_test.go index 2f29db5e..120bb3d6 100644 --- a/service/headers/service_test.go +++ b/service/headers/service_test.go @@ -46,7 +46,7 @@ func Test_RequestHeaders(t *testing.T) { headers: `{"request":{"input": "custom-header"}}`, httpCfg: `{ "enable": true, - "address": ":6029", + "address": ":6078", "maxRequestSize": 1024, "workers":{ "command": "php ../../tests/http/client.php header pipes", @@ -68,7 +68,7 @@ func Test_RequestHeaders(t *testing.T) { time.Sleep(time.Millisecond * 100) defer c.Stop() - req, err := http.NewRequest("GET", "http://localhost:6029?hello=value", nil) + req, err := http.NewRequest("GET", "http://localhost:6078?hello=value", nil) assert.NoError(t, err) r, err := http.DefaultClient.Do(req) @@ -100,7 +100,7 @@ func Test_ResponseHeaders(t *testing.T) { headers: `{"response":{"output": "output-header"},"request":{"input": "custom-header"}}`, httpCfg: `{ "enable": true, - "address": ":6029", + "address": ":6079", "maxRequestSize": 1024, "workers":{ "command": "php ../../tests/http/client.php header pipes", @@ -122,7 +122,7 @@ func Test_ResponseHeaders(t *testing.T) { time.Sleep(time.Millisecond * 100) defer c.Stop() - req, err := http.NewRequest("GET", "http://localhost:6029?hello=value", nil) + req, err := http.NewRequest("GET", "http://localhost:6079?hello=value", nil) assert.NoError(t, err) r, err := http.DefaultClient.Do(req) @@ -164,7 +164,7 @@ func TestCORS_OPTIONS(t *testing.T) { }`, httpCfg: `{ "enable": true, - "address": ":6029", + "address": ":6379", "maxRequestSize": 1024, "workers":{ "command": "php ../../tests/http/client.php headers pipes", @@ -186,7 +186,7 @@ func TestCORS_OPTIONS(t *testing.T) { time.Sleep(time.Millisecond * 100) defer c.Stop() - req, err := http.NewRequest("OPTIONS", "http://localhost:6029", nil) + req, err := http.NewRequest("OPTIONS", "http://localhost:6379", nil) assert.NoError(t, err) r, err := http.DefaultClient.Do(req) @@ -232,7 +232,7 @@ func TestCORS_Pass(t *testing.T) { }`, httpCfg: `{ "enable": true, - "address": ":6029", + "address": ":6672", "maxRequestSize": 1024, "workers":{ "command": "php ../../tests/http/client.php headers pipes", @@ -254,18 +254,11 @@ func TestCORS_Pass(t *testing.T) { time.Sleep(time.Millisecond * 100) defer c.Stop() - req, err := http.NewRequest("GET", "http://localhost:6029", nil) + req, err := http.NewRequest("GET", "http://localhost:6672", nil) assert.NoError(t, err) r, err := http.DefaultClient.Do(req) assert.NoError(t, err) - defer func() { - err := r.Body.Close() - if err != nil { - t.Errorf("error during the body closing: error %v", err) - } - }() - assert.Equal(t, "true", r.Header.Get("Access-Control-Allow-Credentials")) assert.Equal(t, "*", r.Header.Get("Access-Control-Allow-Headers")) assert.Equal(t, "*", r.Header.Get("Access-Control-Allow-Origin")) @@ -275,4 +268,9 @@ func TestCORS_Pass(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 200, r.StatusCode) + + err = r.Body.Close() + if err != nil { + t.Errorf("error during the body closing: error %v", err) + } } diff --git a/service/http/fcgi_test.go b/service/http/fcgi_test.go index 0cfc6e41..e68b2e7f 100644 --- a/service/http/fcgi_test.go +++ b/service/http/fcgi_test.go @@ -37,8 +37,7 @@ func Test_FCGI_Service_Echo(t *testing.T) { s.(*Service).Stop() go func() { assert.NoError(t, c.Serve()) }() - time.Sleep(time.Millisecond * 100) - defer c.Stop() + time.Sleep(time.Second * 1) fcgiConnFactory := gofast.SimpleConnFactory("tcp", "0.0.0.0:6082") @@ -56,6 +55,7 @@ func Test_FCGI_Service_Echo(t *testing.T) { 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) { @@ -83,8 +83,7 @@ func Test_FCGI_Service_Request_Uri(t *testing.T) { s.(*Service).Stop() go func() { assert.NoError(t, c.Serve()) }() - time.Sleep(time.Millisecond * 100) - defer c.Stop() + time.Sleep(time.Second * 1) fcgiConnFactory := gofast.SimpleConnFactory("tcp", "0.0.0.0:6083") @@ -102,4 +101,5 @@ func Test_FCGI_Service_Request_Uri(t *testing.T) { 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/service/http/h2c_test.go b/service/http/h2c_test.go index 7bbc30ac..a2465a0a 100644 --- a/service/http/h2c_test.go +++ b/service/http/h2c_test.go @@ -52,16 +52,15 @@ func Test_Service_H2C(t *testing.T) { req.Header.Add("Connection", "HTTP2-Settings") req.Header.Add("HTTP2-Settings", "") - r, err := http.DefaultClient.Do(req) - assert.NoError(t, err) - defer func() { - err := r.Body.Close() - if err != nil { - t.Errorf("fail to close the Body: error %v", err) - } - }() + r, err2 := http.DefaultClient.Do(req) + if err2 != nil { + t.Fatal(err2) + } assert.Equal(t, "101 Switching Protocols", r.Status) - // will fail with h2c notice + err3 := r.Body.Close() + if err3 != nil { + t.Errorf("fail to close the Body: error %v", err3) + } } diff --git a/service/http/rpc_test.go b/service/http/rpc_test.go index 0e4b2c0a..1971f237 100644 --- a/service/http/rpc_test.go +++ b/service/http/rpc_test.go @@ -55,10 +55,13 @@ func Test_RPC(t *testing.T) { t.Errorf("error during the Serve: error %v", err) } }() - time.Sleep(time.Millisecond * 100) - defer c.Stop() - res, _, _ := get("http://localhost:6029") + time.Sleep(time.Second) + + res, _, err := get("http://localhost:6029") + if err != nil { + t.Fatal(err) + } assert.Equal(t, strconv.Itoa(*ss.rr.Workers()[0].Pid), res) cl, err := rs.Client() @@ -68,9 +71,13 @@ func Test_RPC(t *testing.T) { assert.NoError(t, cl.Call("http.Reset", true, &r)) assert.Equal(t, "OK", r) - res2, _, _ := get("http://localhost:6029") + res2, _, err := get("http://localhost:6029") + if err != nil { + t.Fatal() + } assert.Equal(t, strconv.Itoa(*ss.rr.Workers()[0].Pid), res2) assert.NotEqual(t, res, res2) + c.Stop() } func Test_RPC_Unix(t *testing.T) { @@ -121,22 +128,39 @@ func Test_RPC_Unix(t *testing.T) { t.Errorf("error during the Serve: error %v", err) } }() - time.Sleep(time.Millisecond * 100) - defer c.Stop() - res, _, _ := get("http://localhost:6029") - assert.Equal(t, strconv.Itoa(*ss.rr.Workers()[0].Pid), res) + time.Sleep(time.Second) + + res, _, err := get("http://localhost:6029") + if err != nil { + c.Stop() + t.Fatal(err) + } + if ss.rr.Workers() != nil && len(ss.rr.Workers()) > 0 { + assert.Equal(t, strconv.Itoa(*ss.rr.Workers()[0].Pid), res) + } else { + c.Stop() + t.Fatal("no workers initialized") + } cl, err := rs.Client() - assert.NoError(t, err) + if err != nil { + c.Stop() + t.Fatal(err) + } r := "" assert.NoError(t, cl.Call("http.Reset", true, &r)) assert.Equal(t, "OK", r) - res2, _, _ := get("http://localhost:6029") + res2, _, err := get("http://localhost:6029") + if err != nil { + c.Stop() + t.Fatal(err) + } assert.Equal(t, strconv.Itoa(*ss.rr.Workers()[0].Pid), res2) assert.NotEqual(t, res, res2) + c.Stop() } func Test_Workers(t *testing.T) { diff --git a/service/http/service_test.go b/service/http/service_test.go index c4b2c2c4..1a1c32ae 100644 --- a/service/http/service_test.go +++ b/service/http/service_test.go @@ -115,7 +115,7 @@ func Test_Service_Echo(t *testing.T) { assert.NoError(t, c.Init(&testCfg{httpCfg: `{ "enable": true, - "address": ":6029", + "address": ":6536", "maxRequestSize": 1024, "uploads": { "dir": ` + tmpDir() + `, @@ -146,26 +146,24 @@ func Test_Service_Echo(t *testing.T) { } }() time.Sleep(time.Millisecond * 100) - defer c.Stop() - req, err := http.NewRequest("GET", "http://localhost:6029?hello=world", nil) + req, err := http.NewRequest("GET", "http://localhost:6536?hello=world", nil) assert.NoError(t, err) r, err := http.DefaultClient.Do(req) assert.NoError(t, err) - defer func() { - err := r.Body.Close() - if err != nil { - t.Errorf("error closing the Body: error %v", 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("error closing the Body: error %v", err2) + } + c.Stop() } func Test_Service_Env(t *testing.T) { @@ -178,7 +176,7 @@ func Test_Service_Env(t *testing.T) { assert.NoError(t, c.Init(&testCfg{httpCfg: `{ "enable": true, - "address": ":6029", + "address": ":6031", "maxRequestSize": 1024, "uploads": { "dir": ` + tmpDir() + `, @@ -208,10 +206,10 @@ func Test_Service_Env(t *testing.T) { t.Errorf("serve error: %v", err) } }() - time.Sleep(time.Millisecond * 100) - defer c.Stop() - req, err := http.NewRequest("GET", "http://localhost:6029", nil) + time.Sleep(time.Second * 1) + + req, err := http.NewRequest("GET", "http://localhost:6031", nil) assert.NoError(t, err) r, err := http.DefaultClient.Do(req) @@ -229,6 +227,7 @@ func Test_Service_Env(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 200, r.StatusCode) assert.Equal(t, "ENV_VALUE", string(b)) + c.Stop() } func Test_Service_ErrorEcho(t *testing.T) { @@ -240,7 +239,7 @@ func Test_Service_ErrorEcho(t *testing.T) { assert.NoError(t, c.Init(&testCfg{httpCfg: `{ "enable": true, - "address": ":6029", + "address": ":6030", "maxRequestSize": 1024, "uploads": { "dir": ` + tmpDir() + `, @@ -276,10 +275,10 @@ func Test_Service_ErrorEcho(t *testing.T) { t.Errorf("serve error: %v", err) } }() - time.Sleep(time.Millisecond * 100) - defer c.Stop() - req, err := http.NewRequest("GET", "http://localhost:6029?hello=world", nil) + time.Sleep(time.Second) + + req, err := http.NewRequest("GET", "http://localhost:6030?hello=world", nil) assert.NoError(t, err) r, err := http.DefaultClient.Do(req) @@ -299,6 +298,7 @@ func Test_Service_ErrorEcho(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 201, r.StatusCode) assert.Equal(t, "WORLD", string(b)) + c.Stop() } func Test_Service_Middleware(t *testing.T) { @@ -310,7 +310,7 @@ func Test_Service_Middleware(t *testing.T) { assert.NoError(t, c.Init(&testCfg{httpCfg: `{ "enable": true, - "address": ":6029", + "address": ":6032", "maxRequestSize": 1024, "uploads": { "dir": ` + tmpDir() + `, @@ -351,10 +351,9 @@ func Test_Service_Middleware(t *testing.T) { t.Errorf("serve error: %v", err) } }() - time.Sleep(time.Millisecond * 100) - defer c.Stop() + time.Sleep(time.Second) - req, err := http.NewRequest("GET", "http://localhost:6029?hello=world", nil) + req, err := http.NewRequest("GET", "http://localhost:6032?hello=world", nil) assert.NoError(t, err) r, err := http.DefaultClient.Do(req) @@ -372,7 +371,7 @@ func Test_Service_Middleware(t *testing.T) { t.Errorf("error closing the Body: error %v", err) } - req, err = http.NewRequest("GET", "http://localhost:6029/halt", nil) + req, err = http.NewRequest("GET", "http://localhost:6032/halt", nil) assert.NoError(t, err) r, err = http.DefaultClient.Do(req) @@ -386,8 +385,10 @@ func Test_Service_Middleware(t *testing.T) { err = r.Body.Close() if err != nil { + c.Stop() t.Errorf("error closing the Body: error %v", err) } + c.Stop() } func Test_Service_Listener(t *testing.T) { @@ -399,7 +400,7 @@ func Test_Service_Listener(t *testing.T) { assert.NoError(t, c.Init(&testCfg{httpCfg: `{ "enable": true, - "address": ":6029", + "address": ":6033", "maxRequestSize": 1024, "uploads": { "dir": ` + tmpDir() + `, @@ -448,7 +449,7 @@ func Test_Service_Error(t *testing.T) { assert.NoError(t, c.Init(&testCfg{httpCfg: `{ "enable": true, - "address": ":6029", + "address": ":6034", "maxRequestSize": 1024, "uploads": { "dir": ` + tmpDir() + `, @@ -477,7 +478,7 @@ func Test_Service_Error2(t *testing.T) { assert.NoError(t, c.Init(&testCfg{httpCfg: `{ "enable": true, - "address": ":6029", + "address": ":6035", "maxRequestSize": 1024, "uploads": { "dir": ` + tmpDir() + `, @@ -506,7 +507,7 @@ func Test_Service_Error3(t *testing.T) { assert.Error(t, c.Init(&testCfg{httpCfg: `{ "enable": true, - "address": ":6029", + "address": ":6036", "maxRequestSize": 1024, "uploads": { "dir": ` + tmpDir() + `, diff --git a/service/http/ssl_test.go b/service/http/ssl_test.go index 49bba6cb..b82aa75c 100644 --- a/service/http/ssl_test.go +++ b/service/http/ssl_test.go @@ -84,9 +84,9 @@ func Test_SSL_Service_NoRedirect(t *testing.T) { c.Register(ID, &Service{}) assert.NoError(t, c.Init(&testCfg{httpCfg: `{ - "address": ":6029", + "address": ":6030", "ssl": { - "port": 6900, + "port": 6901, "key": "fixtures/server.key", "cert": "fixtures/server.crt" }, @@ -109,19 +109,16 @@ func Test_SSL_Service_NoRedirect(t *testing.T) { t.Errorf("error during the Serve: error %v", err) } }() - time.Sleep(time.Millisecond * 100) - defer c.Stop() - req, err := http.NewRequest("GET", "http://localhost:6029?hello=world", nil) + time.Sleep(time.Second) + + req, err := http.NewRequest("GET", "http://localhost:6030?hello=world", nil) assert.NoError(t, err) r, err := sslClient.Do(req) assert.NoError(t, err) defer func() { - err := r.Body.Close() - if err != nil { - t.Errorf("fail to close the Body: error %v", err) - } + }() assert.Nil(t, r.TLS) @@ -132,6 +129,12 @@ func Test_SSL_Service_NoRedirect(t *testing.T) { 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) { @@ -142,9 +145,9 @@ func Test_SSL_Service_Redirect(t *testing.T) { c.Register(ID, &Service{}) assert.NoError(t, c.Init(&testCfg{httpCfg: `{ - "address": ":6029", + "address": ":6031", "ssl": { - "port": 6900, + "port": 6902, "redirect": true, "key": "fixtures/server.key", "cert": "fixtures/server.crt" @@ -168,19 +171,16 @@ func Test_SSL_Service_Redirect(t *testing.T) { t.Errorf("error during the Serve: error %v", err) } }() - time.Sleep(time.Millisecond * 100) - defer c.Stop() - req, err := http.NewRequest("GET", "http://localhost:6029?hello=world", nil) + time.Sleep(time.Second) + + req, err := http.NewRequest("GET", "http://localhost:6031?hello=world", nil) assert.NoError(t, err) r, err := sslClient.Do(req) assert.NoError(t, err) defer func() { - err := r.Body.Close() - if err != nil { - t.Errorf("fail to close the Body: error %v", err) - } + }() assert.NotNil(t, r.TLS) @@ -191,6 +191,12 @@ func Test_SSL_Service_Redirect(t *testing.T) { 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) { @@ -201,9 +207,9 @@ func Test_SSL_Service_Push(t *testing.T) { c.Register(ID, &Service{}) assert.NoError(t, c.Init(&testCfg{httpCfg: `{ - "address": ":6029", + "address": ":6032", "ssl": { - "port": 6900, + "port": 6903, "redirect": true, "key": "fixtures/server.key", "cert": "fixtures/server.crt" @@ -230,7 +236,7 @@ func Test_SSL_Service_Push(t *testing.T) { time.Sleep(time.Millisecond * 100) defer c.Stop() - req, err := http.NewRequest("GET", "https://localhost:6900?hello=world", nil) + req, err := http.NewRequest("GET", "https://localhost:6903?hello=world", nil) assert.NoError(t, err) r, err := sslClient.Do(req) diff --git a/service/limit/service_test.go b/service/limit/service_test.go index 8cb3d7dc..abc03e69 100644 --- a/service/limit/service_test.go +++ b/service/limit/service_test.go @@ -57,7 +57,7 @@ func Test_Service_PidEcho(t *testing.T) { assert.NoError(t, c.Init(&testCfg{ httpCfg: `{ - "address": ":6029", + "address": ":7029", "workers":{ "command": "php ../../tests/http/client.php pid pipes", "pool": {"numWorkers": 1} @@ -81,26 +81,26 @@ func Test_Service_PidEcho(t *testing.T) { t.Errorf("error during the Serve: error %v", err) } }() - time.Sleep(time.Millisecond * 100) - defer c.Stop() - req, err := http.NewRequest("GET", "http://localhost:6029", nil) + time.Sleep(time.Millisecond * 100) + req, err := http.NewRequest("GET", "http://localhost:7029", nil) assert.NoError(t, err) r, err := http.DefaultClient.Do(req) assert.NoError(t, err) - defer func() { - err := r.Body.Close() - if err != nil { - t.Errorf("error during the body closing: error %v", err) - } - }() + b, err := ioutil.ReadAll(r.Body) assert.NoError(t, err) assert.NoError(t, err) assert.Equal(t, getPID(s), string(b)) + + err2 := r.Body.Close() + if err2 != nil { + t.Errorf("error during the body closing: error %v", err2) + } + c.Stop() } func Test_Service_ListenerPlusTTL(t *testing.T) { @@ -113,7 +113,7 @@ func Test_Service_ListenerPlusTTL(t *testing.T) { assert.NoError(t, c.Init(&testCfg{ httpCfg: `{ - "address": ":6029", + "address": ":7030", "workers":{ "command": "php ../../tests/http/client.php pid pipes", "pool": {"numWorkers": 1} @@ -145,22 +145,18 @@ func Test_Service_ListenerPlusTTL(t *testing.T) { t.Errorf("error during the Serve: error %v", err) } }() + + time.Sleep(time.Millisecond * 100) - defer c.Stop() lastPID := getPID(s) - req, err := http.NewRequest("GET", "http://localhost:6029", nil) + req, err := http.NewRequest("GET", "http://localhost:7030", nil) assert.NoError(t, err) r, err := http.DefaultClient.Do(req) assert.NoError(t, err) - defer func() { - err := r.Body.Close() - if err != nil { - t.Errorf("error during the body closing: error %v", err) - } - }() + b, err := ioutil.ReadAll(r.Body) assert.NoError(t, err) @@ -169,13 +165,20 @@ func Test_Service_ListenerPlusTTL(t *testing.T) { <-captured // clean state - req, err = http.NewRequest("GET", "http://localhost:6029?new", nil) + req, err = http.NewRequest("GET", "http://localhost:7030?new", nil) assert.NoError(t, err) _, err = http.DefaultClient.Do(req) assert.NoError(t, err) assert.NotEqual(t, lastPID, getPID(s)) + + c.Stop() + + err2 := r.Body.Close() + if err2 != nil { + t.Errorf("error during the body closing: error %v", err2) + } } func Test_Service_ListenerPlusIdleTTL(t *testing.T) { @@ -188,7 +191,7 @@ func Test_Service_ListenerPlusIdleTTL(t *testing.T) { assert.NoError(t, c.Init(&testCfg{ httpCfg: `{ - "address": ":6029", + "address": ":7031", "workers":{ "command": "php ../../tests/http/client.php pid pipes", "pool": {"numWorkers": 1} @@ -220,22 +223,17 @@ func Test_Service_ListenerPlusIdleTTL(t *testing.T) { t.Errorf("error during the Serve: error %v", err) } }() + + time.Sleep(time.Millisecond * 100) - defer c.Stop() lastPID := getPID(s) - req, err := http.NewRequest("GET", "http://localhost:6029", nil) + req, err := http.NewRequest("GET", "http://localhost:7031", nil) assert.NoError(t, err) r, err := http.DefaultClient.Do(req) assert.NoError(t, err) - defer func() { - err := r.Body.Close() - if err != nil { - t.Errorf("error during the body closing: error %v", err) - } - }() b, err := ioutil.ReadAll(r.Body) assert.NoError(t, err) @@ -246,13 +244,19 @@ func Test_Service_ListenerPlusIdleTTL(t *testing.T) { <-captured // clean state - req, err = http.NewRequest("GET", "http://localhost:6029?new", nil) + req, err = http.NewRequest("GET", "http://localhost:7031?new", nil) assert.NoError(t, err) _, err = http.DefaultClient.Do(req) assert.NoError(t, err) assert.NotEqual(t, lastPID, getPID(s)) + + c.Stop() + err2 := r.Body.Close() + if err2 != nil { + t.Errorf("error during the body closing: error %v", err2) + } } func Test_Service_Listener_MaxExecTTL(t *testing.T) { @@ -265,7 +269,7 @@ func Test_Service_Listener_MaxExecTTL(t *testing.T) { assert.NoError(t, c.Init(&testCfg{ httpCfg: `{ - "address": ":6029", + "address": ":7032", "workers":{ "command": "php ../../tests/http/client.php stuck pipes", "pool": {"numWorkers": 1} @@ -297,10 +301,10 @@ func Test_Service_Listener_MaxExecTTL(t *testing.T) { t.Errorf("error during the Serve: error %v", err) } }() + time.Sleep(time.Millisecond * 100) - defer c.Stop() - req, err := http.NewRequest("GET", "http://localhost:6029", nil) + req, err := http.NewRequest("GET", "http://localhost:7032", nil) assert.NoError(t, err) r, err := http.DefaultClient.Do(req) @@ -308,6 +312,8 @@ func Test_Service_Listener_MaxExecTTL(t *testing.T) { assert.Equal(t, 500, r.StatusCode) <-captured + + c.Stop() } func Test_Service_Listener_MaxMemoryUsage(t *testing.T) { @@ -320,7 +326,7 @@ func Test_Service_Listener_MaxMemoryUsage(t *testing.T) { assert.NoError(t, c.Init(&testCfg{ httpCfg: `{ - "address": ":6029", + "address": ":7033", "workers":{ "command": "php ../../tests/http/client.php memleak pipes", "pool": {"numWorkers": 1} @@ -354,12 +360,12 @@ func Test_Service_Listener_MaxMemoryUsage(t *testing.T) { t.Errorf("error during the Serve: error %v", err) } }() + time.Sleep(time.Millisecond * 100) - defer c.Stop() lastPID := getPID(s) - req, err := http.NewRequest("GET", "http://localhost:6029", nil) + req, err := http.NewRequest("GET", "http://localhost:7033", nil) assert.NoError(t, err) for { @@ -367,19 +373,28 @@ func Test_Service_Listener_MaxMemoryUsage(t *testing.T) { case <-captured: _, err := http.DefaultClient.Do(req) if err != nil { + c.Stop() t.Errorf("error during sending the http request: error %v", err) } assert.NotEqual(t, lastPID, getPID(s)) + c.Stop() return default: _, err := http.DefaultClient.Do(req) if err != nil { + c.Stop() t.Errorf("error during sending the http request: error %v", err) } + c.Stop() + return } } } func getPID(s interface{}) string { - w := s.(*rrhttp.Service).Server().Workers()[0] - return fmt.Sprintf("%v", *w.Pid) + if len(s.(*rrhttp.Service).Server().Workers()) > 0 { + w := s.(*rrhttp.Service).Server().Workers()[0] + return fmt.Sprintf("%v", *w.Pid) + } else { + panic("no workers") + } } diff --git a/service/reload/config_test.go b/service/reload/config_test.go index b7e6e669..600975d3 100644 --- a/service/reload/config_test.go +++ b/service/reload/config_test.go @@ -27,7 +27,7 @@ func Test_Config_Valid(t *testing.T) { func Test_Fake_ServiceConfig(t *testing.T) { services := make(map[string]ServiceConfig) cfg := &Config{ - Interval: time.Second, + Interval: time.Microsecond, Patterns: nil, Services: services, } diff --git a/service/reload/watcher_test.go b/service/reload/watcher_test.go index f5a5db01..391f6bb9 100644 --- a/service/reload/watcher_test.go +++ b/service/reload/watcher_test.go @@ -129,7 +129,6 @@ func Test_Get_FileEvent(t *testing.T) { panic("didn't handle event when write file2") } w.Stop() - return } }() }() @@ -224,7 +223,7 @@ func Test_FileExtensionFilter(t *testing.T) { } }() w.Stop() - return + runtime.Goexit() }() err = w.StartPolling(time.Second) diff --git a/service/rpc/config_test.go b/service/rpc/config_test.go index c65e7415..623347ed 100644 --- a/service/rpc/config_test.go +++ b/service/rpc/config_test.go @@ -48,7 +48,7 @@ func TestConfig_Listener(t *testing.T) { }() assert.Equal(t, "tcp", ln.Addr().Network()) - assert.Equal(t, "[::]:18001", ln.Addr().String()) + assert.Equal(t, "0.0.0.0:18001", ln.Addr().String()) } func TestConfig_ListenerUnix(t *testing.T) { diff --git a/service/static/service_test.go b/service/static/service_test.go index 4205650d..bcda26ce 100644 --- a/service/static/service_test.go +++ b/service/static/service_test.go @@ -90,11 +90,13 @@ func Test_Files(t *testing.T) { t.Errorf("serve error: %v", err) } }() - time.Sleep(time.Millisecond * 100) - defer c.Stop() + + time.Sleep(time.Second) + b, _, _ := get("http://localhost:6029/sample.txt") assert.Equal(t, "sample", b) + c.Stop() } func Test_Disabled(t *testing.T) { @@ -334,11 +336,12 @@ func Test_Files_NotFound(t *testing.T) { t.Errorf("serve error: %v", err) } }() - time.Sleep(time.Millisecond * 100) - defer c.Stop() + + time.Sleep(time.Second) b, _, _ := get("http://localhost:6029/client.XXX?hello=world") assert.Equal(t, "WORLD", b) + c.Stop() } func Test_Files_Dir(t *testing.T) { @@ -376,11 +379,11 @@ func Test_Files_Dir(t *testing.T) { t.Errorf("serve error: %v", err) } }() - time.Sleep(time.Millisecond * 100) - defer c.Stop() + time.Sleep(time.Second) b, _, _ := get("http://localhost:6029/http?hello=world") assert.Equal(t, "WORLD", b) + c.Stop() } func Test_Files_NotForbid(t *testing.T) { @@ -418,11 +421,13 @@ func Test_Files_NotForbid(t *testing.T) { t.Errorf("serve error: %v", err) } }() - time.Sleep(time.Millisecond * 100) - defer c.Stop() + + time.Sleep(time.Second) b, _, _ := get("http://localhost:6029/client.php") assert.Equal(t, all("../../tests/client.php"), b) + assert.Equal(t, all("../../tests/client.php"), b) + c.Stop() } func tmpDir() string { diff --git a/socket_factory_test.go b/socket_factory_test.go index 8beb3fc6..c15e720a 100644 --- a/socket_factory_test.go +++ b/socket_factory_test.go @@ -105,9 +105,9 @@ func Test_Tcp_Failboot(t *testing.T) { ls, err := net.Listen("tcp", "localhost:9007") if assert.NoError(t, err) { defer func() { - err := ls.Close() - if err != nil { - t.Errorf("error closing the listener: error %v", err) + err3 := ls.Close() + if err3 != nil { + t.Errorf("error closing the listener: error %v", err3) } }() } else { @@ -116,10 +116,10 @@ func Test_Tcp_Failboot(t *testing.T) { cmd := exec.Command("php", "tests/failboot.php") - w, err := NewSocketFactory(ls, time.Minute).SpawnWorker(cmd) + w, err2 := NewSocketFactory(ls, time.Minute).SpawnWorker(cmd) assert.Nil(t, w) - assert.Error(t, err) - assert.Contains(t, err.Error(), "failboot") + assert.Error(t, err2) + assert.Contains(t, err2.Error(), "failboot") } func Test_Tcp_Timeout(t *testing.T) { @@ -193,8 +193,9 @@ func Test_Tcp_Broken(t *testing.T) { }() defer func() { - err = w.Stop() - assert.Error(t, err) + time.Sleep(time.Second) + err2 := w.Stop() + assert.NoError(t, err2) }() res, err := w.Exec(&Payload{Body: []byte("hello")}) @@ -375,8 +376,9 @@ func Test_Unix_Broken(t *testing.T) { }() defer func() { + time.Sleep(time.Second) err = w.Stop() - assert.Error(t, err) + assert.NoError(t, err) }() res, err := w.Exec(&Payload{Body: []byte("hello")}) diff --git a/static_pool_test.go b/static_pool_test.go index 1f185f58..46f0bed8 100644 --- a/static_pool_test.go +++ b/static_pool_test.go @@ -151,11 +151,10 @@ func Test_StaticPool_Broken_Replace(t *testing.T) { cfg, ) assert.NoError(t, err) - defer p.Destroy() - assert.NotNil(t, p) done := make(chan interface{}) + p.Listen(func(e int, ctx interface{}) { if err, ok := ctx.(error); ok { if strings.Contains(err.Error(), "undefined_function()") { @@ -170,8 +169,10 @@ func Test_StaticPool_Broken_Replace(t *testing.T) { assert.Nil(t, res) <-done + p.Destroy() } + func Test_StaticPool_Broken_FromOutside(t *testing.T) { p, err := NewPool( func() *exec.Cmd { return exec.Command("php", "tests/client.php", "echo", "pipes") }, @@ -218,26 +219,33 @@ func Test_StaticPool_AllocateTimeout(t *testing.T) { NewPipeFactory(), Config{ NumWorkers: 1, - AllocateTimeout: time.Millisecond * 50, - DestroyTimeout: time.Second, + AllocateTimeout: time.Nanosecond * 1, + DestroyTimeout: time.Second * 2, }, ) - - assert.NotNil(t, p) - assert.NoError(t, err) + if err != nil { + t.Fatal(err) + } done := make(chan interface{}) go func() { - _, err := p.Exec(&Payload{Body: []byte("100")}) - assert.NoError(t, err) - close(done) + if p != nil { + _, err := p.Exec(&Payload{Body: []byte("100")}) + assert.NoError(t, err) + close(done) + } else { + t.Fatal("Pool is nil") + } }() + // to ensure that worker is already busy time.Sleep(time.Millisecond * 10) _, err = p.Exec(&Payload{Body: []byte("10")}) - assert.Error(t, err) + if err == nil { + t.Fatal("Test_StaticPool_AllocateTimeout exec should raise error") + } assert.Contains(t, err.Error(), "worker timeout") <-done diff --git a/util/network.go b/util/network.go index b9066de7..d858cb0a 100644 --- a/util/network.go +++ b/util/network.go @@ -1,8 +1,11 @@ +// +build linux darwin freebsd + package util import ( "errors" "fmt" + "github.com/valyala/tcplisten" "net" "os" "strings" @@ -27,6 +30,18 @@ func CreateListener(address string) (net.Listener, error) { } } + cfg := tcplisten.Config{ + ReusePort: true, + DeferAccept: true, + FastOpen: true, + Backlog: 0, + } + + // tcp4 is currently supported + if dsn[0] == "tcp" { + return cfg.NewListener("tcp4", dsn[1]) + } + return net.Listen(dsn[0], dsn[1]) } diff --git a/util/network_test.go b/util/network_test.go index 830e1b8a..09157ec0 100644 --- a/util/network_test.go +++ b/util/network_test.go @@ -1,8 +1,9 @@ +// +build linux darwin freebsd + package util import ( "github.com/stretchr/testify/assert" - "runtime" "testing" ) @@ -15,10 +16,6 @@ func TestCreateListener(t *testing.T) { } func TestUnixCreateListener(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("not supported on " + runtime.GOOS) - } - l, err := CreateListener("unix://file.sock") assert.NoError(t, err) l.Close() diff --git a/util/network_windows.go b/util/network_windows.go new file mode 100644 index 00000000..e5bc67c8 --- /dev/null +++ b/util/network_windows.go @@ -0,0 +1,33 @@ +// +build windows + +package util + +import ( + "errors" + "fmt" + "net" + "os" + "strings" + "syscall" +) + +// CreateListener crates socket listener based on DSN definition. +func CreateListener(address string) (net.Listener, error) { + dsn := strings.Split(address, "://") + if len(dsn) != 2 { + return nil, errors.New("invalid DSN (tcp://:6001, unix://file.sock)") + } + + if dsn[0] != "unix" && dsn[0] != "tcp" { + return nil, errors.New("invalid Protocol (tcp://:6001, unix://file.sock)") + } + + if dsn[0] == "unix" && fileExists(dsn[1]) { + err := syscall.Unlink(dsn[1]) + if err != nil { + return nil, fmt.Errorf("error during the unlink syscall: error %v", err) + } + } + + return net.Listen(dsn[0], dsn[1]) +}
\ No newline at end of file diff --git a/util/network_windows_test.go b/util/network_windows_test.go new file mode 100644 index 00000000..a5a8064e --- /dev/null +++ b/util/network_windows_test.go @@ -0,0 +1,16 @@ +// +build windows + +package util + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestCreateListener(t *testing.T) { + _, err := CreateListener("unexpected dsn") + assert.Error(t, err, "Invalid DSN (tcp://:6001, unix://file.sock)") + + _, err = CreateListener("aaa://192.168.0.1") + assert.Error(t, err, "Invalid Protocol (tcp://:6001, unix://file.sock)") +}
\ No newline at end of file diff --git a/worker_test.go b/worker_test.go index e8cbef90..c21e67cb 100644 --- a/worker_test.go +++ b/worker_test.go @@ -4,6 +4,7 @@ import ( "github.com/stretchr/testify/assert" "os/exec" "testing" + "time" ) func Test_GetState(t *testing.T) { @@ -160,21 +161,23 @@ func Test_Echo_Slow(t *testing.T) { func Test_Broken(t *testing.T) { cmd := exec.Command("php", "tests/client.php", "broken", "pipes") - w, _ := NewPipeFactory().SpawnWorker(cmd) + w, err := NewPipeFactory().SpawnWorker(cmd) + if err != nil { + t.Fatal(err) + } + go func() { err := w.Wait() assert.Error(t, err) assert.Contains(t, err.Error(), "undefined_function()") }() - defer func() { - err := w.Stop() - assert.Error(t, err) - }() - res, err := w.Exec(&Payload{Body: []byte("hello")}) assert.Nil(t, res) assert.NotNil(t, err) + + time.Sleep(time.Second) + assert.NoError(t, w.Stop()) } func Test_OnStarted(t *testing.T) { |