file.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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. // Skip encoding if the data is empty
  71. if len(s.data) == 0 {
  72. return nil
  73. }
  74. data, err := EncodeGob(s.data)
  75. if err != nil {
  76. return err
  77. }
  78. return ioutil.WriteFile(s.p.filepath(s.sid), data, 0600)
  79. }
  80. // Flush deletes all session data.
  81. func (s *FileStore) Flush() error {
  82. s.lock.Lock()
  83. defer s.lock.Unlock()
  84. s.data = make(map[interface{}]interface{})
  85. return nil
  86. }
  87. // FileProvider represents a file session provider implementation.
  88. type FileProvider struct {
  89. lock sync.RWMutex
  90. maxlifetime int64
  91. rootPath string
  92. }
  93. // Init initializes file session provider with given root path.
  94. func (p *FileProvider) Init(maxlifetime int64, rootPath string) error {
  95. p.lock.Lock()
  96. p.maxlifetime = maxlifetime
  97. p.rootPath = rootPath
  98. p.lock.Unlock()
  99. return nil
  100. }
  101. func (p *FileProvider) filepath(sid string) string {
  102. return path.Join(p.rootPath, string(sid[0]), string(sid[1]), sid)
  103. }
  104. // Read returns raw session store by session ID.
  105. func (p *FileProvider) Read(sid string) (_ RawStore, err error) {
  106. filename := p.filepath(sid)
  107. if err = os.MkdirAll(path.Dir(filename), 0700); err != nil {
  108. return nil, err
  109. }
  110. p.lock.RLock()
  111. defer p.lock.RUnlock()
  112. var f *os.File
  113. if com.IsFile(filename) {
  114. f, err = os.OpenFile(filename, os.O_RDONLY, 0600)
  115. } else {
  116. f, err = os.Create(filename)
  117. }
  118. if err != nil {
  119. return nil, err
  120. }
  121. defer f.Close()
  122. if err = os.Chtimes(filename, time.Now(), time.Now()); err != nil {
  123. return nil, err
  124. }
  125. var kv map[interface{}]interface{}
  126. data, err := ioutil.ReadAll(f)
  127. if err != nil {
  128. return nil, err
  129. }
  130. if len(data) == 0 {
  131. kv = make(map[interface{}]interface{})
  132. } else {
  133. kv, err = DecodeGob(data)
  134. if err != nil {
  135. return nil, err
  136. }
  137. }
  138. return NewFileStore(p, sid, kv), nil
  139. }
  140. // Exist returns true if session with given ID exists.
  141. func (p *FileProvider) Exist(sid string) bool {
  142. p.lock.RLock()
  143. defer p.lock.RUnlock()
  144. return com.IsFile(p.filepath(sid))
  145. }
  146. // Destory deletes a session by session ID.
  147. func (p *FileProvider) Destory(sid string) error {
  148. p.lock.Lock()
  149. defer p.lock.Unlock()
  150. return os.Remove(p.filepath(sid))
  151. }
  152. func (p *FileProvider) regenerate(oldsid, sid string) (err error) {
  153. p.lock.Lock()
  154. defer p.lock.Unlock()
  155. filename := p.filepath(sid)
  156. if com.IsExist(filename) {
  157. return fmt.Errorf("new sid '%s' already exists", sid)
  158. }
  159. oldname := p.filepath(oldsid)
  160. if !com.IsFile(oldname) {
  161. data, err := EncodeGob(make(map[interface{}]interface{}))
  162. if err != nil {
  163. return err
  164. }
  165. if err = os.MkdirAll(path.Dir(oldname), 0700); err != nil {
  166. return err
  167. }
  168. if err = ioutil.WriteFile(oldname, data, 0600); err != nil {
  169. return err
  170. }
  171. }
  172. if err = os.MkdirAll(path.Dir(filename), 0700); err != nil {
  173. return err
  174. }
  175. if err = os.Rename(oldname, filename); err != nil {
  176. return err
  177. }
  178. return nil
  179. }
  180. // Regenerate regenerates a session store from old session ID to new one.
  181. func (p *FileProvider) Regenerate(oldsid, sid string) (_ RawStore, err error) {
  182. if err := p.regenerate(oldsid, sid); err != nil {
  183. return nil, err
  184. }
  185. return p.Read(sid)
  186. }
  187. // Count counts and returns number of sessions.
  188. func (p *FileProvider) Count() int {
  189. count := 0
  190. if err := filepath.Walk(p.rootPath, func(path string, fi os.FileInfo, err error) error {
  191. if err != nil {
  192. return err
  193. }
  194. if !fi.IsDir() {
  195. count++
  196. }
  197. return nil
  198. }); err != nil {
  199. log.Printf("error counting session files: %v", err)
  200. return 0
  201. }
  202. return count
  203. }
  204. // GC calls GC to clean expired sessions.
  205. func (p *FileProvider) GC() {
  206. p.lock.RLock()
  207. defer p.lock.RUnlock()
  208. if !com.IsExist(p.rootPath) {
  209. return
  210. }
  211. if err := filepath.Walk(p.rootPath, func(path string, fi os.FileInfo, err error) error {
  212. if err != nil {
  213. return err
  214. }
  215. if !fi.IsDir() &&
  216. (fi.ModTime().Unix()+p.maxlifetime) < time.Now().Unix() {
  217. return os.Remove(path)
  218. }
  219. return nil
  220. }); err != nil {
  221. log.Printf("error garbage collecting session files: %v", err)
  222. }
  223. }
  224. func init() {
  225. Register("file", &FileProvider{})
  226. }