diff options
-rw-r--r-- | .github/workflows/ci-build.yml | 91 | ||||
-rw-r--r-- | CHANGELOG.md | 5 | ||||
-rwxr-xr-x | build.sh | 2 | ||||
-rw-r--r-- | composer.json | 2 | ||||
-rw-r--r-- | service/http/response.go | 58 | ||||
-rw-r--r-- | service/http/response_test.go | 63 |
6 files changed, 208 insertions, 13 deletions
diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml new file mode 100644 index 00000000..3031cbb4 --- /dev/null +++ b/.github/workflows/ci-build.yml @@ -0,0 +1,91 @@ +name: CI + +on: + push: + branches: + tags: + pull_request: + +jobs: + + build: + name: Build + runs-on: ubuntu-latest + strategy: + matrix: + php: [7.1, 7.2, 7.3, 7.4] + steps: + + - name: Set up Go 1.12 + uses: actions/setup-go@v1 + with: + go-version: 1.12 + id: go + + - name: Check out code into the Go module directory + uses: actions/checkout@v1 + + - name: Install PHP + uses: shivammathur/setup-php@master + with: + php-version: ${{ matrix.php }} + extension-csv: dom + coverage: xdebug + + - name: Debug if needed + run: | + export DEBUG=${DEBUG:-false} + if [[ "$DEBUG" == "true" ]]; then + env + go env + fi + env: + DEBUG: ${{ secrets.DEBUG }} + + - name: Show versions + run: | + php -v + composer -V + go version + + - name: Install dependencies and download binary roadrunner + run: | + export GO111MODULE=on + go mod download + composer install --no-interaction --prefer-source + find src/ -name "*.php" -print0 | xargs -0 -n1 -P8 php -l + chmod +x bin/rr && 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 + 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 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + if: diff --git a/CHANGELOG.md b/CHANGELOG.md index a40a7096..12924618 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +v1.5.2 (05.12.2019) +------------------- +- added support for symfony/console 5.0 by @coxa +- added support for HTTP2 trailers by @filakhtov + v1.5.1 (22.10.2019) ------------------- - bugfix: do not halt stop sequence in case of service error @@ -3,7 +3,7 @@ cd $(dirname "${BASH_SOURCE[0]}") OD="$(pwd)" # Pushes application version into the build information. -RR_VERSION=1.5.1 +RR_VERSION=1.5.2 # Hardcode some values to the core package LDFLAGS="$LDFLAGS -X github.com/spiral/roadrunner/cmd/rr/cmd.Version=${RR_VERSION}" diff --git a/composer.json b/composer.json index 7715ee2b..ef09fe20 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "spiral/goridge": "^2.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.0", - "symfony/console": "^2.5.0 || ^3.0.0 || ^4.0.0", + "symfony/console": "^2.5.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", "zendframework/zend-diactoros": "^1.3 || ^2.0" }, "autoload": { diff --git a/service/http/response.go b/service/http/response.go index 8bd770ec..166ced82 100644 --- a/service/http/response.go +++ b/service/http/response.go @@ -2,9 +2,11 @@ package http import ( "encoding/json" - "github.com/spiral/roadrunner" "io" "net/http" + "strings" + + "github.com/spiral/roadrunner" ) // Response handles PSR7 response logic. @@ -31,16 +33,16 @@ func NewResponse(p *roadrunner.Payload) (*Response, error) { // Write writes response headers, status and body into ResponseWriter. func (r *Response) Write(w http.ResponseWriter) error { + p, h := handlePushHeaders(r.Headers) + if pusher, ok := w.(http.Pusher); ok { + for _, v := range p { + pusher.Push(v, nil) + } + } + + h = handleTrailers(h) for n, h := range r.Headers { for _, v := range h { - if n == "http2-push" { - if pusher, ok := w.(http.Pusher); ok { - pusher.Push(v, nil) - } - - continue - } - w.Header().Add(n, v) } } @@ -59,3 +61,41 @@ func (r *Response) Write(w http.ResponseWriter) error { return nil } + +func handlePushHeaders(h map[string][]string) ([]string, map[string][]string) { + var p []string + pushHeader, ok := h["http2-push"] + if !ok { + return p, h + } + + for _, v := range pushHeader { + p = append(p, v) + } + + delete(h, "http2-push") + + return p, h +} + +func handleTrailers(h map[string][]string) map[string][]string { + trailers, ok := h["trailer"] + if !ok { + return h + } + + for _, tr := range trailers { + for _, n := range strings.Split(tr, ",") { + n = strings.Trim(n, "\t ") + if v, ok := h[n]; ok { + h["Trailer:"+n] = v + + delete(h, n) + } + } + } + + delete(h, "trailer") + + return h +} diff --git a/service/http/response_test.go b/service/http/response_test.go index f885be7f..ad524567 100644 --- a/service/http/response_test.go +++ b/service/http/response_test.go @@ -3,10 +3,11 @@ package http import ( "bytes" "errors" - "github.com/spiral/roadrunner" - "github.com/stretchr/testify/assert" "net/http" "testing" + + "github.com/spiral/roadrunner" + "github.com/stretchr/testify/assert" ) type testWriter struct { @@ -15,6 +16,8 @@ type testWriter struct { wroteHeader bool code int err error + pushErr error + pushes []string } func (tw *testWriter) Header() http.Header { return tw.h } @@ -34,6 +37,12 @@ func (tw *testWriter) Write(p []byte) (int, error) { 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 := NewResponse(&roadrunner.Payload{Context: []byte(`invalid payload`)}) assert.Error(t, err) @@ -90,3 +99,53 @@ func TestNewResponse_StreamError(t *testing.T) { 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 := 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 := 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["trailer"]) + assert.Nil(t, w.h["foo"]) + assert.Nil(t, w.h["baz"]) + 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 := 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")) +} |