@@ -54,7 +54,8 @@ type Options struct {
54
54
// Default value is [] but "Origin" is always appended to the list.
55
55
AllowedHeaders []string
56
56
// 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.
58
59
ExposedHeaders []string
59
60
// MaxAge indicates how long (in seconds) the results of a preflight request
60
61
// can be cached
@@ -194,6 +195,7 @@ func AllowAll() *Cors {
194
195
},
195
196
AllowedHeaders : []string {"*" },
196
197
AllowCredentials : false ,
198
+ ExposedHeaders : []string {"*" },
197
199
})
198
200
}
199
201
@@ -216,12 +218,15 @@ func (c *Cors) Handler(h http.Handler) http.Handler {
216
218
} else {
217
219
c .logf ("Handler: Actual request" )
218
220
c .handleActualRequest (w , r )
221
+ w = & ExposeAllRespWriter {w , false }
219
222
h .ServeHTTP (w , r )
220
223
}
221
224
})
222
225
}
223
226
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.
225
230
func (c * Cors ) HandlerFunc (w http.ResponseWriter , r * http.Request ) {
226
231
if r .Method == http .MethodOptions && r .Header .Get ("Access-Control-Request-Method" ) != "" {
227
232
c .logf ("HandlerFunc: Preflight request" )
@@ -249,6 +254,7 @@ func (c *Cors) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.Handl
249
254
} else {
250
255
c .logf ("ServeHTTP: Actual request" )
251
256
c .handleActualRequest (w , r )
257
+ w = & ExposeAllRespWriter {w , false }
252
258
next (w , r )
253
259
}
254
260
}
@@ -427,3 +433,54 @@ func (c *Cors) areHeadersAllowed(requestedHeaders []string) bool {
427
433
}
428
434
return true
429
435
}
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