github.com/astaxie/beego@v1.12.3/session/sess_file.go (about) 1 // Copyright 2014 beego Author. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package session 16 17 import ( 18 "errors" 19 "fmt" 20 "io/ioutil" 21 "net/http" 22 "os" 23 "path" 24 "path/filepath" 25 "strings" 26 "sync" 27 "time" 28 ) 29 30 var ( 31 filepder = &FileProvider{} 32 gcmaxlifetime int64 33 ) 34 35 // FileSessionStore File session store 36 type FileSessionStore struct { 37 sid string 38 lock sync.RWMutex 39 values map[interface{}]interface{} 40 } 41 42 // Set value to file session 43 func (fs *FileSessionStore) Set(key, value interface{}) error { 44 fs.lock.Lock() 45 defer fs.lock.Unlock() 46 fs.values[key] = value 47 return nil 48 } 49 50 // Get value from file session 51 func (fs *FileSessionStore) Get(key interface{}) interface{} { 52 fs.lock.RLock() 53 defer fs.lock.RUnlock() 54 if v, ok := fs.values[key]; ok { 55 return v 56 } 57 return nil 58 } 59 60 // Delete value in file session by given key 61 func (fs *FileSessionStore) Delete(key interface{}) error { 62 fs.lock.Lock() 63 defer fs.lock.Unlock() 64 delete(fs.values, key) 65 return nil 66 } 67 68 // Flush Clean all values in file session 69 func (fs *FileSessionStore) Flush() error { 70 fs.lock.Lock() 71 defer fs.lock.Unlock() 72 fs.values = make(map[interface{}]interface{}) 73 return nil 74 } 75 76 // SessionID Get file session store id 77 func (fs *FileSessionStore) SessionID() string { 78 return fs.sid 79 } 80 81 // SessionRelease Write file session to local file with Gob string 82 func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) { 83 filepder.lock.Lock() 84 defer filepder.lock.Unlock() 85 b, err := EncodeGob(fs.values) 86 if err != nil { 87 SLogger.Println(err) 88 return 89 } 90 _, err = os.Stat(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid)) 91 var f *os.File 92 if err == nil { 93 f, err = os.OpenFile(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid), os.O_RDWR, 0777) 94 if err != nil { 95 SLogger.Println(err) 96 return 97 } 98 } else if os.IsNotExist(err) { 99 f, err = os.Create(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid)) 100 if err != nil { 101 SLogger.Println(err) 102 return 103 } 104 } else { 105 return 106 } 107 f.Truncate(0) 108 f.Seek(0, 0) 109 f.Write(b) 110 f.Close() 111 } 112 113 // FileProvider File session provider 114 type FileProvider struct { 115 lock sync.RWMutex 116 maxlifetime int64 117 savePath string 118 } 119 120 // SessionInit Init file session provider. 121 // savePath sets the session files path. 122 func (fp *FileProvider) SessionInit(maxlifetime int64, savePath string) error { 123 fp.maxlifetime = maxlifetime 124 fp.savePath = savePath 125 return nil 126 } 127 128 // SessionRead Read file session by sid. 129 // if file is not exist, create it. 130 // the file path is generated from sid string. 131 func (fp *FileProvider) SessionRead(sid string) (Store, error) { 132 invalidChars := "./" 133 if strings.ContainsAny(sid, invalidChars) { 134 return nil, errors.New("the sid shouldn't have following characters: " + invalidChars) 135 } 136 if len(sid) < 2 { 137 return nil, errors.New("length of the sid is less than 2") 138 } 139 filepder.lock.Lock() 140 defer filepder.lock.Unlock() 141 142 err := os.MkdirAll(path.Join(fp.savePath, string(sid[0]), string(sid[1])), 0755) 143 if err != nil { 144 SLogger.Println(err.Error()) 145 } 146 _, err = os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) 147 var f *os.File 148 if err == nil { 149 f, err = os.OpenFile(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid), os.O_RDWR, 0777) 150 } else if os.IsNotExist(err) { 151 f, err = os.Create(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) 152 } else { 153 return nil, err 154 } 155 156 defer f.Close() 157 158 os.Chtimes(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid), time.Now(), time.Now()) 159 var kv map[interface{}]interface{} 160 b, err := ioutil.ReadAll(f) 161 if err != nil { 162 return nil, err 163 } 164 if len(b) == 0 { 165 kv = make(map[interface{}]interface{}) 166 } else { 167 kv, err = DecodeGob(b) 168 if err != nil { 169 return nil, err 170 } 171 } 172 173 ss := &FileSessionStore{sid: sid, values: kv} 174 return ss, nil 175 } 176 177 // SessionExist Check file session exist. 178 // it checks the file named from sid exist or not. 179 func (fp *FileProvider) SessionExist(sid string) bool { 180 filepder.lock.Lock() 181 defer filepder.lock.Unlock() 182 183 if len(sid) < 2 { 184 SLogger.Println("min length of session id is 2", sid) 185 return false 186 } 187 188 _, err := os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) 189 return err == nil 190 } 191 192 // SessionDestroy Remove all files in this save path 193 func (fp *FileProvider) SessionDestroy(sid string) error { 194 filepder.lock.Lock() 195 defer filepder.lock.Unlock() 196 os.Remove(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) 197 return nil 198 } 199 200 // SessionGC Recycle files in save path 201 func (fp *FileProvider) SessionGC() { 202 filepder.lock.Lock() 203 defer filepder.lock.Unlock() 204 205 gcmaxlifetime = fp.maxlifetime 206 filepath.Walk(fp.savePath, gcpath) 207 } 208 209 // SessionAll Get active file session number. 210 // it walks save path to count files. 211 func (fp *FileProvider) SessionAll() int { 212 a := &activeSession{} 213 err := filepath.Walk(fp.savePath, func(path string, f os.FileInfo, err error) error { 214 return a.visit(path, f, err) 215 }) 216 if err != nil { 217 SLogger.Printf("filepath.Walk() returned %v\n", err) 218 return 0 219 } 220 return a.total 221 } 222 223 // SessionRegenerate Generate new sid for file session. 224 // it delete old file and create new file named from new sid. 225 func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (Store, error) { 226 filepder.lock.Lock() 227 defer filepder.lock.Unlock() 228 229 oldPath := path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1])) 230 oldSidFile := path.Join(oldPath, oldsid) 231 newPath := path.Join(fp.savePath, string(sid[0]), string(sid[1])) 232 newSidFile := path.Join(newPath, sid) 233 234 // new sid file is exist 235 _, err := os.Stat(newSidFile) 236 if err == nil { 237 return nil, fmt.Errorf("newsid %s exist", newSidFile) 238 } 239 240 err = os.MkdirAll(newPath, 0755) 241 if err != nil { 242 SLogger.Println(err.Error()) 243 } 244 245 // if old sid file exist 246 // 1.read and parse file content 247 // 2.write content to new sid file 248 // 3.remove old sid file, change new sid file atime and ctime 249 // 4.return FileSessionStore 250 _, err = os.Stat(oldSidFile) 251 if err == nil { 252 b, err := ioutil.ReadFile(oldSidFile) 253 if err != nil { 254 return nil, err 255 } 256 257 var kv map[interface{}]interface{} 258 if len(b) == 0 { 259 kv = make(map[interface{}]interface{}) 260 } else { 261 kv, err = DecodeGob(b) 262 if err != nil { 263 return nil, err 264 } 265 } 266 267 ioutil.WriteFile(newSidFile, b, 0777) 268 os.Remove(oldSidFile) 269 os.Chtimes(newSidFile, time.Now(), time.Now()) 270 ss := &FileSessionStore{sid: sid, values: kv} 271 return ss, nil 272 } 273 274 // if old sid file not exist, just create new sid file and return 275 newf, err := os.Create(newSidFile) 276 if err != nil { 277 return nil, err 278 } 279 newf.Close() 280 ss := &FileSessionStore{sid: sid, values: make(map[interface{}]interface{})} 281 return ss, nil 282 } 283 284 // remove file in save path if expired 285 func gcpath(path string, info os.FileInfo, err error) error { 286 if err != nil { 287 return err 288 } 289 if info.IsDir() { 290 return nil 291 } 292 if (info.ModTime().Unix() + gcmaxlifetime) < time.Now().Unix() { 293 os.Remove(path) 294 } 295 return nil 296 } 297 298 type activeSession struct { 299 total int 300 } 301 302 func (as *activeSession) visit(paths string, f os.FileInfo, err error) error { 303 if err != nil { 304 return err 305 } 306 if f.IsDir() { 307 return nil 308 } 309 as.total = as.total + 1 310 return nil 311 } 312 313 func init() { 314 Register("file", filepder) 315 }