From 9e5ef71c04a299088512e5686fb35608307c4658 Mon Sep 17 00:00:00 2001 From: Carl Jackson Date: Sun, 23 Mar 2014 03:31:46 -0700 Subject: [PATCH] Automatic OPTIONS middleware --- default.go | 1 + web/middleware/options.go | 63 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 web/middleware/options.go diff --git a/default.go b/default.go index fca2a27..6ec68ed 100644 --- a/default.go +++ b/default.go @@ -14,6 +14,7 @@ func init() { DefaultMux.Use("RequestId", middleware.RequestId) DefaultMux.Use("Logger", middleware.Logger) DefaultMux.Use("Recoverer", middleware.Recoverer) + DefaultMux.Use("AutomaticOptions", middleware.AutomaticOptions) } // Append the given middleware to the default Mux's middleware stack. See the diff --git a/web/middleware/options.go b/web/middleware/options.go new file mode 100644 index 0000000..ffa969b --- /dev/null +++ b/web/middleware/options.go @@ -0,0 +1,63 @@ +package middleware + +import ( + "io" + "net/http" + "net/http/httptest" + "strings" + + "github.com/zenazn/goji/web" +) + +// Automatically return an appropriate "Allow" header when the request method is +// OPTIONS and the request would have otherwise been 404'd. +func AutomaticOptions(c *web.C, h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + // This will probably slow down OPTIONS calls a bunch, but it + // probably won't happen too much, and it'll just be hitting the + // 404 route anyways. + var fw *httptest.ResponseRecorder + pw := w + if strings.ToUpper(r.Method) == "OPTIONS" { + fw = httptest.NewRecorder() + pw = fw + } + + h.ServeHTTP(pw, r) + + if fw == nil { + return + } + + for k, v := range fw.Header() { + w.Header()[k] = v + } + + methods := getValidMethods(*c) + + if fw.Code == http.StatusNotFound && methods != nil { + w.Header().Set("Allow", strings.Join(methods, ", ")) + w.WriteHeader(http.StatusOK) + } else { + w.WriteHeader(fw.Code) + io.Copy(w, fw.Body) + } + } + + return http.HandlerFunc(fn) +} + +func getValidMethods(c web.C) []string { + if c.Env == nil { + return nil + } + v, ok := c.Env["goji.web.validMethods"] + if !ok { + return nil + } + if methods, ok := v.([]string); ok { + return methods + } else { + return nil + } +}