Skip to content

Commit

Permalink
Merge pull request #421 from spiral/plugin/headers
Browse files Browse the repository at this point in the history
[RR2] Headers plugin
  • Loading branch information
rustatian authored Dec 2, 2020
2 parents 7091082 + 509db1b commit bdabf97
Show file tree
Hide file tree
Showing 21 changed files with 711 additions and 61 deletions.
31 changes: 16 additions & 15 deletions .github/workflows/ci-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,26 +65,27 @@ jobs:

- name: Run golang tests
run: |
go test -v -race . -tags=debug -coverprofile=lib.txt -covermode=atomic
go test -v -race ./plugins/rpc -tags=debug -coverprofile=rpc_config.txt -covermode=atomic
go test -v -race ./plugins/rpc/tests -tags=debug -coverprofile=rpc.txt -covermode=atomic
go test -v -race ./plugins/config/tests -tags=debug -coverprofile=plugin_config.txt -covermode=atomic
go test -v -race ./plugins/logger/tests -tags=debug -coverprofile=logger.txt -covermode=atomic
go test -v -race ./plugins/server/tests -tags=debug -coverprofile=server.txt -covermode=atomic
go test -v -race ./plugins/metrics/tests -tags=debug -coverprofile=metrics.txt -covermode=atomic
go test -v -race ./plugins/informer/tests -tags=debug -coverprofile=informer.txt -covermode=atomic
go test -v -race ./plugins/resetter/tests -tags=debug -coverprofile=informer.txt -covermode=atomic
go test -v -race ./plugins/http/attributes -tags=debug -coverprofile=attributes.txt -covermode=atomic
go test -v -race ./plugins/http/tests -tags=debug -coverprofile=http_tests.txt -covermode=atomic
go test -v -race ./plugins/gzip/tests -tags=debug -coverprofile=gzip.txt -covermode=atomic
go test -v -race -cover ./plugins/static/tests -tags=debug -coverprofile=static.txt -covermode=atomic
go test -v -race -cover ./plugins/static -tags=debug -coverprofile=static_root.txt -covermode=atomic
go test -v -race -cover -tags=debug -coverprofile=lib.txt -covermode=atomic .
go test -v -race -cover -tags=debug -coverprofile=rpc_config.txt -covermode=atomic ./plugins/rpc
go test -v -race -cover -tags=debug -coverprofile=rpc.txt -covermode=atomic ./plugins/rpc/tests
go test -v -race -cover -tags=debug -coverprofile=plugin_config.txt -covermode=atomic ./plugins/config/tests
go test -v -race -cover -tags=debug -coverprofile=logger.txt -covermode=atomic ./plugins/logger/tests
go test -v -race -cover -tags=debug -coverprofile=server.txt -covermode=atomic ./plugins/server/tests
go test -v -race -cover -tags=debug -coverprofile=metrics.txt -covermode=atomic ./plugins/metrics/tests
go test -v -race -cover -tags=debug -coverprofile=informer.txt -covermode=atomic ./plugins/informer/tests
go test -v -race -cover -tags=debug -coverprofile=informer.txt -covermode=atomic ./plugins/resetter/tests
go test -v -race -cover -tags=debug -coverprofile=attributes.txt -covermode=atomic ./plugins/http/attributes
go test -v -race -cover -tags=debug -coverprofile=http_tests.txt -covermode=atomic ./plugins/http/tests
go test -v -race -cover -tags=debug -coverprofile=gzip.txt -covermode=atomic ./plugins/gzip/tests
go test -v -race -cover -tags=debug -coverprofile=static.txt -covermode=atomic ./plugins/static/tests
go test -v -race -cover -tags=debug -coverprofile=static_root.txt -covermode=atomic ./plugins/static
go test -v -race -cover -tags=debug -coverprofile=headers.txt -covermode=atomic ./plugins/headers/tests
- name: Run code coverage
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: static.txt, static_root.txt, gzip.txt, lib.txt, rpc_config.txt, rpc.txt, plugin_config.txt, logger.txt, server.txt, metrics.txt, informer.txt attributes.txt http_tests.txt
files: headers.txt, static.txt, static_root.txt, gzip.txt, lib.txt, rpc_config.txt, rpc.txt, plugin_config.txt, logger.txt, server.txt, metrics.txt, informer.txt attributes.txt http_tests.txt
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false
Expand Down
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ run:
- plugins/http/tests/rpc_test_old.go
- plugins/http/tests/config_test.go
- plugins/static/tests/static_plugin_test.go
- plugins/headers/tests/old.go
linters:
disable-all: true
enable:
Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@ test:
go test -v -race -cover ./plugins/http/tests -tags=debug
go test -v -race -cover ./plugins/gzip/tests -tags=debug
go test -v -race -cover ./plugins/static/tests -tags=debug
go test -v -race -cover ./plugins/static -tags=debug
go test -v -race -cover ./plugins/static -tags=debug
go test -v -race -cover ./plugins/headers/tests -tags=debug

