Skip to content

Commit 881667a

Browse files
author
Jonathan Gaillard
committed
Support wildcard for ExposedHeaders option.
Via echoing back all headers in a wrapped response writer since browsers don't currently support the wildcard. Fixes #79
1 parent 33ffc07 commit 881667a

File tree

2 files changed

+151
-29
lines changed

2 files changed

+151
-29
lines changed

cors.go

+56-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ type Options struct {
5454
// Default value is [] but "Origin" is always appended to the list.
5555
AllowedHeaders []string
5656
// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
57-
// API specification
57+
// API specification.
58+
// If the special "*" value is present in the list, all headers will be allowed.
5859
ExposedHeaders []string
5960
// MaxAge indicates how long (in seconds) the results of a preflight request
6061
// can be cached
@@ -194,6 +195,7 @@ func AllowAll() *Cors {
194195
},
195196
AllowedHeaders: []string{"*"},
196197
AllowCredentials: false,
198+
ExposedHeaders: []string{"*"},
197199
})
198200
}
199201

@@ -216,6 +218,7 @@ func (c *Cors) Handler(h http.Handler) http.Handler {
216218
} else {
217219
c.logf("Handler: Actual request")
218220
c.handleActualRequest(w, r)
221+
w = &ExposeAllRespWriter{w, false}
219222
h.ServeHTTP(w, r)
220223
}
221224
})
@@ -249,6 +252,7 @@ func (c *Cors) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.Handl
249252
} else {
250253
c.logf("ServeHTTP: Actual request")
251254
c.handleActualRequest(w, r)
255+
w = &ExposeAllRespWriter{w, false}
252256
next(w, r)
253257
}
254258
}
@@ -427,3 +431,54 @@ func (c *Cors) areHeadersAllowed(requestedHeaders []string) bool {
427431
}
428432
return true
429433
}
434+
435+
// ExposeAllRespWriter echos back any headers that are set in the wrapped response writer
436+
// to support the wildcard "*" case for Access-Control-Expose-Headers since
437+
// browsers do not currently have good compatibility with wildcard.
438+
type ExposeAllRespWriter struct {
439+
http.ResponseWriter
440+
applied bool
441+
}
442+
443+
func (w *ExposeAllRespWriter) Write(b []byte) (int, error) {
444+
w.setHeaders()
445+
return w.ResponseWriter.Write(b)
446+
}
447+
448+
func (w *ExposeAllRespWriter) WriteHeader(c int) {
449+
w.setHeaders()
450+
w.ResponseWriter.WriteHeader(c)
451+
}
452+
453+
func (w *ExposeAllRespWriter) setHeaders() {
454+
if w.applied {
455+
return
456+
}
457+
w.applied = true
458+
459+
if w.ResponseWriter.Header().Get("Access-Control-Expose-Headers") != "*" {
460+
return
461+
}
462+
463+
var toExpose []string
464+
for k := range w.ResponseWriter.Header() {
465+
switch k {
466+
case
467+
// CORs headers that could be set when Access-Control-Expose-Headers is set
468+
"Access-Control-Allow-Origin", "Access-Control-Allow-Credentials", "Access-Control-Expose-Headers",
469+
470+
// already allowed by spec
471+
"Cache-Control", "Content-Language", "Content-Type", "Expires", "Last-Modified", "Pragma":
472+
continue
473+
default:
474+
toExpose = append(toExpose, k)
475+
}
476+
}
477+
478+
if len(toExpose) == 0 {
479+
w.ResponseWriter.Header().Del("Access-Control-Expose-Headers")
480+
return
481+
}
482+
483+
w.ResponseWriter.Header().Set("Access-Control-Expose-Headers", strings.Join(toExpose, ", "))
484+
}

0 commit comments

Comments
 (0)