/django/contrib/sessions/backends/file.py

https://code.google.com/p/mango-py/ · Python · 149 lines · 97 code · 21 blank · 31 comment · 31 complexity · 05184dc68d4a0cad58eb8d5553cde7b6 MD5 · raw file

  1. import errno
  2. import os
  3. import tempfile
  4. from django.conf import settings
  5. from django.contrib.sessions.backends.base import SessionBase, CreateError
  6. from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured
  7. class SessionStore(SessionBase):
  8. """
  9. Implements a file based session store.
  10. """
  11. def __init__(self, session_key=None):
  12. self.storage_path = getattr(settings, "SESSION_FILE_PATH", None)
  13. if not self.storage_path:
  14. self.storage_path = tempfile.gettempdir()
  15. # Make sure the storage path is valid.
  16. if not os.path.isdir(self.storage_path):
  17. raise ImproperlyConfigured(
  18. "The session storage path %r doesn't exist. Please set your"
  19. " SESSION_FILE_PATH setting to an existing directory in which"
  20. " Django can store session data." % self.storage_path)
  21. self.file_prefix = settings.SESSION_COOKIE_NAME
  22. super(SessionStore, self).__init__(session_key)
  23. VALID_KEY_CHARS = set("abcdef0123456789")
  24. def _key_to_file(self, session_key=None):
  25. """
  26. Get the file associated with this session key.
  27. """
  28. if session_key is None:
  29. session_key = self.session_key
  30. # Make sure we're not vulnerable to directory traversal. Session keys
  31. # should always be md5s, so they should never contain directory
  32. # components.
  33. if not set(session_key).issubset(self.VALID_KEY_CHARS):
  34. raise SuspiciousOperation(
  35. "Invalid characters in session key")
  36. return os.path.join(self.storage_path, self.file_prefix + session_key)
  37. def load(self):
  38. session_data = {}
  39. try:
  40. session_file = open(self._key_to_file(), "rb")
  41. try:
  42. file_data = session_file.read()
  43. # Don't fail if there is no data in the session file.
  44. # We may have opened the empty placeholder file.
  45. if file_data:
  46. try:
  47. session_data = self.decode(file_data)
  48. except (EOFError, SuspiciousOperation):
  49. self.create()
  50. finally:
  51. session_file.close()
  52. except IOError:
  53. self.create()
  54. return session_data
  55. def create(self):
  56. while True:
  57. self._session_key = self._get_new_session_key()
  58. try:
  59. self.save(must_create=True)
  60. except CreateError:
  61. continue
  62. self.modified = True
  63. self._session_cache = {}
  64. return
  65. def save(self, must_create=False):
  66. # Get the session data now, before we start messing
  67. # with the file it is stored within.
  68. session_data = self._get_session(no_load=must_create)
  69. session_file_name = self._key_to_file()
  70. try:
  71. # Make sure the file exists. If it does not already exist, an
  72. # empty placeholder file is created.
  73. flags = os.O_WRONLY | os.O_CREAT | getattr(os, 'O_BINARY', 0)
  74. if must_create:
  75. flags |= os.O_EXCL
  76. fd = os.open(session_file_name, flags)
  77. os.close(fd)
  78. except OSError, e:
  79. if must_create and e.errno == errno.EEXIST:
  80. raise CreateError
  81. raise
  82. # Write the session file without interfering with other threads
  83. # or processes. By writing to an atomically generated temporary
  84. # file and then using the atomic os.rename() to make the complete
  85. # file visible, we avoid having to lock the session file, while
  86. # still maintaining its integrity.
  87. #
  88. # Note: Locking the session file was explored, but rejected in part
  89. # because in order to be atomic and cross-platform, it required a
  90. # long-lived lock file for each session, doubling the number of
  91. # files in the session storage directory at any given time. This
  92. # rename solution is cleaner and avoids any additional overhead
  93. # when reading the session data, which is the more common case
  94. # unless SESSION_SAVE_EVERY_REQUEST = True.
  95. #
  96. # See ticket #8616.
  97. dir, prefix = os.path.split(session_file_name)
  98. try:
  99. output_file_fd, output_file_name = tempfile.mkstemp(dir=dir,
  100. prefix=prefix + '_out_')
  101. renamed = False
  102. try:
  103. try:
  104. os.write(output_file_fd, self.encode(session_data))
  105. finally:
  106. os.close(output_file_fd)
  107. os.rename(output_file_name, session_file_name)
  108. renamed = True
  109. finally:
  110. if not renamed:
  111. os.unlink(output_file_name)
  112. except (OSError, IOError, EOFError):
  113. pass
  114. def exists(self, session_key):
  115. if os.path.exists(self._key_to_file(session_key)):
  116. return True
  117. return False
  118. def delete(self, session_key=None):
  119. if session_key is None:
  120. if self._session_key is None:
  121. return
  122. session_key = self._session_key
  123. try:
  124. os.unlink(self._key_to_file(session_key))
  125. except OSError:
  126. pass
  127. def clean(self):
  128. pass