You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

136 lines
3.1 KiB

  1. package handlers
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "time"
  6. "github.com/garyburd/redigo/redis"
  7. )
  8. var (
  9. ErrNoKeyPrefix = errors.New("cannot get session keys without a key prefix")
  10. )
  11. type RedisStoreOptions struct {
  12. Network string
  13. Address string
  14. ConnectTimeout time.Duration
  15. ReadTimeout time.Duration
  16. WriteTimeout time.Duration
  17. Database int // Redis database to use for session keys
  18. KeyPrefix string // If set, keys will be KeyPrefix:SessionID (semicolon added)
  19. BrowserSessServerTTL time.Duration // Defaults to 2 days
  20. }
  21. type RedisStore struct {
  22. opts *RedisStoreOptions
  23. conn redis.Conn
  24. }
  25. // Create a redis session store with the specified options.
  26. func NewRedisStore(opts *RedisStoreOptions) *RedisStore {
  27. var err error
  28. rs := &RedisStore{opts, nil}
  29. rs.conn, err = redis.DialTimeout(opts.Network, opts.Address, opts.ConnectTimeout,
  30. opts.ReadTimeout, opts.WriteTimeout)
  31. if err != nil {
  32. panic(err)
  33. }
  34. return rs
  35. }
  36. // Get the session from the store.
  37. func (this *RedisStore) Get(id string) (*Session, error) {
  38. key := id
  39. if this.opts.KeyPrefix != "" {
  40. key = this.opts.KeyPrefix + ":" + id
  41. }
  42. b, err := redis.Bytes(this.conn.Do("GET", key))
  43. if err != nil {
  44. return nil, err
  45. }
  46. var sess Session
  47. err = json.Unmarshal(b, &sess)
  48. if err != nil {
  49. return nil, err
  50. }
  51. return &sess, nil
  52. }
  53. // Save the session into the store.
  54. func (this *RedisStore) Set(sess *Session) error {
  55. b, err := json.Marshal(sess)
  56. if err != nil {
  57. return err
  58. }
  59. key := sess.ID()
  60. if this.opts.KeyPrefix != "" {
  61. key = this.opts.KeyPrefix + ":" + sess.ID()
  62. }
  63. ttl := sess.MaxAge()
  64. if ttl == 0 {
  65. // Browser session, set to specified TTL
  66. ttl = this.opts.BrowserSessServerTTL
  67. if ttl == 0 {
  68. ttl = 2 * 24 * time.Hour // Default to 2 days
  69. }
  70. }
  71. _, err = this.conn.Do("SETEX", key, int(ttl.Seconds()), b)
  72. if err != nil {
  73. return err
  74. }
  75. return nil
  76. }
  77. // Delete the session from the store.
  78. func (this *RedisStore) Delete(id string) error {
  79. key := id
  80. if this.opts.KeyPrefix != "" {
  81. key = this.opts.KeyPrefix + ":" + id
  82. }
  83. _, err := this.conn.Do("DEL", key)
  84. if err != nil {
  85. return err
  86. }
  87. return nil
  88. }
  89. // Clear all sessions from the store. Requires the use of a key
  90. // prefix in the store options, otherwise the method refuses to delete all keys.
  91. func (this *RedisStore) Clear() error {
  92. vals, err := this.getSessionKeys()
  93. if err != nil {
  94. return err
  95. }
  96. if len(vals) > 0 {
  97. this.conn.Send("MULTI")
  98. for _, v := range vals {
  99. this.conn.Send("DEL", v)
  100. }
  101. _, err = this.conn.Do("EXEC")
  102. if err != nil {
  103. return err
  104. }
  105. }
  106. return nil
  107. }
  108. // Get the number of session keys in the store. Requires the use of a
  109. // key prefix in the store options, otherwise returns -1 (cannot tell
  110. // session keys from other keys).
  111. func (this *RedisStore) Len() int {
  112. vals, err := this.getSessionKeys()
  113. if err != nil {
  114. return -1
  115. }
  116. return len(vals)
  117. }
  118. func (this *RedisStore) getSessionKeys() ([]interface{}, error) {
  119. if this.opts.KeyPrefix != "" {
  120. return redis.Values(this.conn.Do("KEYS", this.opts.KeyPrefix+":*"))
  121. }
  122. return nil, ErrNoKeyPrefix
  123. }