session.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. package session
  2. import (
  3. "crypto/sha1"
  4. "encoding/json"
  5. "fmt"
  6. "math/rand"
  7. "net/http"
  8. "os"
  9. "strings"
  10. "time"
  11. )
  12. // vars in memory storage for session variables
  13. type vars struct {
  14. Bool map[string]bool
  15. Int map[string]int
  16. Int64 map[string]int64
  17. String map[string]string
  18. }
  19. // Session session for managing session variables
  20. type Session struct {
  21. w http.ResponseWriter
  22. r *http.Request
  23. tmpdir string
  24. varlist *vars
  25. changed bool
  26. hash string
  27. }
  28. // New to create new or load saved session,
  29. // returns error if can't load saved session
  30. func New(w http.ResponseWriter, r *http.Request, tmpdir string) (*Session, error) {
  31. s := Session{
  32. w: w,
  33. r: r,
  34. tmpdir: tmpdir,
  35. varlist: &vars{
  36. Bool: map[string]bool{},
  37. Int: map[string]int{},
  38. Int64: map[string]int64{},
  39. String: map[string]string{},
  40. },
  41. changed: false,
  42. hash: "",
  43. }
  44. cookie, err := r.Cookie("session")
  45. if err == nil && len(cookie.Value) == 40 {
  46. // Load from file
  47. s.hash = cookie.Value
  48. fname := strings.Join([]string{s.tmpdir, s.hash}, string(os.PathSeparator))
  49. info, err := os.Stat(fname)
  50. if err != nil {
  51. return &s, err
  52. }
  53. var f *os.File
  54. f, err = os.Open(fname)
  55. if err != nil {
  56. return &s, err
  57. }
  58. defer f.Close()
  59. dec := json.NewDecoder(f)
  60. err = dec.Decode(&s.varlist)
  61. if err != nil {
  62. return &s, err
  63. }
  64. // Update file last modify time
  65. if time.Since(info.ModTime()) > 30*time.Minute {
  66. if err := os.Chtimes(fname, time.Now(), time.Now()); err != nil {
  67. return &s, err
  68. }
  69. }
  70. } else {
  71. // Create new
  72. rand.Seed(time.Now().Unix())
  73. // Real remote IP for proxy servers
  74. rRemoteAddr := r.RemoteAddr
  75. if r.Header.Get("X-Real-IP") != "" && len(r.Header.Get("X-Real-IP")) <= 25 {
  76. rRemoteAddr = rRemoteAddr + ", " + strings.TrimSpace(r.Header.Get("X-Real-IP"))
  77. } else if r.Header.Get("X-Forwarded-For") != "" && len(r.Header.Get("X-Forwarded-For")) <= 25 {
  78. rRemoteAddr = rRemoteAddr + ", " + strings.TrimSpace(r.Header.Get("X-Forwarded-For"))
  79. }
  80. sign := rRemoteAddr + r.Header.Get("User-Agent") + fmt.Sprintf("%d", int64(time.Now().Unix())) + fmt.Sprintf("%d", int64(rand.Intn(9999999-99)+99))
  81. s.hash = fmt.Sprintf("%x", sha1.Sum([]byte(sign)))
  82. http.SetCookie(w, &http.Cookie{
  83. Name: "session",
  84. Value: s.hash,
  85. Path: "/",
  86. Expires: time.Now().Add(7 * 24 * time.Hour),
  87. HttpOnly: true,
  88. })
  89. }
  90. return &s, nil
  91. }
  92. // Close to close session and save data to local file
  93. func (s *Session) Close() bool {
  94. if !s.changed {
  95. return false
  96. }
  97. r, err := json.Marshal(s.varlist)
  98. if err == nil {
  99. f, err := os.Create(strings.Join([]string{s.tmpdir, s.hash}, string(os.PathSeparator)))
  100. if err == nil {
  101. defer f.Close()
  102. _, err = f.Write(r)
  103. if err == nil {
  104. s.changed = false
  105. return true
  106. }
  107. }
  108. }
  109. return false
  110. }
  111. // Destroy to remove session local file
  112. func (s *Session) Destroy() error {
  113. if s.tmpdir == "" || s.hash == "" {
  114. return nil
  115. }
  116. err := os.Remove(strings.Join([]string{s.tmpdir, s.hash}, string(os.PathSeparator)))
  117. if err == nil {
  118. s.changed = false
  119. }
  120. return err
  121. }