session.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  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. type vars struct {
  13. Bool map[string]bool
  14. Int map[string]int
  15. String map[string]string
  16. }
  17. type Session struct {
  18. w http.ResponseWriter
  19. r *http.Request
  20. tmpdir string
  21. varlist *vars
  22. changed bool
  23. hash string
  24. }
  25. func New(w http.ResponseWriter, r *http.Request, tmpdir string) *Session {
  26. sess := Session{
  27. w: w,
  28. r: r,
  29. tmpdir: tmpdir,
  30. varlist: &vars{},
  31. changed: false,
  32. hash: "",
  33. }
  34. cookie, err := r.Cookie("session")
  35. if err == nil && len(cookie.Value) == 40 {
  36. // Load from file
  37. sess.hash = cookie.Value
  38. fname := strings.Join([]string{sess.tmpdir, sess.hash}, string(os.PathSeparator))
  39. f, err := os.Open(fname)
  40. if err == nil {
  41. defer f.Close()
  42. dec := json.NewDecoder(f)
  43. err = dec.Decode(&sess.varlist)
  44. if err == nil {
  45. return &sess
  46. }
  47. // Update file last modify time if needs
  48. if info, err := os.Stat(fname); err == nil {
  49. if time.Since(info.ModTime()) > 30*time.Minute {
  50. _ = os.Chtimes(fname, time.Now(), time.Now())
  51. }
  52. }
  53. }
  54. } else {
  55. // Create new
  56. rand.Seed(time.Now().Unix())
  57. // Real remote IP for proxy servers
  58. rRemoteAddr := r.RemoteAddr
  59. if r.Header.Get("X-Real-IP") != "" && len(r.Header.Get("X-Real-IP")) <= 25 {
  60. rRemoteAddr = rRemoteAddr + ", " + strings.TrimSpace(r.Header.Get("X-Real-IP"))
  61. } else if r.Header.Get("X-Forwarded-For") != "" && len(r.Header.Get("X-Forwarded-For")) <= 25 {
  62. rRemoteAddr = rRemoteAddr + ", " + strings.TrimSpace(r.Header.Get("X-Forwarded-For"))
  63. }
  64. sign := rRemoteAddr + r.Header.Get("User-Agent") + fmt.Sprintf("%d", int64(time.Now().Unix())) + fmt.Sprintf("%d", int64(rand.Intn(9999999-99)+99))
  65. sess.hash = fmt.Sprintf("%x", sha1.Sum([]byte(sign)))
  66. http.SetCookie(w, &http.Cookie{
  67. Name: "session",
  68. Value: sess.hash,
  69. Path: "/",
  70. Expires: time.Now().Add(7 * 24 * time.Hour),
  71. HttpOnly: true,
  72. })
  73. }
  74. // Init empty
  75. sess.varlist = &vars{
  76. Bool: map[string]bool{},
  77. Int: map[string]int{},
  78. String: map[string]string{},
  79. }
  80. return &sess
  81. }
  82. func (s *Session) Close() bool {
  83. if !s.changed {
  84. return false
  85. }
  86. r, err := json.Marshal(s.varlist)
  87. if err == nil {
  88. f, err := os.Create(strings.Join([]string{s.tmpdir, s.hash}, string(os.PathSeparator)))
  89. if err == nil {
  90. defer f.Close()
  91. _, err = f.Write(r)
  92. if err == nil {
  93. s.changed = false
  94. return true
  95. }
  96. }
  97. }
  98. return false
  99. }
  100. func (s *Session) Destroy() error {
  101. if s.tmpdir == "" || s.hash == "" {
  102. return nil
  103. }
  104. err := os.Remove(strings.Join([]string{s.tmpdir, s.hash}, string(os.PathSeparator)))
  105. if err == nil {
  106. s.changed = false
  107. }
  108. return err
  109. }