diff options
author | Matthew Holt <mholt@users.noreply.github.com> | 2019-05-20 15:46:47 -0600 |
---|---|---|
committer | Matthew Holt <mholt@users.noreply.github.com> | 2019-05-20 15:46:52 -0600 |
commit | d22f64e6d41117c242fc4ecd00179e68b8f118c5 (patch) | |
tree | de059f4423337b6d58c6322c110eb6e928f1080e | |
parent | 22995e5655b3d5117743d98bf5c7ba8ed335a2c5 (diff) |
Implement headers middleware
-rw-r--r-- | cmd/caddy2/main.go | 1 | ||||
-rw-r--r-- | modules/caddyhttp/headers/headers.go | 82 | ||||
-rw-r--r-- | modules/caddyhttp/headers/headers_test.go | 7 | ||||
-rw-r--r-- | modules/caddyhttp/staticfiles/matcher.go | 3 |
4 files changed, 93 insertions, 0 deletions
diff --git a/cmd/caddy2/main.go b/cmd/caddy2/main.go index 81f670b..f311e5c 100644 --- a/cmd/caddy2/main.go +++ b/cmd/caddy2/main.go @@ -6,6 +6,7 @@ import ( // this is where modules get plugged in _ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp" _ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp/caddylog" + _ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp/headers" _ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp/reverseproxy" _ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp/staticfiles" _ "bitbucket.org/lightcodelabs/caddy2/modules/caddytls" diff --git a/modules/caddyhttp/headers/headers.go b/modules/caddyhttp/headers/headers.go new file mode 100644 index 0000000..1826c7a --- /dev/null +++ b/modules/caddyhttp/headers/headers.go @@ -0,0 +1,82 @@ +package headers + +import ( + "net/http" + "strings" + + "bitbucket.org/lightcodelabs/caddy2" + "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp" +) + +func init() { + caddy2.RegisterModule(caddy2.Module{ + Name: "http.middleware.headers", + New: func() (interface{}, error) { return new(Headers), nil }, + }) +} + +// Headers is a middleware which can mutate HTTP headers. +type Headers struct { + Request HeaderOps + Response RespHeaderOps +} + +// HeaderOps defines some operations to +// perform on HTTP headers. +type HeaderOps struct { + Add http.Header + Set http.Header + Delete []string +} + +// RespHeaderOps is like HeaderOps, but +// optionally deferred until response time. +type RespHeaderOps struct { + HeaderOps + Deferred bool `json:"deferred"` +} + +func (h Headers) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { + apply(h.Request, r.Header) + if h.Response.Deferred { + w = &responseWriterWrapper{ + ResponseWriterWrapper: &caddyhttp.ResponseWriterWrapper{ResponseWriter: w}, + headerOps: h.Response.HeaderOps, + } + } else { + apply(h.Response.HeaderOps, w.Header()) + } + return next.ServeHTTP(w, r) +} + +func apply(ops HeaderOps, hdr http.Header) { + for fieldName, vals := range ops.Add { + for _, v := range vals { + hdr.Add(fieldName, v) + } + } + for fieldName, vals := range ops.Set { + hdr.Set(fieldName, strings.Join(vals, ",")) + } + for _, fieldName := range ops.Delete { + hdr.Del(fieldName) + } +} + +// responseWriterWrapper defers response header +// operations until WriteHeader is called. +type responseWriterWrapper struct { + *caddyhttp.ResponseWriterWrapper + headerOps HeaderOps +} + +func (rww *responseWriterWrapper) WriteHeader(status int) { + apply(rww.headerOps, rww.ResponseWriterWrapper.Header()) + rww.ResponseWriterWrapper.WriteHeader(status) +} + +// Interface guards +var ( + _ caddyhttp.MiddlewareHandler = (*Headers)(nil) + _ caddyhttp.HTTPInterfaces = (*responseWriterWrapper)(nil) +) diff --git a/modules/caddyhttp/headers/headers_test.go b/modules/caddyhttp/headers/headers_test.go new file mode 100644 index 0000000..cb83d47 --- /dev/null +++ b/modules/caddyhttp/headers/headers_test.go @@ -0,0 +1,7 @@ +package headers + +import "testing" + +func TestReqHeaders(t *testing.T) { + // TODO: write tests +} diff --git a/modules/caddyhttp/staticfiles/matcher.go b/modules/caddyhttp/staticfiles/matcher.go index cccf54b..9ce3f4c 100644 --- a/modules/caddyhttp/staticfiles/matcher.go +++ b/modules/caddyhttp/staticfiles/matcher.go @@ -16,6 +16,8 @@ func init() { }) } +// FileMatcher is a matcher that can match requests +// based on the local file system. // TODO: Not sure how to do this well; we'd need the ability to // hide files, etc... // TODO: Also consider a feature to match directory that @@ -29,6 +31,7 @@ type FileMatcher struct { Flags []string `json:"flags"` } +// Match matches the request r against m. func (m FileMatcher) Match(r *http.Request) bool { // TODO: sanitize path fullPath := filepath.Join(m.Root, m.Path) |