alexedwards / scs

HTTP Session Management for Go
MIT License
2.13k stars 166 forks source link

Use Redis to store idle timeout issues #22

Closed dxvgef closed 7 years ago

dxvgef commented 7 years ago

This is my code snippet, I use Redis storage, and define the IdleTimeout value is 20 minutes and the Lifetime value is 2 hours, but the session data is always in 20 minutes of failure, the page refreshes does not seem to automatically extend the idle timeout time?

SessionManager = scs.NewManager(
    redisstore.New(&redis.Pool{
        MaxIdle: 10,
        Dial: func() (redis.Conn, error) {
            return redis.Dial("tcp", "127.0.0.1:6379", redis.DialDatabase(0))
        },
        IdleTimeout: time.Duration(20) * time.Minute,
    }),
)
SessionManager.IdleTimeout(20 * time.Minute)
SessionManager.Lifetime(time.Duration(2 * time.Hour)
alexedwards commented 7 years ago

By default the idle timeout is updated whenever the session is modified (not when it's only read). So a page refresh alone wouldn't trigger the idle timeout to be updated.

I've added a new *Manager.Use() middleware, which checks whether there an idle timeout is set on the session manager, and if there is, will trigger the idle timeout update each time the session is read. If you wrap your handlers with that it should do what you need it too.

var sessionManager *scs.Manager

func main() {

    sessionManager = scs.NewManager(redisstore.New(&redis.Pool{
        MaxIdle: 10,
        Dial: func() (redis.Conn, error) {
            return redis.Dial("tcp", "127.0.0.1:6379", redis.DialDatabase(0))
        },
        IdleTimeout: time.Duration(20 * time.Minute),
    }))
    sessionManager.IdleTimeout(20 * time.Minute)
    sessionManager.Lifetime(time.Duration(2 * time.Hour))

    mux := http.NewServeMux()
    mux.HandleFunc("/put", putHandler)
    mux.HandleFunc("/get", getHandler)

    http.ListenAndServe(":4000", sessionManager.Use(mux))
}

Thanks

dxvgef commented 7 years ago

But I'm using the echo framework. How can I solve this problem?

alexedwards commented 7 years ago

I'm not hugely familiar with Echo but the code below seems to work for me:

package main

import (
    "net/http"
    "time"

    "github.com/alexedwards/scs"
    "github.com/alexedwards/scs/stores/redisstore"
    "github.com/garyburd/redigo/redis"
    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
)

var sessionManager *scs.Manager

func main() {
    sessionManager = scs.NewManager(redisstore.New(&redis.Pool{
        MaxIdle: 10,
        Dial: func() (redis.Conn, error) {
            return redis.Dial("tcp", "127.0.0.1:6379", redis.DialDatabase(0))
        },
        IdleTimeout: time.Duration(20 * time.Minute),
    }))
    sessionManager.IdleTimeout(20 * time.Minute)
    sessionManager.Lifetime(time.Duration(2 * time.Hour))

    e := echo.New()
    e.Use(echo.WrapMiddleware(sessionManager.Use))

    e.GET("/put", func(c echo.Context) error {
        session := sessionManager.Load(c.Request())
        err := session.PutString(c.Response().Writer, "message", "Hello world!")
        if err != nil {
            http.Error(c.Response().Writer, err.Error(), 500)
        }
        return c.String(http.StatusOK, "put")
    })

    e.GET("/get", func(c echo.Context) error {
        session := sessionManager.Load(c.Request())
        msg, err := session.GetString("message")
        if err != nil {
            http.Error(c.Response().Writer, err.Error(), 500)
        }
        return c.String(http.StatusOK, "result "+msg)
    })

    e.Start(":4000")
}
dxvgef commented 7 years ago

Thank you!

jpfluger commented 6 years ago

I think it's worth mentioning that redis.Pool.IdleTimeout has a different meaning than session.IdleTimeout. The redis client docs say that IdleTimeout has to do with stale client connections from redis.Pool to the redis.Server. It has nothing to do with invalidating the record within redis. The redis EXPIRE directive sets the timeout on the key.

The following is sufficient for controlling stale sessions within a web request:

SessionManager.IdleTimeout(20 * time.Minute)
SessionManager.Lifetime(time.Duration(2 * time.Hour)

Then use it in the middleware so that at least Touch() is called which updates the deadline property.