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
headers: Ability to mutate request headers including http.Request.Host
Also a few bug fixes
Matthew Holt committed
Sep 14, 2019 at 13:22 UTC
2fd22139c6b7b6055ae53c77d56278fce38d1195
1 file changed
+55
-20
modules/caddyhttp/headers/headers.go
+55
-20
@@ -79,15 +79,8 @@ func (h Handler) Validate() error {
79
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
80
repl := r.Context().Value(caddy.ReplacerCtxKey).(caddy.Replacer)
81
82
- h.Request.applyTo(r.Header, repl)
83
-
84
- // request header's Host is handled specially by the
85
- // Go standard library, so if that header was changed,
86
- // change it in the Host field since the Header won't
87
- // be used
88
- if intendedHost := r.Header.Get("Host"); intendedHost != "" {
89
- r.Host = intendedHost
90
- r.Header.Del("Host")
82
+ if h.Request != nil {
83
+ h.Request.ApplyToRequest(r)
84
}
85
86
if h.Response != nil {
@@ -99,7 +92,7 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhtt
92
headerOps: h.Response.HeaderOps,
93
}
94
} else {
102
- h.Response.applyTo(w.Header(), repl)
95
+ h.Response.ApplyTo(w.Header(), repl)
96
}
97
}
98
@@ -160,11 +153,9 @@ type RespHeaderOps struct {
153
Deferred bool `json:"deferred,omitempty"`
154
}
155
163
-func (ops *HeaderOps) applyTo(hdr http.Header, repl caddy.Replacer) {
164
- if ops == nil {
165
- return
166
- }
167
-
156
+// ApplyTo applies ops to hdr using repl.
157
+func (ops HeaderOps) ApplyTo(hdr http.Header, repl caddy.Replacer) {
158
+ // add
159
for fieldName, vals := range ops.Add {
160
fieldName = repl.ReplaceAll(fieldName, "")
161
for _, v := range vals {
@@ -172,22 +163,28 @@ func (ops *HeaderOps) applyTo(hdr http.Header, repl caddy.Replacer) {
163
}
164
}
165
166
+ // set
167
for fieldName, vals := range ops.Set {
168
fieldName = repl.ReplaceAll(fieldName, "")
169
+ var newVals []string
170
for i := range vals {
178
- vals[i] = repl.ReplaceAll(vals[i], "")
171
+ // append to new slice so we don't overwrite
172
+ // the original values in ops.Set
173
+ newVals = append(newVals, repl.ReplaceAll(vals[i], ""))
174
}
180
- hdr.Set(fieldName, strings.Join(vals, ","))
175
+ hdr.Set(fieldName, strings.Join(newVals, ","))
176
}
177
178
+ // delete
179
for _, fieldName := range ops.Delete {
180
hdr.Del(repl.ReplaceAll(fieldName, ""))
181
}
182
183
+ // replace
184
for fieldName, replacements := range ops.Replace {
185
fieldName = repl.ReplaceAll(fieldName, "")
186
190
- // perform replacements across all fields
187
+ // all fields...
188
if fieldName == "*" {
189
for _, r := range replacements {
190
search := repl.ReplaceAll(r.Search, "")
@@ -205,7 +202,7 @@ func (ops *HeaderOps) applyTo(hdr http.Header, repl caddy.Replacer) {
202
continue
203
}
204
208
- // perform replacements only with the named field
205
+ // ...or only with the named field
206
for _, r := range replacements {
207
search := repl.ReplaceAll(r.Search, "")
208
replace := repl.ReplaceAll(r.Replace, "")
@@ -220,6 +217,42 @@ func (ops *HeaderOps) applyTo(hdr http.Header, repl caddy.Replacer) {
217
}
218
}
219
220
+// ApplyToRequest applies ops to r, specially handling the Host
221
+// header which the standard library does not include with the
222
+// header map with all the others. This method mutates r.Host.
223
+func (ops HeaderOps) ApplyToRequest(r *http.Request) {
224
+ repl := r.Context().Value(caddy.ReplacerCtxKey).(caddy.Replacer)
225
+
226
+ // capture the current Host header so we can
227
+ // reset to it when we're done
228
+ origHost, hadHost := r.Header["Host"]
229
+
230
+ // append r.Host; this way, we know that our value
231
+ // was last in the list, and if an Add operation
232
+ // appended something else after it, that's probably
233
+ // fine because it's weird to have multiple Host
234
+ // headers anyway and presumably the one they added
235
+ // is the one they wanted
236
+ r.Header["Host"] = append(r.Header["Host"], r.Host)
237
+
238
+ // apply header operations
239
+ ops.ApplyTo(r.Header, repl)
240
+
241
+ // retrieve the last Host value (likely the one we appended)
242
+ if len(r.Header["Host"]) > 0 {
243
+ r.Host = r.Header["Host"][len(r.Header["Host"])-1]
244
+ } else {
245
+ r.Host = ""
246
+ }
247
+
248
+ // reset the Host header slice
249
+ if hadHost {
250
+ r.Header["Host"] = origHost
251
+ } else {
252
+ delete(r.Header, "Host")
253
+ }
254
+}
255
+
256
// responseWriterWrapper defers response header
257
// operations until WriteHeader is called.
258
type responseWriterWrapper struct {
@@ -236,7 +269,9 @@ func (rww *responseWriterWrapper) WriteHeader(status int) {
269
}
270
rww.wroteHeader = true
271
if rww.require == nil || rww.require.Match(status, rww.ResponseWriterWrapper.Header()) {
239
- rww.headerOps.applyTo(rww.ResponseWriterWrapper.Header(), rww.replacer)
272
+ if rww.headerOps != nil {
273
+ rww.headerOps.ApplyTo(rww.ResponseWriterWrapper.Header(), rww.replacer)
274
+ }
275
}
276
rww.ResponseWriterWrapper.WriteHeader(status)
277
}