Skip to content

Commit 4327f7e

Browse files
found-cakesrbry
andauthored
feat: different stores for many sessions #144 (#282)
fixed #124 fixed #144 * Add option to use multiple sessions with different stores * Change `http.NewRequest` to `http.NewRequestWithContext` Follow-up to #830a1b9: Replace `http.NewRequest` with `http.NewRequestWithContext` in multiple test functions in `tester.go` and `tester_options_samesite_go1.11.go` Signed-off-by: found-cake <me@foundcake.kr> * fix typo Signed-off-by: found-cake <me@foundcake.kr> --------- Signed-off-by: found-cake <me@foundcake.kr> Co-authored-by: Sam Bryant <srbry@hotmail.com>
1 parent 12aa9f4 commit 4327f7e

9 files changed

Lines changed: 147 additions & 0 deletions

File tree

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,56 @@ func main() {
104104
}
105105
```
106106

107+
### multiple sessions with different stores
108+
109+
```go
110+
package main
111+
112+
import (
113+
"github.com/gin-contrib/sessions"
114+
"github.com/gin-contrib/sessions/cookie"
115+
"github.com/gin-gonic/gin"
116+
)
117+
118+
func main() {
119+
r := gin.Default()
120+
cookieStore := cookie.NewStore([]byte("secret"))
121+
redisStore, _ := redis.NewStore(10, "tcp", "localhost:6379", "", []byte("secret"))
122+
sessionStores := []sessions.SessionStore{
123+
{
124+
Name: "a",
125+
Store: cookieStore,
126+
},
127+
{
128+
Name: "b",
129+
Store: redisStore,
130+
},
131+
}
132+
r.Use(sessions.SessionsManyStores(sessionStores))
133+
134+
r.GET("/hello", func(c *gin.Context) {
135+
sessionA := sessions.DefaultMany(c, "a")
136+
sessionB := sessions.DefaultMany(c, "b")
137+
138+
if sessionA.Get("hello") != "world!" {
139+
sessionA.Set("hello", "world!")
140+
sessionA.Save()
141+
}
142+
143+
if sessionB.Get("hello") != "world?" {
144+
sessionB.Set("hello", "world?")
145+
sessionB.Save()
146+
}
147+
148+
c.JSON(200, gin.H{
149+
"a": sessionA.Get("hello"),
150+
"b": sessionB.Get("hello"),
151+
})
152+
})
153+
r.Run(":8000")
154+
}
155+
```
156+
107157
## Backend Examples
108158

109159
### cookie-based

cookie/cookie_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,7 @@ func TestCookie_SessionOptions(t *testing.T) {
3535
func TestCookie_SessionMany(t *testing.T) {
3636
tester.Many(t, newStore)
3737
}
38+
39+
func TestCookie_SessionManyStores(t *testing.T) {
40+
tester.ManyStores(t, newStore)
41+
}

memcached/memcached_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ func TestMemcached_SessionMany(t *testing.T) {
4141
tester.Many(t, newStore)
4242
}
4343

44+
func TestMemcached_SessionManyStores(t *testing.T) {
45+
tester.ManyStores(t, newStore)
46+
}
47+
4448
var newBinaryStore = func(_ *testing.T) sessions.Store {
4549
store := NewMemcacheStore(
4650
mc.NewMC(memcachedTestServer, "", ""), "", []byte("secret"))
@@ -70,3 +74,7 @@ func TestBinaryMemcached_SessionOptions(t *testing.T) {
7074
func TestBinaryMemcached_SessionMany(t *testing.T) {
7175
tester.Many(t, newBinaryStore)
7276
}
77+
78+
func TestBinaryMemcached_SessionManyStores(t *testing.T) {
79+
tester.ManyStores(t, newBinaryStore)
80+
}

memstore/memstore_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,7 @@ func TestCookie_SessionOptions(t *testing.T) {
3535
func TestCookie_SessionMany(t *testing.T) {
3636
tester.Many(t, newStore)
3737
}
38+
39+
func TestCookie_SessionManyStores(t *testing.T) {
40+
tester.ManyStores(t, newStore)
41+
}

mongo/mongomgo/mongomgo_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,7 @@ func TestMongoMGO_SessionOptions(t *testing.T) {
4343
func TestMongoMGO_SessionMany(t *testing.T) {
4444
tester.Many(t, newStore)
4545
}
46+
47+
func TestMongo_SessionManyStores(t *testing.T) {
48+
tester.ManyStores(t, newStore)
49+
}

postgres/postgres_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,7 @@ func TestPostgres_SessionOptions(t *testing.T) {
4747
func TestPostgres_SessionMany(t *testing.T) {
4848
tester.Many(t, newStore)
4949
}
50+
51+
func TestPostgres_SessionManyStores(t *testing.T) {
52+
tester.ManyStores(t, newStore)
53+
}

redis/redis_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ func TestRedis_SessionMany(t *testing.T) {
4141
tester.Many(t, newRedisStore)
4242
}
4343

44+
func TestRedis_SessionManyStores(t *testing.T) {
45+
tester.ManyStores(t, newRedisStore)
46+
}
47+
4448
func TestGetRedisStore(t *testing.T) {
4549
t.Run("unmatched type", func(t *testing.T) {
4650
type store struct{ Store }

sessions.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ type Session interface {
4646
Save() error
4747
}
4848

49+
// SessionStore named session stores allow multiple sessions with different store types
50+
type SessionStore struct {
51+
Name string
52+
Store Store
53+
}
54+
4955
func Sessions(name string, store Store) gin.HandlerFunc {
5056
return func(c *gin.Context) {
5157
s := &session{name, c.Request, store, nil, false, c.Writer}
@@ -67,6 +73,18 @@ func SessionsMany(names []string, store Store) gin.HandlerFunc {
6773
}
6874
}
6975

76+
func SessionsManyStores(sessionStores []SessionStore) gin.HandlerFunc {
77+
return func(c *gin.Context) {
78+
sessions := make(map[string]Session, len(sessionStores))
79+
for _, sessionStore := range sessionStores {
80+
sessions[sessionStore.Name] = &session{sessionStore.Name, c.Request, sessionStore.Store, nil, false, c.Writer}
81+
}
82+
c.Set(DefaultKey, sessions)
83+
defer context.Clear(c.Request)
84+
c.Next()
85+
}
86+
}
87+
7088
type session struct {
7189
name string
7290
request *http.Request

tester/tester.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,57 @@ func Many(t *testing.T, newStore storeFactory) {
316316
r.ServeHTTP(res2, req2)
317317
}
318318

319+
func ManyStores(t *testing.T, newStore storeFactory) {
320+
r := gin.Default()
321+
322+
store := newStore(t)
323+
sessionStores := []sessions.SessionStore{
324+
{Name: "a", Store: store},
325+
{Name: "b", Store: store},
326+
}
327+
328+
r.Use(sessions.SessionsManyStores(sessionStores))
329+
330+
r.GET("/set", func(c *gin.Context) {
331+
sessionA := sessions.DefaultMany(c, "a")
332+
sessionA.Set("hello", "world")
333+
_ = sessionA.Save()
334+
335+
sessionB := sessions.DefaultMany(c, "b")
336+
sessionB.Set("foo", "bar")
337+
_ = sessionB.Save()
338+
c.String(http.StatusOK, ok)
339+
})
340+
341+
r.GET("/get", func(c *gin.Context) {
342+
sessionA := sessions.DefaultMany(c, "a")
343+
if sessionA.Get("hello") != "world" {
344+
t.Error("Session writing failed")
345+
}
346+
_ = sessionA.Save()
347+
348+
sessionB := sessions.DefaultMany(c, "b")
349+
if sessionB.Get("foo") != "bar" {
350+
t.Error("Session writing failed")
351+
}
352+
_ = sessionB.Save()
353+
c.String(http.StatusOK, ok)
354+
})
355+
356+
res1 := httptest.NewRecorder()
357+
req1, _ := http.NewRequestWithContext(context.Background(), "GET", "/set", nil)
358+
r.ServeHTTP(res1, req1)
359+
360+
res2 := httptest.NewRecorder()
361+
req2, _ := http.NewRequestWithContext(context.Background(), "GET", "/get", nil)
362+
header := ""
363+
for _, x := range res1.Header()["Set-Cookie"] {
364+
header += strings.Split(x, ";")[0] + "; \n"
365+
}
366+
req2.Header.Set("Cookie", header)
367+
r.ServeHTTP(res2, req2)
368+
}
369+
319370
func copyCookies(req *http.Request, res *httptest.ResponseRecorder) {
320371
req.Header.Set("Cookie", strings.Join(res.Header().Values("Set-Cookie"), "; "))
321372
}

0 commit comments

Comments
 (0)