@@ -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.
225230func (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