test_headers:
go test -v -race -cover ./plugins/headers/tests -tags=debug
4 changes: 2 additions & 2 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
coverage:
status:
project: off
patch: off
project: true
patch: false
4 changes: 2 additions & 2 deletions plugins/gzip/tests/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
)

func TestGzipPlugin(t *testing.T) {
cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.DebugLevel), endure.Visualize(endure.StdOut, ""))
cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.DebugLevel))
assert.NoError(t, err)

cfg := &config.Viper{
Expand Down Expand Up @@ -102,7 +102,7 @@ func headerCheck(t *testing.T) {
}

func TestMiddlewareNotExist(t *testing.T) {
cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.DebugLevel), endure.Visualize(endure.StdOut, ""))
cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.DebugLevel))
assert.NoError(t, err)

cfg := &config.Viper{
Expand Down
36 changes: 36 additions & 0 deletions plugins/headers/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package headers

// Config declares headers service configuration.
type Config struct {
Headers struct {
// CORS settings.
CORS *CORSConfig

// Request headers to add to every payload send to PHP.
Request map[string]string

// Response headers to add to every payload generated by PHP.
Response map[string]string
}
}

// CORSConfig headers configuration.
type CORSConfig struct {
// AllowedOrigin: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
AllowedOrigin string

// AllowedHeaders: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
AllowedHeaders string

// AllowedMethods: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
AllowedMethods string

// AllowCredentials https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
AllowCredentials *bool

// ExposeHeaders: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers
ExposedHeaders string

// MaxAge of CORS headers in seconds/
MaxAge int
}
117 changes: 117 additions & 0 deletions plugins/headers/plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package headers

import (
"net/http"
"strconv"

"github.com/spiral/errors"
"github.com/spiral/roadrunner/v2/plugins/config"
)

// ID contains default service name.
const PluginName = "headers"
const RootPluginName = "http"

// Service serves headers files. Potentially convert into middleware?
type Plugin struct {
// server configuration (location, forbidden files and etc)
cfg *Config
}

// Init must return configure service and return true if service hasStatus enabled. Must return error in case of
// misconfiguration. Services must not be used without proper configuration pushed first.
func (s *Plugin) Init(cfg config.Configurer) error {
const op = errors.Op("headers plugin init")
err := cfg.UnmarshalKey(RootPluginName, &s.cfg)
if err != nil {
return errors.E(op, errors.Disabled, err)
}

return nil
}

// middleware must return true if request/response pair is handled within the middleware.
func (s *Plugin) Middleware(next http.Handler) http.HandlerFunc {
// Define the http.HandlerFunc
return func(w http.ResponseWriter, r *http.Request) {
if s.cfg.Headers.Request != nil {
for k, v := range s.cfg.Headers.Request {
r.Header.Add(k, v)
}
}

if s.cfg.Headers.Response != nil {
for k, v := range s.cfg.Headers.Response {
w.Header().Set(k, v)
}
}

if s.cfg.Headers.CORS != nil {
if r.Method == http.MethodOptions {
s.preflightRequest(w)
return
}
s.corsHeaders(w)
}

next.ServeHTTP(w, r)
}
}

func (s *Plugin) Name() string {
return PluginName
}

// configure OPTIONS response
func (s *Plugin) preflightRequest(w http.ResponseWriter) {
headers := w.Header()

headers.Add("Vary", "Origin")
headers.Add("Vary", "Access-Control-Request-Method")
headers.Add("Vary", "Access-Control-Request-Headers")

if s.cfg.Headers.CORS.AllowedOrigin != "" {
headers.Set("Access-Control-Allow-Origin", s.cfg.Headers.CORS.AllowedOrigin)
}

if s.cfg.Headers.CORS.AllowedHeaders != "" {
headers.Set("Access-Control-Allow-Headers", s.cfg.Headers.CORS.AllowedHeaders)
}

if s.cfg.Headers.CORS.AllowedMethods != "" {
headers.Set("Access-Control-Allow-Methods", s.cfg.Headers.CORS.AllowedMethods)
}

if s.cfg.Headers.CORS.AllowCredentials != nil {
headers.Set("Access-Control-Allow-Credentials", strconv.FormatBool(*s.cfg.Headers.CORS.AllowCredentials))
}

if s.cfg.Headers.CORS.MaxAge > 0 {
headers.Set("Access-Control-Max-Age", strconv.Itoa(s.cfg.Headers.CORS.MaxAge))
}

w.WriteHeader(http.StatusOK)
}

// configure CORS headers
func (s *Plugin) corsHeaders(w http.ResponseWriter) {
headers := w.Header()

headers.Add("Vary", "Origin")

if s.cfg.Headers.CORS.AllowedOrigin != "" {
headers.Set("Access-Control-Allow-Origin", s.cfg.Headers.CORS.AllowedOrigin)
}

if s.cfg.Headers.CORS.AllowedHeaders != "" {
headers.Set("Access-Control-Allow-Headers", s.cfg.Headers.CORS.AllowedHeaders)
}

if s.cfg.Headers.CORS.ExposedHeaders != "" {
headers.Set("Access-Control-Expose-Headers", s.cfg.Headers.CORS.ExposedHeaders)
}

if s.cfg.Headers.CORS.AllowCredentials != nil {
headers.Set("Access-Control-Allow-Credentials", strconv.FormatBool(*s.cfg.Headers.CORS.AllowCredentials))
}
}
37 changes: 37 additions & 0 deletions plugins/headers/tests/configs/.rr-cors-headers.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
server:
command: "php ../../../tests/http/client.php headers pipes"
user: ""
group: ""
env:
"RR_HTTP": "true"
relay: "pipes"
relayTimeout: "20s"

http:
debug: true
address: 127.0.0.1:22855
maxRequestSize: 1024
middleware: [ "headers" ]
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" ]
# Additional HTTP headers and CORS control.
headers:
cors:
allowedOrigin: "*"
allowedHeaders: "*"
allowedMethods: "GET,POST,PUT,DELETE"
allowCredentials: true
exposedHeaders: "Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma"
maxAge: 600
request:
"input": "custom-header"
response:
"output": "output-header"
pool:
numWorkers: 2
maxJobs: 0
allocateTimeout: 60s
destroyTimeout: 60s


37 changes: 37 additions & 0 deletions plugins/headers/tests/configs/.rr-headers-init.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
server:
command: "php ../../../tests/http/client.php echo pipes"
user: ""
group: ""
env:
"RR_HTTP": "true"
relay: "pipes"
relayTimeout: "20s"

http:
debug: true
address: 127.0.0.1:33453
maxRequestSize: 1024
middleware: [ "headers" ]
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" ]
# Additional HTTP headers and CORS control.
headers:
cors:
allowedOrigin: "*"
allowedHeaders: "*"
allowedMethods: "GET,POST,PUT,DELETE"
allowCredentials: true
exposedHeaders: "Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma"
maxAge: 600
request:
"Example-Request-Header": "Value"
response:
"X-Powered-By": "RoadRunner"
pool:
numWorkers: 2
maxJobs: 0
allocateTimeout: 60s
destroyTimeout: 60s


30 changes: 30 additions & 0 deletions plugins/headers/tests/configs/.rr-req-headers.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
server:
command: "php ../../../tests/http/client.php header pipes"
user: ""
group: ""
env:
"RR_HTTP": "true"
relay: "pipes"
relayTimeout: "20s"

http:
debug: true
address: 127.0.0.1:22655
maxRequestSize: 1024
middleware: [ "headers" ]
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" ]
# Additional HTTP headers and CORS control.
headers:
request:
"input": "custom-header"
response:
"output": "output-header"
pool:
numWorkers: 2
maxJobs: 0
allocateTimeout: 60s
destroyTimeout: 60s


30 changes: 30 additions & 0 deletions plugins/headers/tests/configs/.rr-res-headers.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
server:
command: "php ../../../tests/http/client.php header pipes"
user: ""
group: ""
env:
"RR_HTTP": "true"
relay: "pipes"
relayTimeout: "20s"

http:
debug: true
address: 127.0.0.1:22455
maxRequestSize: 1024
middleware: [ "headers" ]
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" ]
# Additional HTTP headers and CORS control.
headers:
request:
"input": "custom-header"
response:
"output": "output-header"
pool:
numWorkers: 2
maxJobs: 0
allocateTimeout: 60s
destroyTimeout: 60s


Loading

0 comments on commit bdabf97

Please sign in to comment.