sessions.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. // Package sessions contains middleware for easy session management in Martini.
  2. //
  3. // package main
  4. //
  5. // import (
  6. // "github.com/go-martini/martini"
  7. // "github.com/martini-contrib/sessions"
  8. // )
  9. //
  10. // func main() {
  11. // m := martini.Classic()
  12. //
  13. // store := sessions.NewCookieStore([]byte("secret123"))
  14. // m.Use(sessions.Sessions("my_session", store))
  15. //
  16. // m.Get("/", func(session sessions.Session) string {
  17. // session.Set("hello", "world")
  18. // })
  19. // }
  20. package sessions
  21. import (
  22. "github.com/go-martini/martini"
  23. "github.com/gorilla/context"
  24. "github.com/gorilla/sessions"
  25. "log"
  26. "net/http"
  27. )
  28. const (
  29. errorFormat = "[sessions] ERROR! %s\n"
  30. )
  31. // Store is an interface for custom session stores.
  32. type Store interface {
  33. sessions.Store
  34. }
  35. // Options stores configuration for a session or session store.
  36. //
  37. // Fields are a subset of http.Cookie fields.
  38. type Options struct {
  39. Path string
  40. Domain string
  41. // MaxAge=0 means no 'Max-Age' attribute specified.
  42. // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'.
  43. // MaxAge>0 means Max-Age attribute present and given in seconds.
  44. MaxAge int
  45. Secure bool
  46. HttpOnly bool
  47. }
  48. // Session stores the values and optional configuration for a session.
  49. type Session interface {
  50. // Get returns the session value associated to the given key.
  51. Get(key interface{}) interface{}
  52. // Set sets the session value associated to the given key.
  53. Set(key interface{}, val interface{})
  54. // Delete removes the session value associated to the given key.
  55. Delete(key interface{})
  56. // Clear deletes all values in the session.
  57. Clear()
  58. // AddFlash adds a flash message to the session.
  59. // A single variadic argument is accepted, and it is optional: it defines the flash key.
  60. // If not defined "_flash" is used by default.
  61. AddFlash(value interface{}, vars ...string)
  62. // Flashes returns a slice of flash messages from the session.
  63. // A single variadic argument is accepted, and it is optional: it defines the flash key.
  64. // If not defined "_flash" is used by default.
  65. Flashes(vars ...string) []interface{}
  66. // Options sets confuguration for a session.
  67. Options(Options)
  68. }
  69. // Sessions is a Middleware that maps a session.Session service into the Martini handler chain.
  70. // Sessions can use a number of storage solutions with the given store.
  71. func Sessions(name string, store Store) martini.Handler {
  72. return func(res http.ResponseWriter, r *http.Request, c martini.Context, l *log.Logger) {
  73. // Map to the Session interface
  74. s := &session{name, r, l, store, nil, false}
  75. c.MapTo(s, (*Session)(nil))
  76. // Use before hook to save out the session
  77. rw := res.(martini.ResponseWriter)
  78. rw.Before(func(martini.ResponseWriter) {
  79. if s.Written() {
  80. check(s.Session().Save(r, res), l)
  81. }
  82. })
  83. // clear the context, we don't need to use
  84. // gorilla context and we don't want memory leaks
  85. defer context.Clear(r)
  86. c.Next()
  87. }
  88. }
  89. type session struct {
  90. name string
  91. request *http.Request
  92. logger *log.Logger
  93. store Store
  94. session *sessions.Session
  95. written bool
  96. }
  97. func (s *session) Get(key interface{}) interface{} {
  98. return s.Session().Values[key]
  99. }
  100. func (s *session) Set(key interface{}, val interface{}) {
  101. s.Session().Values[key] = val
  102. s.written = true
  103. }
  104. func (s *session) Delete(key interface{}) {
  105. delete(s.Session().Values, key)
  106. s.written = true
  107. }
  108. func (s *session) Clear() {
  109. for key := range s.Session().Values {
  110. s.Delete(key)
  111. }
  112. }
  113. func (s *session) AddFlash(value interface{}, vars ...string) {
  114. s.Session().AddFlash(value, vars...)
  115. s.written = true
  116. }
  117. func (s *session) Flashes(vars ...string) []interface{} {
  118. s.written = true
  119. return s.Session().Flashes(vars...)
  120. }
  121. func (s *session) Options(options Options) {
  122. s.Session().Options = &sessions.Options{
  123. Path: options.Path,
  124. Domain: options.Domain,
  125. MaxAge: options.MaxAge,
  126. Secure: options.Secure,
  127. HttpOnly: options.HttpOnly,
  128. }
  129. }
  130. func (s *session) Session() *sessions.Session {
  131. if s.session == nil {
  132. var err error
  133. s.session, err = s.store.Get(s.request, s.name)
  134. check(err, s.logger)
  135. }
  136. return s.session
  137. }
  138. func (s *session) Written() bool {
  139. return s.written
  140. }
  141. func check(err error, l *log.Logger) {
  142. if err != nil {
  143. l.Printf(errorFormat, err)
  144. }
  145. }