Browse Source

headers: New 'request_header' directive; handle Host header specially

Before this change, only response headers could be manipulated with the
Caddyfile's 'header' directive.

Also handle the request Host header specially, since the Go standard
library treats it separately from the other header fields...
master
Matthew Holt 5 years ago
parent
commit
005a11cf4b
No known key found for this signature in database GPG Key ID: 2A349DD577D586A5
  1. 1
      caddyconfig/httpcaddyfile/directives.go
  2. 69
      modules/caddyhttp/headers/caddyfile.go
  3. 11
      modules/caddyhttp/headers/headers.go

1
caddyconfig/httpcaddyfile/directives.go

@ -30,6 +30,7 @@ var defaultDirectiveOrder = []string{
"rewrite", "rewrite",
"try_files", "try_files",
"headers", "headers",
"request_header",
"encode", "encode",
"templates", "templates",
"redir", "redir",

69
modules/caddyhttp/headers/caddyfile.go

@ -24,9 +24,11 @@ import (
func init() { func init() {
httpcaddyfile.RegisterHandlerDirective("headers", parseCaddyfile) httpcaddyfile.RegisterHandlerDirective("headers", parseCaddyfile)
httpcaddyfile.RegisterHandlerDirective("request_header", parseReqHdrCaddyfile)
} }
// parseCaddyfile sets up the handler from Caddyfile tokens. Syntax: // parseCaddyfile sets up the handler for response headers from
// Caddyfile tokens. Syntax:
// //
// headers [<matcher>] [[+|-]<field> <value>] { // headers [<matcher>] [[+|-]<field> <value>] {
// [+][<field>] [<value>] // [+][<field>] [<value>]
@ -43,9 +45,11 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
if h.NextArg() { if h.NextArg() {
hasArgs = true hasArgs = true
field := h.Val() field := h.Val()
h.NextArg() var value string
value := h.Val() if h.NextArg() {
processCaddyfileLine(hdr, field, value) value = h.Val()
}
processCaddyfileLineRespHdr(hdr, field, value)
} }
// if not, they should be in a block // if not, they should be in a block
@ -58,31 +62,68 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
if h.NextArg() { if h.NextArg() {
value = h.Val() value = h.Val()
} }
processCaddyfileLine(hdr, field, value) processCaddyfileLineRespHdr(hdr, field, value)
} }
} }
return hdr, nil return hdr, nil
} }
func processCaddyfileLine(hdr *Headers, field, value string) { // parseReqHdrCaddyfile sets up the handler for request headers
// from Caddyfile tokens. Syntax:
//
// request_header [<matcher>] [[+|-]<field> <value>]
//
func parseReqHdrCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
hdr := new(Headers)
for h.Next() {
if !h.NextArg() {
return nil, h.ArgErr()
}
field := h.Val()
var value string
if h.NextArg() {
value = h.Val()
}
if hdr.Request == nil {
hdr.Request = new(HeaderOps)
}
if strings.HasPrefix(field, "+") { if strings.HasPrefix(field, "+") {
if hdr.Request.Add == nil {
hdr.Request.Add = make(http.Header)
}
hdr.Request.Add.Set(field[1:], value)
} else if strings.HasPrefix(field, "-") {
hdr.Request.Delete = append(hdr.Request.Delete, field[1:])
} else {
if hdr.Request.Set == nil {
hdr.Request.Set = make(http.Header)
}
hdr.Request.Set.Set(field, value)
}
if h.NextArg() {
return nil, h.ArgErr()
}
}
return hdr, nil
}
func processCaddyfileLineRespHdr(hdr *Headers, field, value string) {
if hdr.Response == nil { if hdr.Response == nil {
hdr.Response = &RespHeaderOps{HeaderOps: new(HeaderOps)} hdr.Response = &RespHeaderOps{
HeaderOps: new(HeaderOps),
Deferred: true,
} }
}
if strings.HasPrefix(field, "+") {
if hdr.Response.Add == nil { if hdr.Response.Add == nil {
hdr.Response.Add = make(http.Header) hdr.Response.Add = make(http.Header)
} }
hdr.Response.Add.Set(field[1:], value) hdr.Response.Add.Set(field[1:], value)
} else if strings.HasPrefix(field, "-") { } else if strings.HasPrefix(field, "-") {
if hdr.Response == nil {
hdr.Response = &RespHeaderOps{HeaderOps: new(HeaderOps)}
}
hdr.Response.Delete = append(hdr.Response.Delete, field[1:]) hdr.Response.Delete = append(hdr.Response.Delete, field[1:])
hdr.Response.Deferred = true
} else { } else {
if hdr.Response == nil {
hdr.Response = &RespHeaderOps{HeaderOps: new(HeaderOps)}
}
if hdr.Response.Set == nil { if hdr.Response.Set == nil {
hdr.Response.Set = make(http.Header) hdr.Response.Set = make(http.Header)
} }

11
modules/caddyhttp/headers/headers.go

@ -58,7 +58,18 @@ type RespHeaderOps struct {
func (h Headers) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { func (h Headers) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
repl := r.Context().Value(caddy.ReplacerCtxKey).(caddy.Replacer) repl := r.Context().Value(caddy.ReplacerCtxKey).(caddy.Replacer)
apply(h.Request, r.Header, repl) apply(h.Request, r.Header, repl)
// request header's Host is handled specially by the
// Go standard library, so if that header was changed,
// change it in the Host field since the Header won't
// be used
if intendedHost := r.Header.Get("Host"); intendedHost != "" {
r.Host = intendedHost
r.Header.Del("Host")
}
if h.Response != nil { if h.Response != nil {
if h.Response.Deferred || h.Response.Require != nil { if h.Response.Deferred || h.Response.Require != nil {
w = &responseWriterWrapper{ w = &responseWriterWrapper{

Loading…
Cancel
Save