summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc.go45
-rw-r--r--sessions.go45
-rw-r--r--sessions_test.go197
-rw-r--r--store.go24
-rw-r--r--store_test.go73
5 files changed, 61 insertions, 323 deletions
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)
- }
-}