From 793a11a5b02a3dded19b557be93d2732c3aad9f0 Mon Sep 17 00:00:00 2001 From: swh Date: Thu, 20 Apr 2017 14:44:54 +0800 Subject: [PATCH] update --- doc.go | 45 ++++++----- sessions.go | 45 ++++++----- sessions_test.go | 197 ----------------------------------------------- store.go | 24 +++--- store_test.go | 73 ------------------ 5 files changed, 61 insertions(+), 323 deletions(-) delete mode 100644 sessions_test.go delete mode 100644 store_test.go diff --git a/doc.go b/doc.go index b03975f..19ab4c7 100644 --- a/doc.go +++ b/doc.go @@ -22,26 +22,26 @@ The key features are: Let's start with an example that shows the sessions API in a nutshell: import ( - "net/http" - "github.com/gorilla/sessions" + "github.com/admpub/sessions" + "github.com/webx-top/echo" ) var store = sessions.NewCookieStore([]byte("something-very-secret")) - func MyHandler(w http.ResponseWriter, r *http.Request) { + func MyHandler(ctx echo.Context) error { // Get a session. We're ignoring the error resulted from decoding an // existing session: Get() always returns a session, even if empty. - session, err := store.Get(r, "session-name") + session, err := store.Get(ctx, "session-name") if err != nil { - http.Error(w, err.Error(), 500) - return + return err } // Set some session values. session.Values["foo"] = "bar" session.Values[42] = 43 // Save it before we write to the response/return from the handler. - session.Save(r, w) + session.Save(ctx) + return nil } First we initialize a session store calling NewCookieStore() and passing a @@ -72,12 +72,11 @@ Ruby On Rails a few years back. When we request a flash message, it is removed from the session. To add a flash, call session.AddFlash(), and to get all flashes, call session.Flashes(). Here is an example: - func MyHandler(w http.ResponseWriter, r *http.Request) { + func MyHandler(ctx echo.Context) error { // Get a session. - session, err := store.Get(r, "session-name") + session, err := store.Get(ctx, "session-name") if err != nil { - http.Error(w, err.Error(), 500) - return + return error } // Get the previously flashes, if any. @@ -87,7 +86,8 @@ flashes, call session.Flashes(). Here is an example: // Set a new flash. session.AddFlash("Hello, flash messages world!") } - session.Save(r, w) + session.Save(ctx) + return nil } Flash messages are useful to set information to be read after a redirection, @@ -99,7 +99,8 @@ so it is easy to register new datatypes for storage in sessions: import( "encoding/gob" - "github.com/gorilla/sessions" + "github.com/admpub/sessions" + "github.com/webx-top/echo" ) type Person struct { @@ -126,11 +127,10 @@ values of those types to and from our sessions. Note that because session values are stored in a map[string]interface{}, there's a need to type-assert data when retrieving it. We'll use the Person struct we registered above: - func MyHandler(w http.ResponseWriter, r *http.Request) { - session, err := store.Get(r, "session-name") + func MyHandler(ctx echo.Context) error { + session, err := store.Get(ctx, "session-name") if err != nil { - http.Error(w, err.Error(), 500) - return + return err } // Retrieve our struct and type-assert it @@ -141,6 +141,8 @@ a need to type-assert data when retrieving it. We'll use the Person struct we re } // Now we can use our person object + + return nil } By default, session cookies last for a month. This is probably too long for @@ -182,15 +184,16 @@ at once: it's sessions.Save(). Here's an example: var store = sessions.NewCookieStore([]byte("something-very-secret")) - func MyHandler(w http.ResponseWriter, r *http.Request) { + func MyHandler(ctx echo.Context) error { // Get a session and set a value. - session1, _ := store.Get(r, "session-one") + session1, _ := store.Get(ctx, "session-one") session1.Values["foo"] = "bar" // Get another session and set another value. - session2, _ := store.Get(r, "session-two") + session2, _ := store.Get(ctx, "session-two") session2.Values[42] = 43 // Save all sessions. - sessions.Save(r, w) + sessions.Save(ctx) + return nil } This is possible because when we call Get() from a session store, it adds the diff --git a/sessions.go b/sessions.go index 38d486a..a4d019d 100644 --- a/sessions.go +++ b/sessions.go @@ -11,7 +11,6 @@ import ( "time" "github.com/webx-top/echo" - "github.com/webx-top/echo/engine" ) // Default flashes key. @@ -92,7 +91,7 @@ func (s *Session) AddFlash(value interface{}, vars ...string) { // store.Save(request, response, session). You should call Save before writing to // the response or returning from the handler. func (s *Session) Save(ctx echo.Context) error { - return s.store.Save(ctx.Request(), ctx.Response(), s) + return s.store.Save(ctx, s) } // Name returns the name used to register the session. @@ -113,30 +112,26 @@ type sessionInfo struct { e error } -// contextKey is the type used to store the registry in the context. -type contextKey int - // registryKey is the key used to store the registry in the context. -const registryKey contextKey = 0 +const registryKey = `webx:mw.sessions` // GetRegistry returns a registry instance for the current request. func GetRegistry(ctx echo.Context) *Registry { - r := ctx.Request() - registry := engine.Get(r, registryKey) - if registry != nil { - return registry.(*Registry) + registry, ok := ctx.Get(registryKey).(*Registry) + if ok { + return registry } - newRegistry := &Registry{ - request: r, + registry = &Registry{ + context: ctx, sessions: make(map[string]sessionInfo), } - engine.Set(r, registryKey, newRegistry) - return newRegistry + ctx.Set(registryKey, registry) + return registry } // Registry stores sessions used during a request. type Registry struct { - request engine.Request + context echo.Context sessions map[string]sessionInfo } @@ -147,7 +142,7 @@ func (s *Registry) Get(store Store, name string) (session *Session, err error) { if info, ok := s.sessions[name]; ok { session, err = info.s, info.e } else { - session, err = store.New(s.request, name) + session, err = store.New(s.context, name) session.name = name s.sessions[name] = sessionInfo{s: session, e: err} } @@ -156,14 +151,14 @@ func (s *Registry) Get(store Store, name string) (session *Session, err error) { } // Save saves all sessions registered for the current request. -func (s *Registry) Save(w engine.Response) error { +func (s *Registry) Save(ctx echo.Context) error { var errMulti MultiError for name, info := range s.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(ctx, session); err != nil { errMulti = append(errMulti, fmt.Errorf( "sessions: error saving session %q -- %v", name, err)) } @@ -182,7 +177,7 @@ func init() { // Save saves all sessions used during the current request. func Save(ctx echo.Context) error { - return GetRegistry(ctx).Save(ctx.Response()) + return GetRegistry(ctx).Save(ctx) } // NewCookie returns an http.Cookie with the options set. It also sets @@ -208,6 +203,18 @@ func NewCookie(name, value string, options *Options) *http.Cookie { return cookie } +// SetCookie for echo +func SetCookie(ctx echo.Context, key string, value string, options *Options) { + ctx.SetCookie( + key, value, + options.MaxAge, + options.Path, + options.Domain, + options.Secure, + options.HttpOnly, + ) +} + // Error ---------------------------------------------------------------------- // MultiError stores multiple errors. diff --git a/sessions_test.go b/sessions_test.go deleted file mode 100644 index fcc69eb..0000000 --- a/sessions_test.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sessions - -import ( - "bytes" - "encoding/gob" - "net/http" - "testing" -) - -// ---------------------------------------------------------------------------- -// ResponseRecorder -// ---------------------------------------------------------------------------- -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// ResponseRecorder is an implementation of http.ResponseWriter that -// records its mutations for later inspection in tests. -type ResponseRecorder struct { - Code int // the HTTP response code from WriteHeader - HeaderMap http.Header // the HTTP response headers - Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to - Flushed bool -} - -// NewRecorder returns an initialized ResponseRecorder. -func NewRecorder() *ResponseRecorder { - return &ResponseRecorder{ - HeaderMap: make(http.Header), - Body: new(bytes.Buffer), - } -} - -// DefaultRemoteAddr is the default remote address to return in RemoteAddr if -// an explicit DefaultRemoteAddr isn't set on ResponseRecorder. -const DefaultRemoteAddr = "1.2.3.4" - -// Header returns the response headers. -func (rw *ResponseRecorder) Header() http.Header { - return rw.HeaderMap -} - -// Write always succeeds and writes to rw.Body, if not nil. -func (rw *ResponseRecorder) Write(buf []byte) (int, error) { - if rw.Body != nil { - rw.Body.Write(buf) - } - if rw.Code == 0 { - rw.Code = http.StatusOK - } - return len(buf), nil -} - -// WriteHeader sets rw.Code. -func (rw *ResponseRecorder) WriteHeader(code int) { - rw.Code = code -} - -// Flush sets rw.Flushed to true. -func (rw *ResponseRecorder) Flush() { - rw.Flushed = true -} - -// ---------------------------------------------------------------------------- - -type FlashMessage struct { - Type int - Message string -} - -func TestFlashes(t *testing.T) { - var req *http.Request - var rsp *ResponseRecorder - var hdr http.Header - var err error - var ok bool - var cookies []string - var session *Session - var flashes []interface{} - - store := NewCookieStore([]byte("secret-key")) - - // Round 1 ---------------------------------------------------------------- - - req, _ = http.NewRequest("GET", "http://localhost:8080/", nil) - rsp = NewRecorder() - // Get a session. - if session, err = store.Get(req, "session-key"); err != nil { - t.Fatalf("Error getting session: %v", err) - } - // Get a flash. - flashes = session.Flashes() - if len(flashes) != 0 { - t.Errorf("Expected empty flashes; Got %v", flashes) - } - // Add some flashes. - session.AddFlash("foo") - session.AddFlash("bar") - // Custom key. - session.AddFlash("baz", "custom_key") - // Save. - if err = Save(req, rsp); err != nil { - t.Fatalf("Error saving session: %v", err) - } - hdr = rsp.Header() - cookies, ok = hdr["Set-Cookie"] - if !ok || len(cookies) != 1 { - t.Fatal("No cookies. Header:", hdr) - } - - // Round 2 ---------------------------------------------------------------- - - req, _ = http.NewRequest("GET", "http://localhost:8080/", nil) - req.Header.Add("Cookie", cookies[0]) - rsp = NewRecorder() - // Get a session. - if session, err = store.Get(req, "session-key"); err != nil { - t.Fatalf("Error getting session: %v", err) - } - // Check all saved values. - flashes = session.Flashes() - if len(flashes) != 2 { - t.Fatalf("Expected flashes; Got %v", flashes) - } - if flashes[0] != "foo" || flashes[1] != "bar" { - t.Errorf("Expected foo,bar; Got %v", flashes) - } - flashes = session.Flashes() - if len(flashes) != 0 { - t.Errorf("Expected dumped flashes; Got %v", flashes) - } - // Custom key. - flashes = session.Flashes("custom_key") - if len(flashes) != 1 { - t.Errorf("Expected flashes; Got %v", flashes) - } else if flashes[0] != "baz" { - t.Errorf("Expected baz; Got %v", flashes) - } - flashes = session.Flashes("custom_key") - if len(flashes) != 0 { - t.Errorf("Expected dumped flashes; Got %v", flashes) - } - - // Round 3 ---------------------------------------------------------------- - // Custom type - - req, _ = http.NewRequest("GET", "http://localhost:8080/", nil) - rsp = NewRecorder() - // Get a session. - if session, err = store.Get(req, "session-key"); err != nil { - t.Fatalf("Error getting session: %v", err) - } - // Get a flash. - flashes = session.Flashes() - if len(flashes) != 0 { - t.Errorf("Expected empty flashes; Got %v", flashes) - } - // Add some flashes. - session.AddFlash(&FlashMessage{42, "foo"}) - // Save. - if err = Save(req, rsp); err != nil { - t.Fatalf("Error saving session: %v", err) - } - hdr = rsp.Header() - cookies, ok = hdr["Set-Cookie"] - if !ok || len(cookies) != 1 { - t.Fatal("No cookies. Header:", hdr) - } - - // Round 4 ---------------------------------------------------------------- - // Custom type - - req, _ = http.NewRequest("GET", "http://localhost:8080/", nil) - req.Header.Add("Cookie", cookies[0]) - rsp = NewRecorder() - // Get a session. - if session, err = store.Get(req, "session-key"); err != nil { - t.Fatalf("Error getting session: %v", err) - } - // Check all saved values. - flashes = session.Flashes() - if len(flashes) != 1 { - t.Fatalf("Expected flashes; Got %v", flashes) - } - custom := flashes[0].(FlashMessage) - if custom.Type != 42 || custom.Message != "foo" { - t.Errorf("Expected %#v, got %#v", FlashMessage{42, "foo"}, custom) - } -} - -func init() { - gob.Register(FlashMessage{}) -} diff --git a/store.go b/store.go index 3940d26..4992482 100644 --- a/store.go +++ b/store.go @@ -14,7 +14,6 @@ import ( "github.com/gorilla/securecookie" "github.com/webx-top/echo" - "github.com/webx-top/echo/engine" ) // Store is an interface for custom session stores. @@ -28,10 +27,10 @@ type Store interface { // // Note that New should never return a nil session, even in the case of // an error if using the Registry infrastructure to cache the session. - New(r engine.Request, name string) (*Session, error) + New(ctx echo.Context, name string) (*Session, error) // Save should persist session to the underlying store implementation. - Save(r engine.Request, w engine.Response, s *Session) error + Save(ctx echo.Context, s *Session) error } // CookieStore ---------------------------------------------------------------- @@ -86,13 +85,13 @@ func (s *CookieStore) Get(ctx echo.Context, name string) (*Session, error) { // The difference between New() and Get() is that calling New() twice will // decode the session data twice, while Get() registers and reuses the same // decoded session after the first call. -func (s *CookieStore) New(r engine.Request, name string) (*Session, error) { +func (s *CookieStore) New(ctx echo.Context, name string) (*Session, error) { session := NewSession(s, name) opts := *s.Options session.Options = &opts session.IsNew = true var err error - if v := r.Cookie(name); v != `` { + if v := ctx.Request().Cookie(name); len(v) > 0 { err = securecookie.DecodeMulti(name, v, &session.Values, s.Codecs...) if err == nil { @@ -103,14 +102,13 @@ func (s *CookieStore) New(r engine.Request, name string) (*Session, error) { } // Save adds a single session to the response. -func (s *CookieStore) Save(r engine.Request, w engine.Response, - session *Session) error { +func (s *CookieStore) Save(ctx echo.Context, session *Session) error { encoded, err := securecookie.EncodeMulti(session.Name(), session.Values, s.Codecs...) if err != nil { return err } - w.SetCookie(NewCookie(session.Name(), encoded, session.Options)) + SetCookie(ctx, session.Name(), encoded, session.Options) return nil } @@ -187,13 +185,13 @@ func (s *FilesystemStore) Get(ctx echo.Context, name string) (*Session, error) { // New returns a session for the given name without adding it to the registry. // // See CookieStore.New(). -func (s *FilesystemStore) New(r engine.Request, name string) (*Session, error) { +func (s *FilesystemStore) New(ctx echo.Context, name string) (*Session, error) { session := NewSession(s, name) opts := *s.Options session.Options = &opts session.IsNew = true var err error - if v := r.Cookie(name); v != `` { + if v := ctx.Request().Cookie(name); len(v) > 0 { err = securecookie.DecodeMulti(name, v, &session.ID, s.Codecs...) if err == nil { err = s.load(session) @@ -206,9 +204,9 @@ func (s *FilesystemStore) New(r engine.Request, name string) (*Session, error) { } // Save adds a single session to the response. -func (s *FilesystemStore) Save(r engine.Request, w engine.Response, +func (s *FilesystemStore) Save(ctx echo.Context, session *Session) error { - if session.ID == "" { + if len(session.ID) == 0 { // Because the ID is used in the filename, encode it to // use alphanumeric characters only. session.ID = strings.TrimRight( @@ -223,7 +221,7 @@ func (s *FilesystemStore) Save(r engine.Request, w engine.Response, if err != nil { return err } - w.SetCookie(NewCookie(session.Name(), encoded, session.Options)) + SetCookie(ctx, session.Name(), encoded, session.Options) return nil } diff --git a/store_test.go b/store_test.go deleted file mode 100644 index 022acba..0000000 --- a/store_test.go +++ /dev/null @@ -1,73 +0,0 @@ -package sessions - -import ( - "encoding/base64" - "net/http" - "net/http/httptest" - "testing" -) - -// Test for GH-8 for CookieStore -func TestGH8CookieStore(t *testing.T) { - originalPath := "/" - store := NewCookieStore() - store.Options.Path = originalPath - req, err := http.NewRequest("GET", "http://www.example.com", nil) - if err != nil { - t.Fatal("failed to create request", err) - } - - session, err := store.New(req, "hello") - if err != nil { - t.Fatal("failed to create session", err) - } - - store.Options.Path = "/foo" - if session.Options.Path != originalPath { - t.Fatalf("bad session path: got %q, want %q", session.Options.Path, originalPath) - } -} - -// Test for GH-8 for FilesystemStore -func TestGH8FilesystemStore(t *testing.T) { - originalPath := "/" - store := NewFilesystemStore("") - store.Options.Path = originalPath - req, err := http.NewRequest("GET", "http://www.example.com", nil) - if err != nil { - t.Fatal("failed to create request", err) - } - - session, err := store.New(req, "hello") - if err != nil { - t.Fatal("failed to create session", err) - } - - store.Options.Path = "/foo" - if session.Options.Path != originalPath { - t.Fatalf("bad session path: got %q, want %q", session.Options.Path, originalPath) - } -} - -// Test for GH-2. -func TestGH2MaxLength(t *testing.T) { - store := NewFilesystemStore("", []byte("some key")) - req, err := http.NewRequest("GET", "http://www.example.com", nil) - if err != nil { - t.Fatal("failed to create request", err) - } - w := httptest.NewRecorder() - - session, err := store.New(req, "my session") - session.Values["big"] = make([]byte, base64.StdEncoding.DecodedLen(4096*2)) - err = session.Save(req, 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) - if err != nil { - t.Fatal("failed to Save:", err) - } -}