Skip to content

Commit e5b7366

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 e5b7366

File tree

2 files changed

+154
-30
lines changed

2 files changed

+154
-30
lines changed

cors.go

+59-2
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,12 +218,15 @@ 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
})
222225
}
223226

224-
// HandlerFunc provides Martini compatible handler
227+
// HandlerFunc provides Martini compatible handler.
228+
// Since a handler isn't wrapped using this func, considering using
229+
// ExposeAllRespWriter for wildcard support.
225230
func (c *Cors) HandlerFunc(w http.ResponseWriter, r *http.Request) {
226231
if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" {
227232
c.logf("HandlerFunc: Preflight request")
@@ -249,6 +254,7 @@ func (c *Cors) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.Handl
249254
} else {
250255
c.logf("ServeHTTP: Actual request")
251256
c.handleActualRequest(w, r)
257+
w = &ExposeAllRespWriter{w, false}
252258
next(w, r)
253259
}
254260
}
@@ -427,3 +433,54 @@ func (c *Cors) areHeadersAllowed(requestedHeaders []string) bool {
427433
}
428434
return true
429435
}
436+
437+
// ExposeAllRespWriter echos back any headers that are set in the wrapped response writer
438+
// to support the wildcard "*" case for Access-Control-Expose-Headers since
439+
// browsers do not currently have good compatibility.
440+
type ExposeAllRespWriter struct {
441+
http.ResponseWriter
442+
applied bool
443+
}
444+
445+
func (w *ExposeAllRespWriter) Write(b []byte) (int, error) {
446+
w.setHeaders()
447+
return w.ResponseWriter.Write(b)
448+
}
449+
450+
func (w *ExposeAllRespWriter) WriteHeader(c int) {
451+
w.setHeaders()
452+
w.ResponseWriter.WriteHeader(c)
453+
}
454+
455+
func (w *ExposeAllRespWriter) setHeaders() {
456+
if w.applied {
457+
return
458+
}
459+
w.applied = true
460+
461+
if w.ResponseWriter.Header().Get("Access-Control-Expose-Headers") != "*" {
462+
return
463+
}
464+
465+
var toExpose []string
466+
for k := range w.ResponseWriter.Header() {
467+
switch k {
468+
case
469+
// CORs headers that could be set when Access-Control-Expose-Headers is set
470+
"Access-Control-Allow-Origin", "Access-Control-Allow-Credentials", "Access-Control-Expose-Headers",
471+
472+
// already allowed by spec
473+
"Cache-Control", "Content-Language", "Content-Type", "Expires", "Last-Modified", "Pragma":
474+
continue
475+
default:
476+
toExpose = append(toExpose, k)
477+
}
478+
}
479+
480+
if len(toExpose) == 0 {
481+
w.ResponseWriter.Header().Del("Access-Control-Expose-Headers")
482+
return
483+
}
484+
485+
w.ResponseWriter.Header().Set("Access-Control-Expose-Headers", strings.Join(toExpose, ", "))
486+
}

0 commit comments

Comments
 (0)