123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- package session
- import (
- "crypto/sha1"
- "encoding/json"
- "fmt"
- "math/rand"
- "net/http"
- "os"
- "strings"
- "sync"
- "time"
- )
- // vars in memory storage for session variables
- type vars struct {
- sync.RWMutex
- Bool map[string]bool
- Int map[string]int
- Int64 map[string]int64
- String map[string]string
- }
- // Session session for managing session variables
- type Session struct {
- w http.ResponseWriter
- r *http.Request
- tmpdir string
- varlist *vars
- changed bool
- hash string
- }
- // New to create new or load saved session,
- // returns error if can't load saved session
- func New(w http.ResponseWriter, r *http.Request, tmpdir string) (*Session, error) {
- s := Session{
- w: w,
- r: r,
- tmpdir: tmpdir,
- varlist: &vars{
- Bool: map[string]bool{},
- Int: map[string]int{},
- Int64: map[string]int64{},
- String: map[string]string{},
- },
- changed: false,
- hash: "",
- }
- s.varlist.Lock()
- defer s.varlist.Unlock()
- cookie, err := r.Cookie("session")
- if err == nil && len(cookie.Value) == 40 {
- // Load from file
- s.hash = cookie.Value
- fname := strings.Join([]string{s.tmpdir, s.hash}, string(os.PathSeparator))
- info, err := os.Stat(fname)
- if err != nil {
- return &s, err
- }
- var f *os.File
- f, err = os.Open(fname)
- if err != nil {
- return &s, err
- }
- defer f.Close()
- dec := json.NewDecoder(f)
- err = dec.Decode(&s.varlist)
- if err != nil {
- return &s, err
- }
- // Update file last modify time
- if time.Since(info.ModTime()) > 30*time.Minute {
- if err := os.Chtimes(fname, time.Now(), time.Now()); err != nil {
- return &s, err
- }
- }
- } else {
- // Create new
- rand.Seed(time.Now().Unix())
- // Real remote IP for proxy servers
- rRemoteAddr := r.RemoteAddr
- if r.Header.Get("X-Real-IP") != "" && len(r.Header.Get("X-Real-IP")) <= 25 {
- rRemoteAddr = rRemoteAddr + ", " + strings.TrimSpace(r.Header.Get("X-Real-IP"))
- } else if r.Header.Get("X-Forwarded-For") != "" && len(r.Header.Get("X-Forwarded-For")) <= 25 {
- rRemoteAddr = rRemoteAddr + ", " + strings.TrimSpace(r.Header.Get("X-Forwarded-For"))
- }
- sign := rRemoteAddr + r.Header.Get("User-Agent") + fmt.Sprintf("%d", int64(time.Now().Unix())) + fmt.Sprintf("%d", int64(rand.Intn(9999999-99)+99))
- s.hash = fmt.Sprintf("%x", sha1.Sum([]byte(sign)))
- http.SetCookie(w, &http.Cookie{
- Name: "session",
- Value: s.hash,
- Path: "/",
- Expires: time.Now().Add(7 * 24 * time.Hour),
- HttpOnly: true,
- })
- }
- return &s, nil
- }
- // Close to close session and save data to local file
- func (s *Session) Close() bool {
- if !s.changed {
- return false
- }
- s.varlist.Lock()
- defer s.varlist.Unlock()
- r, err := json.Marshal(s.varlist)
- if err == nil {
- f, err := os.Create(strings.Join([]string{s.tmpdir, s.hash}, string(os.PathSeparator)))
- if err == nil {
- defer f.Close()
- _, err = f.Write(r)
- if err == nil {
- s.changed = false
- return true
- }
- }
- }
- return false
- }
- // Destroy to remove session local file
- func (s *Session) Destroy() error {
- if s.tmpdir == "" || s.hash == "" {
- return nil
- }
- err := os.Remove(strings.Join([]string{s.tmpdir, s.hash}, string(os.PathSeparator)))
- if err == nil {
- s.changed = false
- }
- return err
- }
|