@marcel91200 / caddy Mirror · read-only / commits / 2fd22139
Fork 0

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 }