summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci-build.yml91
-rw-r--r--CHANGELOG.md5
-rwxr-xr-xbuild.sh2
-rw-r--r--composer.json2
-rw-r--r--service/http/response.go58
-rw-r--r--service/http/response_test.go63
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
diff --git a/build.sh b/build.sh
index a071c7ad..128b2f09 100755
--- a/build.sh
+++ b/build.sh
@@ -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"))
+}