more platform stuff
This commit is contained in:
1 parent
4b9a882018
commit
fc5eefbe5e
7 files changed
+756
No files matched your search
@@ -0,0 +1,101 @@
|
||||
package httpclient
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type corsConfig struct {
|
||||
origin string // "" = mirror request Origin; "*" = wildcard (no credentials)
|
||||
methods string
|
||||
headers string
|
||||
exposeHeaders string
|
||||
}
|
||||
|
||||
// CORSOption customises the CORS middleware.
|
||||
type CORSOption func(*corsConfig)
|
||||
|
||||
// WithOrigin sets the Access-Control-Allow-Origin value. Use "*" for public
|
||||
// APIs that don't need credentials. The default (empty string) mirrors the
|
||||
// request's Origin header and enables credentials.
|
||||
func WithOrigin(origin string) CORSOption {
|
||||
return func(c *corsConfig) { c.origin = origin }
|
||||
}
|
||||
|
||||
// WithMethods sets Access-Control-Allow-Methods. Default: "GET, POST, PUT, OPTIONS".
|
||||
func WithMethods(methods string) CORSOption {
|
||||
return func(c *corsConfig) { c.methods = methods }
|
||||
}
|
||||
|
||||
// WithHeaders sets Access-Control-Allow-Headers. Default: "*".
|
||||
func WithHeaders(headers string) CORSOption {
|
||||
return func(c *corsConfig) { c.headers = headers }
|
||||
}
|
||||
|
||||
// WithExposeHeaders sets Access-Control-Expose-Headers. Default: empty.
|
||||
func WithExposeHeaders(headers string) CORSOption {
|
||||
return func(c *corsConfig) { c.exposeHeaders = headers }
|
||||
}
|
||||
|
||||
// CORS returns middleware that sets CORS response headers and handles
|
||||
// preflight (OPTIONS) requests. By default it mirrors the request Origin,
|
||||
// enables credentials, and allows GET/POST/PUT/OPTIONS with any request
|
||||
// headers.
|
||||
//
|
||||
// Use WithOrigin("*") for public APIs that don't need credentials (per the
|
||||
// CORS spec, credentials and wildcard origin are mutually exclusive).
|
||||
//
|
||||
// Common patterns:
|
||||
//
|
||||
// // Public API (wildcard origin, no credentials):
|
||||
// mux := http.NewServeMux()
|
||||
// handler := httpclient.CORS(
|
||||
// httpclient.WithOrigin("*"),
|
||||
// httpclient.WithMethods("GET, POST, OPTIONS"),
|
||||
// )(mux)
|
||||
//
|
||||
// // Authenticated API (origin reflection, credentials):
|
||||
// handler := httpclient.CORS()(mux)
|
||||
func CORS(opts ...CORSOption) func(http.Handler) http.Handler {
|
||||
cfg := corsConfig{
|
||||
methods: "GET, POST, PUT, OPTIONS",
|
||||
headers: "*",
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(&cfg)
|
||||
}
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
setCORSHeaders(w, r, cfg)
|
||||
if r.Method == http.MethodOptions {
|
||||
w.Header().Set("Access-Control-Max-Age", "86400")
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setCORSHeaders(w http.ResponseWriter, r *http.Request, cfg corsConfig) {
|
||||
origin := cfg.origin
|
||||
if origin == "" {
|
||||
origin = r.Header.Get("Origin")
|
||||
}
|
||||
if origin == "" {
|
||||
return
|
||||
}
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
// Per spec, credentials can't be used with wildcard origin.
|
||||
if origin != "*" {
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
}
|
||||
if cfg.exposeHeaders != "" {
|
||||
w.Header().Set("Access-Control-Expose-Headers", cfg.exposeHeaders)
|
||||
}
|
||||
if cfg.methods != "" {
|
||||
w.Header().Set("Access-Control-Allow-Methods", cfg.methods)
|
||||
}
|
||||
if cfg.headers != "" {
|
||||
w.Header().Set("Access-Control-Allow-Headers", cfg.headers)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user