github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/web/session.go (about) 1 // Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package web 6 7 import ( 8 "crypto/rand" 9 "fmt" 10 "log" 11 "net/http" 12 "sync" 13 "time" 14 ) 15 16 // SessionManager manager sessions. 17 type SessionManager struct { 18 mutex sync.RWMutex 19 mutexOnEnd sync.RWMutex 20 sessionMap map[string]*Session 21 onStart func(*Session) 22 onTouch func(*Session) 23 onEnd func(*Session) 24 path string 25 timeout uint 26 } 27 28 // Session stores the id and value for a session. 29 type Session struct { 30 Id string 31 Value interface{} 32 33 manager *SessionManager 34 res http.ResponseWriter 35 expire int64 36 } 37 38 func (session *Session) Cookie() string { 39 tm := time.Unix(session.expire, 0).UTC() 40 return fmt.Sprintf( 41 "SessionId=%s; path=%s; expires=%s;", 42 session.Id, 43 session.manager.path, 44 tm.Format("Fri, 02-Jan-2006 15:04:05 -0700"), 45 ) 46 } 47 48 func (session *Session) Abandon() { 49 if _, found := session.manager.sessionMap[session.Id]; found { 50 delete((*session.manager).sessionMap, session.Id) 51 } 52 if session.res != nil { 53 session.res.Header().Set( 54 "Set-Cookie", fmt.Sprintf("SessionId=; path=%s;", session.manager.path), 55 ) 56 } 57 } 58 59 func NewSessionManager(logger *log.Logger) *SessionManager { 60 p := &SessionManager{ 61 path: "/", 62 sessionMap: make(map[string]*Session), 63 timeout: 300, 64 } 65 go func(p *SessionManager) { 66 for { // never stop !!! 67 l := time.Now().Unix() 68 if f := p.onEnd; f != nil { 69 p.mutexOnEnd.Lock() 70 for id, v := range p.sessionMap { 71 if v.expire < l { 72 if logger != nil { 73 logger.Printf("Expired session(id:%s)", id) 74 } 75 f(v) 76 delete(p.sessionMap, id) 77 } 78 } 79 p.mutexOnEnd.Unlock() 80 } else { 81 for id, v := range p.sessionMap { 82 if v.expire < l { 83 if logger != nil { 84 logger.Printf("Expired session(id:%s)", id) 85 } 86 delete(p.sessionMap, id) 87 } 88 } 89 } 90 time.Sleep(time.Second) 91 } 92 }(p) 93 return p 94 } 95 96 func (p *SessionManager) Has(id string) (found bool) { 97 _, found = p.sessionMap[id] 98 return 99 } 100 101 func (p *SessionManager) GetSessionById(id string) (session *Session) { 102 p.mutex.Lock() 103 defer p.mutex.Unlock() 104 if id == "" || !p.Has(id) { 105 b := make([]byte, 16) 106 if _, err := rand.Read(b); err != nil { 107 return 108 } 109 id = fmt.Sprintf("%x", b) 110 } 111 tm := time.Unix(time.Now().Unix()+int64(p.timeout), 0).UTC() 112 var found bool 113 if session, found = p.sessionMap[id]; found { 114 session.expire = tm.Unix() 115 if f := p.onTouch; f != nil { 116 f(session) 117 } 118 return 119 } else { 120 session = &Session{Id: id, expire: tm.Unix(), manager: p} 121 p.sessionMap[id] = session 122 if f := p.onStart; f != nil { 123 f(session) 124 } 125 } 126 return 127 } 128 129 func (p *SessionManager) GetSession(res http.ResponseWriter, req *http.Request) (session *Session) { 130 if c, _ := req.Cookie("SessionId"); c != nil { 131 session = p.GetSessionById(c.Value) 132 } else { 133 session = p.GetSessionById("") 134 } 135 if res != nil { 136 session.res = res 137 res.Header().Add("Set-Cookie", 138 fmt.Sprintf("SessionId=%s; path=%s; expires=%s;", 139 session.Id, 140 session.manager.path, 141 time.Unix(session.expire, 0).UTC().Format( 142 "Fri, 02-Jan-2006 15:04:05 GMT", 143 ), 144 ), 145 ) 146 } 147 return 148 } 149 150 func (p *SessionManager) Abandon() { 151 p.mutex.Lock() 152 defer p.mutex.Unlock() 153 if f := p.onEnd; f != nil { 154 p.mutexOnEnd.Lock() 155 for id, v := range p.sessionMap { 156 f(v) 157 delete(p.sessionMap, id) 158 } 159 p.mutexOnEnd.Unlock() 160 } else { 161 for id, _ := range p.sessionMap { 162 delete(p.sessionMap, id) 163 } 164 } 165 } 166 167 func (p *SessionManager) OnStart(f func(*Session)) { 168 p.mutex.Lock() 169 defer p.mutex.Unlock() 170 p.onStart = f 171 } 172 173 func (p *SessionManager) OnTouch(f func(*Session)) { 174 p.mutex.Lock() 175 defer p.mutex.Unlock() 176 p.onTouch = f 177 } 178 179 func (p *SessionManager) OnEnd(f func(*Session)) { 180 p.mutex.Lock() 181 defer p.mutex.Unlock() 182 p.onEnd = f 183 } 184 185 func (p *SessionManager) SetTimeout(t uint) { 186 p.mutex.Lock() 187 defer p.mutex.Unlock() 188 p.timeout = t 189 } 190 191 func (p *SessionManager) GetTimeout() uint { 192 p.mutex.Lock() 193 defer p.mutex.Unlock() 194 return p.timeout 195 } 196 197 func (p *SessionManager) SetPath(t string) { 198 p.mutex.Lock() 199 defer p.mutex.Unlock() 200 p.path = t 201 } 202 203 func (p *SessionManager) GetPath() string { 204 p.mutex.Lock() 205 defer p.mutex.Unlock() 206 return p.path 207 }