Commits

Robert Brewer committed e35e169

Some additional session safety (see #563).

  • Participants
  • Parent commits e5a7d91

Comments (0)

Files changed (2)

File cherrypy/lib/sessions.py

     
     def save(self):
         """Save session data."""
-        # If session data has never been loaded then it's never been
-        #   accessed: no need to delete it
-        if self.loaded:
-            t = datetime.timedelta(seconds = self.timeout * 60)
-            expiration_time = datetime.datetime.now() + t
-            self._save(expiration_time)
-        
-        if self.locked:
-            # Always release the lock if the user didn't release it
-            self.release_lock()
+        try:
+            # If session data has never been loaded then it's never been
+            #   accessed: no need to delete it
+            if self.loaded:
+                t = datetime.timedelta(seconds = self.timeout * 60)
+                expiration_time = datetime.datetime.now() + t
+                self._save(expiration_time)
+            
+        finally:
+            if self.locked:
+                # Always release the lock if the user didn't release it
+                self.release_lock()
     
     def load(self):
         """Copy stored session data into this session instance."""
                     del self.cache[id]
                 except KeyError:
                     pass
+                try:
+                    del self.locks[id]
+                except KeyError:
+                    pass
     
     def _load(self):
         return self.cache.get(self.id)
     
     def acquire_lock(self):
         self.locked = True
-        self.locks.setdefault(self.id, threading.Semaphore()).acquire()
+        self.locks.setdefault(self.id, threading.RLock()).acquire()
     
     def release_lock(self):
         self.locks[self.id].release()
     if sess.locked:
         # If the session is still locked we release the lock
         sess.release_lock()
+    del cherrypy._serving.session
 close.failsafe = True
+close.priority = 90
 
 
 _def_session = RamSession()
     if not hasattr(cherrypy, "session"):
         cherrypy.session = cherrypy._ThreadLocalProxy('session', _def_session)
     
-    # Create and attach a new Session instance to cherrypy.request.
+    # Create and attach a new Session instance to cherrypy._serving.
     # It will possess a reference to (and lock, and lazily load)
     # the requested session data.
     storage_class = storage_type.title() + 'Session'

File cherrypy/test/test_conn.py

         conn.connect()
         
         # Put request 1
-        conn.putrequest("GET", "/", skip_host=True)
+        conn.putrequest("GET", "/hello", skip_host=True)
         conn.putheader("Host", self.HOST)
         conn.endheaders()
         
-        # Put request 2
-        conn._output('GET /hello HTTP/1.1')
-        conn._output("Host: %s" % self.HOST)
-        conn._send_output()
+        for trial in xrange(5):
+            # Put next request
+            conn._output('GET /hello HTTP/1.1')
+            conn._output("Host: %s" % self.HOST)
+            conn._send_output()
+            
+            # Retrieve previous response
+            response = conn.response_class(conn.sock, method="GET")
+            response.begin()
+            body = response.read()
+            self.assertEqual(response.status, 200)
+            self.assertEqual(body, "Hello, world!")
         
-        # Retrieve response 1
-        response = conn.response_class(conn.sock, method="GET")
-        response.begin()
-        body = response.read()
-        self.assertEqual(response.status, 200)
-        self.assertEqual(body, pov)
-        
-        # Retrieve response 2
+        # Retrieve final response
         response = conn.response_class(conn.sock, method="GET")
         response.begin()
         body = response.read()