Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 05:15 UTC
reverse_proxy/headers: Expose header replacement ability in Caddyfile
Adds header_up and header_down subdirectives to reverse_proxy
Matthew Holt committed
Sep 20, 2019 at 13:13 UTC
2f684e42d5b8b78fe688b2f7e31d54d2333c0701
2 files changed
+107
-36
modules/caddyhttp/headers/caddyfile.go
+62
-36
@@ -30,26 +30,40 @@ func init() {
30
// parseCaddyfile sets up the handler for response headers from
31
// Caddyfile tokens. Syntax:
32
//
33
-// headers [<matcher>] [[+|-]<field> <value>] {
34
-// [+][<field>] [<value>]
35
-// [-<field>]
33
+// headers [<matcher>] [[+|-]<field> [<value|regexp>] [<replacement>]] {
34
+// [+]<field> [<value|regexp> [<replacement>]]
35
+// -<field>
36
// }
37
//
38
// Either a block can be opened or a single header field can be configured
39
// in the first line, but not both in the same directive.
40
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
41
hdr := new(Handler)
42
+
43
+ makeResponseOps := func() {
44
+ if hdr.Response == nil {
45
+ hdr.Response = &RespHeaderOps{
46
+ HeaderOps: new(HeaderOps),
47
+ Deferred: true,
48
+ }
49
+ }
50
+ }
51
+
52
for h.Next() {
53
// first see if headers are in the initial line
54
var hasArgs bool
55
if h.NextArg() {
56
hasArgs = true
57
field := h.Val()
48
- var value string
58
+ var value, replacement string
59
if h.NextArg() {
60
value = h.Val()
61
}
52
- processCaddyfileLineRespHdr(hdr, field, value)
62
+ if h.NextArg() {
63
+ replacement = h.Val()
64
+ }
65
+ makeResponseOps()
66
+ CaddyfileHeaderOp(hdr.Response.HeaderOps, field, value, replacement)
67
}
68
69
// if not, they should be in a block
@@ -58,20 +72,25 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
72
return nil, h.Err("cannot specify headers in both arguments and block")
73
}
74
field := h.Val()
61
- var value string
75
+ var value, replacement string
76
if h.NextArg() {
77
value = h.Val()
78
}
65
- processCaddyfileLineRespHdr(hdr, field, value)
79
+ if h.NextArg() {
80
+ replacement = h.Val()
81
+ }
82
+ makeResponseOps()
83
+ CaddyfileHeaderOp(hdr.Response.HeaderOps, field, value, replacement)
84
}
85
}
86
+
87
return hdr, nil
88
}
89
90
// parseReqHdrCaddyfile sets up the handler for request headers
91
// from Caddyfile tokens. Syntax:
92
//
74
-// request_header [<matcher>] [[+|-]<field> <value>]
93
+// request_header [<matcher>] [[+|-]<field> [<value|regexp>] [<replacement>]]
94
//
95
func parseReqHdrCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
96
hdr := new(Handler)
@@ -80,27 +99,18 @@ func parseReqHdrCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler,
99
return nil, h.ArgErr()
100
}
101
field := h.Val()
83
- var value string
102
+ var value, replacement string
103
if h.NextArg() {
104
value = h.Val()
105
}
106
+ if h.NextArg() {
107
+ replacement = h.Val()
108
+ }
109
110
if hdr.Request == nil {
111
hdr.Request = new(HeaderOps)
112
}
91
- if strings.HasPrefix(field, "+") {
92
- if hdr.Request.Add == nil {
93
- hdr.Request.Add = make(http.Header)
94
- }
95
- hdr.Request.Add.Set(field[1:], value)
96
- } else if strings.HasPrefix(field, "-") {
97
- hdr.Request.Delete = append(hdr.Request.Delete, field[1:])
98
- } else {
99
- if hdr.Request.Set == nil {
100
- hdr.Request.Set = make(http.Header)
101
- }
102
- hdr.Request.Set.Set(field, value)
103
- }
113
+ CaddyfileHeaderOp(hdr.Request, field, value, replacement)
114
115
if h.NextArg() {
116
return nil, h.ArgErr()
@@ -109,24 +119,40 @@ func parseReqHdrCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler,
119
return hdr, nil
120
}
121
112
-func processCaddyfileLineRespHdr(hdr *Handler, field, value string) {
113
- if hdr.Response == nil {
114
- hdr.Response = &RespHeaderOps{
115
- HeaderOps: new(HeaderOps),
116
- Deferred: true,
117
- }
118
- }
122
+// CaddyfileHeaderOp applies a new header operation according to
123
+// field, value, and replacement. The field can be prefixed with
124
+// "+" or "-" to specify adding or removing; otherwise, the value
125
+// will be set (overriding any previous value). If replacement is
126
+// non-empty, value will be treated as a regular expression which
127
+// will be used to search and then replacement will be used to
128
+// complete the substring replacement; in that case, any + or -
129
+// prefix to field will be ignored.
130
+func CaddyfileHeaderOp(ops *HeaderOps, field, value, replacement string) {
131
if strings.HasPrefix(field, "+") {
120
- if hdr.Response.Add == nil {
121
- hdr.Response.Add = make(http.Header)
132
+ if ops.Add == nil {
133
+ ops.Add = make(http.Header)
134
}
123
- hdr.Response.Add.Set(field[1:], value)
135
+ ops.Add.Set(field[1:], value)
136
} else if strings.HasPrefix(field, "-") {
125
- hdr.Response.Delete = append(hdr.Response.Delete, field[1:])
137
+ ops.Delete = append(ops.Delete, field[1:])
138
} else {
127
- if hdr.Response.Set == nil {
128
- hdr.Response.Set = make(http.Header)
139
+ if replacement == "" {
140
+ if ops.Set == nil {
141
+ ops.Set = make(http.Header)
142
+ }
143
+ ops.Set.Set(field, value)
144
+ } else {
145
+ if ops.Replace == nil {
146
+ ops.Replace = make(map[string][]Replacement)
147
+ }
148
+ field = strings.TrimLeft(field, "+-")
149
+ ops.Replace[field] = append(
150
+ ops.Replace[field],
151
+ Replacement{
152
+ SearchRegexp: value,
153
+ Replace: replacement,
154
+ },
155
+ )
156
}
130
- hdr.Response.Set.Set(field, value)
157
}
158
}
modules/caddyhttp/reverseproxy/caddyfile.go
+45
@@ -25,6 +25,7 @@ import (
25
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
26
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
27
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
28
+ "github.com/caddyserver/caddy/v2/modules/caddyhttp/headers"
29
"github.com/dustin/go-humanize"
30
)
31
@@ -67,6 +68,10 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
68
// unhealthy_status <status>
69
// unhealthy_latency <duration>
70
//
71
+// # header manipulation
72
+// header_up [+|-]<field> [<value|regexp> [<replacement>]]
73
+// header_down [+|-]<field> [<value|regexp> [<replacement>]]
74
+//
75
// # round trip
76
// transport <name> {
77
// ...
@@ -327,6 +332,46 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
332
}
333
h.HealthChecks.Passive.UnhealthyLatency = caddy.Duration(dur)
334
335
+ case "header_up":
336
+ if h.Headers == nil {
337
+ h.Headers = new(headers.Handler)
338
+ }
339
+ if h.Headers.Request == nil {
340
+ h.Headers.Request = new(headers.HeaderOps)
341
+ }
342
+ args := d.RemainingArgs()
343
+ switch len(args) {
344
+ case 1:
345
+ headers.CaddyfileHeaderOp(h.Headers.Request, args[0], "", "")
346
+ case 2:
347
+ headers.CaddyfileHeaderOp(h.Headers.Request, args[0], args[1], "")
348
+ case 3:
349
+ headers.CaddyfileHeaderOp(h.Headers.Request, args[0], args[1], args[2])
350
+ default:
351
+ return d.ArgErr()
352
+ }
353
+
354
+ case "header_down":
355
+ if h.Headers == nil {
356
+ h.Headers = new(headers.Handler)
357
+ }
358
+ if h.Headers.Response == nil {
359
+ h.Headers.Response = &headers.RespHeaderOps{
360
+ HeaderOps: new(headers.HeaderOps),
361
+ }
362
+ }
363
+ args := d.RemainingArgs()
364
+ switch len(args) {
365
+ case 1:
366
+ headers.CaddyfileHeaderOp(h.Headers.Response.HeaderOps, args[0], "", "")
367
+ case 2:
368
+ headers.CaddyfileHeaderOp(h.Headers.Response.HeaderOps, args[0], args[1], "")
369
+ case 3:
370
+ headers.CaddyfileHeaderOp(h.Headers.Response.HeaderOps, args[0], args[1], args[2])
371
+ default:
372
+ return d.ArgErr()
373
+ }
374
+
375
case "transport":
376
if !d.NextArg() {
377
return d.ArgErr()