fix(19.3-06): cors:<profile> headers on every response of the route (CR-01)
- Router.wrap applies the route's profile outside recovery, body limit and locale, whatever its position in the list or the group nesting - bufferedResponse.commit merges Vary instead of replacing it - TestCORSProfileHeadersOnRefusals, TestCORSProfileVaryKeptWithHandlerVary required by name in the gate
This commit is contained in:
@@ -413,6 +413,13 @@ func (r *Router) wrap(rt route) (http.Handler, error) {
|
||||
return nil, fmt.Errorf("surf: plugin %q: %w", rt.pluginID, err)
|
||||
}
|
||||
h = orgSlot(h)
|
||||
// The route's cors:<profile> (on the route or an enclosing group) is
|
||||
// applied below, outside recovery, the body limit and locale, so every
|
||||
// answer of the route carries it, a guard's refusal included.
|
||||
profile, err := routeCORSProfile(rt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := len(rt.middleware) - 1; i >= 0; i-- {
|
||||
name := rt.middleware[i]
|
||||
if named, ok := r.named[name]; ok {
|
||||
@@ -437,6 +444,8 @@ func (r *Router) wrap(rt route) (http.Handler, error) {
|
||||
if _, ok := r.corsProfiles[param]; !ok {
|
||||
return nil, fmt.Errorf("surf: plugin %q route %s %s references unknown CORS profile %q (define http.cors_profiles.%s)", rt.pluginID, rt.method, rt.path, param, param)
|
||||
}
|
||||
// Never composed at its list position: see below.
|
||||
continue
|
||||
}
|
||||
if base == "turnstile" && param != "open" && param != "closed" {
|
||||
return nil, fmt.Errorf("surf: plugin %q route %s %s: turnstile:%s must be turnstile:open or turnstile:closed", rt.pluginID, rt.method, rt.path, param)
|
||||
@@ -467,6 +476,12 @@ func (r *Router) wrap(rt route) (http.Handler, error) {
|
||||
} else {
|
||||
h = recoverJSON(h)
|
||||
}
|
||||
// The CORS profile wraps recovery, so a refusal from a guard listed
|
||||
// before it, a 413 from the body limit and a recovered 500 all carry
|
||||
// the profile's headers; otherwise the browser hides them from the page.
|
||||
if profile != "" {
|
||||
h = r.corsProfiles[profile].profileMiddleware(h)
|
||||
}
|
||||
// Outermost: name the matched route for the access log (accesslog.go).
|
||||
h = recordPattern(rt.method+" "+rt.path, h)
|
||||
return h, nil
|
||||
@@ -520,7 +535,8 @@ func BuildRouter(app *backpack.App, plugins []party.Plugin) (*Router, error) {
|
||||
}
|
||||
r.corsProfiles = map[string]*corsPolicy{}
|
||||
if err := r.RegisterMiddlewareFactory("surf", "cors", func(param string) pact.Middleware {
|
||||
// wrap refuses an unknown profile before the factory runs.
|
||||
// The name stays reserved for surf, but wrap applies the profile
|
||||
// itself, outermost, and never calls this factory for it.
|
||||
return r.corsProfiles[param].profileMiddleware
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
@@ -793,6 +809,18 @@ func (*bufferedResponse) Flush() {}
|
||||
|
||||
func (w *bufferedResponse) commit(dst http.ResponseWriter) {
|
||||
for key, values := range w.header {
|
||||
if key == "Vary" {
|
||||
// Merged, not replaced: a Vary: Origin a CORS wrapper set on
|
||||
// dst survives a handler that sets its own Vary.
|
||||
for _, line := range values {
|
||||
for _, v := range strings.Split(line, ",") {
|
||||
if v = strings.TrimSpace(v); v != "" {
|
||||
addVary(dst.Header(), v)
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
dst.Header()[key] = append([]string(nil), values...)
|
||||
}
|
||||
status := w.status
|
||||
|
||||
Reference in New Issue
Block a user