diff --git a/README.md b/README.md index 10eb7f0..30aa6a8 100644 --- a/README.md +++ b/README.md @@ -48,18 +48,6 @@ secret key used to authenticate the session. Inside the handler, we call some session values in session.Values, which is a `map[interface{}]interface{}`. And finally we call `session.Save()` to save the session in the response. -Important Note: If you aren't using gorilla/mux, you need to wrap your handlers -with -[`context.ClearHandler`](http://www.gorillatoolkit.org/pkg/context#ClearHandler) -or else you will leak memory! An easy way to do this is to wrap the top-level -mux when calling http.ListenAndServe: - -```go - http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux)) -``` - -The ClearHandler function is provided by the gorilla/context package. - More examples are available [on the Gorilla website](http://www.gorillatoolkit.org/pkg/sessions). diff --git a/doc.go b/doc.go index 591d932..cf5ddc2 100644 --- a/doc.go +++ b/doc.go @@ -55,14 +55,6 @@ session.Save(r, w), and either display an error message or otherwise handle it. Save must be called before writing to the response, otherwise the session cookie will not be sent to the client. -Important Note: If you aren't using gorilla/mux, you need to wrap your handlers -with context.ClearHandler as or else you will leak memory! An easy way to do this -is to wrap the top-level mux when calling http.ListenAndServe: - - http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux)) - -The ClearHandler function is provided by the gorilla/context package. - That's all you need to know for the basic usage. Let's take a look at other options, starting with flash messages. diff --git a/sessions.go b/sessions.go index fe0d2bc..e668866 100644 --- a/sessions.go +++ b/sessions.go @@ -5,12 +5,11 @@ package sessions import ( + "context" "encoding/gob" "fmt" "net/http" "time" - - "github.com/gorilla/context" ) // Default flashes key. @@ -93,8 +92,8 @@ func (s *Session) AddFlash(value interface{}, vars ...string) { // Save is a convenience method to save this session. It is the same as calling // store.Save(request, response, session). You should call Save before writing to // the response or returning from the handler. -func (s *Session) Save(r *http.Request, w http.ResponseWriter) error { - return s.store.Save(r, w, s) +func (s *Session) Save(w http.ResponseWriter) error { + return s.store.Save(w, s) } // Name returns the name used to register the session. @@ -122,52 +121,52 @@ type contextKey int const registryKey contextKey = 0 // GetRegistry returns a registry instance for the current request. +// If there is no registry in the context, create a new Registry and +// add it to the request's context. func GetRegistry(r *http.Request) *Registry { - registry := context.Get(r, registryKey) + c := r.Context() + registry, _ := c.Value(registryKey).(*Registry) if registry != nil { - return registry.(*Registry) + return registry } newRegistry := &Registry{ - request: r, sessions: make(map[string]sessionInfo), } - context.Set(r, registryKey, newRegistry) + *r = *(r.WithContext(context.WithValue(c, registryKey, newRegistry))) return newRegistry } // Registry stores sessions used during a request. type Registry struct { - request *http.Request sessions map[string]sessionInfo } // Get registers and returns a session for the given name and session store. // // It returns a new session if there are no sessions registered for the name. -func (s *Registry) Get(store Store, name string) (session *Session, err error) { +func (registry *Registry) Get(r *http.Request, store Store, name string) (session *Session, err error) { if !isCookieNameValid(name) { return nil, fmt.Errorf("sessions: invalid character in cookie name: %s", name) } - if info, ok := s.sessions[name]; ok { + if info, ok := registry.sessions[name]; ok { session, err = info.s, info.e } else { - session, err = store.New(s.request, name) - session.name = name - s.sessions[name] = sessionInfo{s: session, e: err} + session, err = store.New(r, name) + registry.sessions[name] = sessionInfo{s: session, e: err} } session.store = store return } // Save saves all sessions registered for the current request. -func (s *Registry) Save(w http.ResponseWriter) error { +func (registry *Registry) Save(w http.ResponseWriter) error { var errMulti MultiError - for name, info := range s.sessions { + for name, info := range registry.sessions { session := info.s if session.store == nil { errMulti = append(errMulti, fmt.Errorf( "sessions: missing store for session %q", name)) - } else if err := session.store.Save(s.request, w, session); err != nil { + } else if err := session.store.Save(w, session); err != nil { errMulti = append(errMulti, fmt.Errorf( "sessions: error saving session %q -- %v", name, err)) } diff --git a/store.go b/store.go index 4ff6b6c..4b326eb 100644 --- a/store.go +++ b/store.go @@ -30,7 +30,7 @@ type Store interface { New(r *http.Request, name string) (*Session, error) // Save should persist session to the underlying store implementation. - Save(r *http.Request, w http.ResponseWriter, s *Session) error + Save(w http.ResponseWriter, s *Session) error } // CookieStore ---------------------------------------------------------------- @@ -77,7 +77,7 @@ type CookieStore struct { // It returns a new session and an error if the session exists but could // not be decoded. func (s *CookieStore) Get(r *http.Request, name string) (*Session, error) { - return GetRegistry(r).Get(s, name) + return GetRegistry(r).Get(r, s, name) } // New returns a session for the given name without adding it to the registry. @@ -102,8 +102,7 @@ func (s *CookieStore) New(r *http.Request, name string) (*Session, error) { } // Save adds a single session to the response. -func (s *CookieStore) Save(r *http.Request, w http.ResponseWriter, - session *Session) error { +func (s *CookieStore) Save(w http.ResponseWriter, session *Session) error { encoded, err := securecookie.EncodeMulti(session.Name(), session.Values, s.Codecs...) if err != nil { @@ -180,7 +179,7 @@ func (s *FilesystemStore) MaxLength(l int) { // // See CookieStore.Get(). func (s *FilesystemStore) Get(r *http.Request, name string) (*Session, error) { - return GetRegistry(r).Get(s, name) + return GetRegistry(r).Get(r, s, name) } // New returns a session for the given name without adding it to the registry. @@ -210,8 +209,7 @@ func (s *FilesystemStore) New(r *http.Request, name string) (*Session, error) { // deleted from the store path. With this process it enforces the properly // session cookie handling so no need to trust in the cookie management in the // web browser. -func (s *FilesystemStore) Save(r *http.Request, w http.ResponseWriter, - session *Session) error { +func (s *FilesystemStore) Save(w http.ResponseWriter, session *Session) error { // Delete if max-age is <= 0 if session.Options.MaxAge <= 0 { if err := s.erase(session); err != nil { diff --git a/store_test.go b/store_test.go index 3561e5d..396ca1c 100644 --- a/store_test.go +++ b/store_test.go @@ -64,13 +64,13 @@ func TestGH2MaxLength(t *testing.T) { } session.Values["big"] = make([]byte, base64.StdEncoding.DecodedLen(4096*2)) - err = session.Save(req, w) + err = session.Save(w) if err == nil { t.Fatal("expected an error, got nil") } store.MaxLength(4096 * 3) // A bit more than the value size to account for encoding overhead. - err = session.Save(req, w) + err = session.Save(w) if err != nil { t.Fatal("failed to Save:", err) } @@ -90,13 +90,13 @@ func TestGH8FilesystemStoreDelete(t *testing.T) { t.Fatal("failed to create session", err) } - err = session.Save(req, w) + err = session.Save(w) if err != nil { t.Fatal("failed to save session", err) } session.Options.MaxAge = -1 - err = session.Save(req, w) + err = session.Save(w) if err != nil { t.Fatal("failed to delete session", err) } @@ -116,13 +116,13 @@ func TestGH8FilesystemStoreDelete2(t *testing.T) { t.Fatal("failed to create session", err) } - err = session.Save(req, w) + err = session.Save(w) if err != nil { t.Fatal("failed to save session", err) } session.Options.MaxAge = 0 - err = session.Save(req, w) + err = session.Save(w) if err != nil { t.Fatal("failed to delete session", err) }