file.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. // Copyright 2013 Beego Authors
  2. // Copyright 2014 The Macaron Authors
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License"): you may
  5. // not use this file except in compliance with the License. You may obtain
  6. // a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. // License for the specific language governing permissions and limitations
  14. // under the License.
  15. package session
  16. import (
  17. "fmt"
  18. "io/ioutil"
  19. "log"
  20. "os"
  21. "path"
  22. "path/filepath"
  23. "sync"
  24. "time"
  25. "github.com/Unknwon/com"
  26. )
  27. // FileStore represents a file session store implementation.
  28. type FileStore struct {
  29. p *FileProvider
  30. sid string
  31. lock sync.RWMutex
  32. data map[interface{}]interface{}
  33. }
  34. // NewFileStore creates and returns a file session store.
  35. func NewFileStore(p *FileProvider, sid string, kv map[interface{}]interface{}) *FileStore {
  36. return &FileStore{
  37. p: p,
  38. sid: sid,
  39. data: kv,
  40. }
  41. }
  42. // Set sets value to given key in session.
  43. func (s *FileStore) Set(key, val interface{}) error {
  44. s.lock.Lock()
  45. defer s.lock.Unlock()
  46. s.data[key] = val
  47. return nil
  48. }
  49. // Get gets value by given key in session.
  50. func (s *FileStore) Get(key interface{}) interface{} {
  51. s.lock.RLock()
  52. defer s.lock.RUnlock()
  53. return s.data[key]
  54. }
  55. // Delete delete a key from session.
  56. func (s *FileStore) Delete(key interface{}) error {
  57. s.lock.Lock()
  58. defer s.lock.Unlock()
  59. delete(s.data, key)
  60. return nil
  61. }
  62. // ID returns current session ID.
  63. func (s *FileStore) ID() string {
  64. return s.sid
  65. }
  66. // Release releases resource and save data to provider.
  67. func (s *FileStore) Release() error {
  68. s.p.lock.Lock()
  69. defer s.p.lock.Unlock()
  70. data, err := EncodeGob(s.data)
  71. if err != nil {
  72. return err
  73. }
  74. return ioutil.WriteFile(s.p.filepath(s.sid), data, 0600)
  75. }
  76. // Flush deletes all session data.
  77. func (s *FileStore) Flush() error {
  78. s.lock.Lock()
  79. defer s.lock.Unlock()
  80. s.data = make(map[interface{}]interface{})
  81. return nil
  82. }
  83. // FileProvider represents a file session provider implementation.
  84. type FileProvider struct {
  85. lock sync.RWMutex
  86. maxlifetime int64
  87. rootPath string
  88. }
  89. // Init initializes file session provider with given root path.
  90. func (p *FileProvider) Init(maxlifetime int64, rootPath string) error {
  91. p.lock.Lock()
  92. p.maxlifetime = maxlifetime
  93. p.rootPath = rootPath
  94. p.lock.Unlock()
  95. return nil
  96. }
  97. func (p *FileProvider) filepath(sid string) string {
  98. return path.Join(p.rootPath, string(sid[0]), string(sid[1]), sid)
  99. }
  100. // Read returns raw session store by session ID.
  101. func (p *FileProvider) Read(sid string) (_ RawStore, err error) {
  102. filename := p.filepath(sid)
  103. if err = os.MkdirAll(path.Dir(filename), 0700); err != nil {
  104. return nil, err
  105. }
  106. p.lock.RLock()
  107. defer p.lock.RUnlock()
  108. var f *os.File
  109. if com.IsFile(filename) {
  110. f, err = os.OpenFile(filename, os.O_RDONLY, 0600)
  111. } else {
  112. f, err = os.Create(filename)
  113. }
  114. if err != nil {
  115. return nil, err
  116. }
  117. defer f.Close()
  118. if err = os.Chtimes(filename, time.Now(), time.Now()); err != nil {
  119. return nil, err
  120. }
  121. var kv map[interface{}]interface{}
  122. data, err := ioutil.ReadAll(f)
  123. if err != nil {
  124. return nil, err
  125. }
  126. if len(data) == 0 {
  127. kv = make(map[interface{}]interface{})
  128. } else {
  129. kv, err = DecodeGob(data)
  130. if err != nil {
  131. return nil, err
  132. }
  133. }
  134. return NewFileStore(p, sid, kv), nil
  135. }
  136. // Exist returns true if session with given ID exists.
  137. func (p *FileProvider) Exist(sid string) bool {
  138. p.lock.RLock()
  139. defer p.lock.RUnlock()
  140. return com.IsFile(p.filepath(sid))
  141. }
  142. // Destory deletes a session by session ID.
  143. func (p *FileProvider) Destory(sid string) error {
  144. p.lock.Lock()
  145. defer p.lock.Unlock()
  146. return os.Remove(p.filepath(sid))
  147. }
  148. func (p *FileProvider) regenerate(oldsid, sid string) (err error) {
  149. p.lock.Lock()
  150. defer p.lock.Unlock()
  151. filename := p.filepath(sid)
  152. if com.IsExist(filename) {
  153. return fmt.Errorf("new sid '%s' already exists", sid)
  154. }
  155. oldname := p.filepath(oldsid)
  156. if !com.IsFile(oldname) {
  157. data, err := EncodeGob(make(map[interface{}]interface{}))
  158. if err != nil {
  159. return err
  160. }
  161. if err = os.MkdirAll(path.Dir(oldname), 0700); err != nil {
  162. return err
  163. }
  164. if err = ioutil.WriteFile(oldname, data, 0600); err != nil {
  165. return err
  166. }
  167. }
  168. if err = os.MkdirAll(path.Dir(filename), 0700); err != nil {
  169. return err
  170. }
  171. if err = os.Rename(oldname, filename); err != nil {
  172. return err
  173. }
  174. return nil
  175. }
  176. // Regenerate regenerates a session store from old session ID to new one.
  177. func (p *FileProvider) Regenerate(oldsid, sid string) (_ RawStore, err error) {
  178. if err := p.regenerate(oldsid, sid); err != nil {
  179. return nil, err
  180. }
  181. return p.Read(sid)
  182. }
  183. // Count counts and returns number of sessions.
  184. func (p *FileProvider) Count() int {
  185. count := 0
  186. if err := filepath.Walk(p.rootPath, func(path string, fi os.FileInfo, err error) error {
  187. if err != nil {
  188. return err
  189. }
  190. if !fi.IsDir() {
  191. count++
  192. }
  193. return nil
  194. }); err != nil {
  195. log.Printf("error counting session files: %v", err)
  196. return 0
  197. }
  198. return count
  199. }
  200. // GC calls GC to clean expired sessions.
  201. func (p *FileProvider) GC() {
  202. p.lock.RLock()
  203. defer p.lock.RUnlock()
  204. if !com.IsExist(p.rootPath) {
  205. return
  206. }
  207. if err := filepath.Walk(p.rootPath, func(path string, fi os.FileInfo, err error) error {
  208. if err != nil {
  209. return err
  210. }
  211. if !fi.IsDir() &&
  212. (fi.ModTime().Unix()+p.maxlifetime) < time.Now().Unix() {
  213. return os.Remove(path)
  214. }
  215. return nil
  216. }); err != nil {
  217. log.Printf("error garbage collecting session files: %v", err)
  218. }
  219. }
  220. func init() {
  221. Register("file", &FileProvider{})
  222. }