Package cherrypy :: Package test :: Module test_session
[hide private]
[frames] | no frames]

Source Code for Module cherrypy.test.test_session

  1  import os 
  2  localDir = os.path.dirname(__file__) 
  3  import sys 
  4  import threading 
  5  import time 
  6   
  7  import cherrypy 
  8  from cherrypy._cpcompat import copykeys, HTTPConnection, HTTPSConnection 
  9  from cherrypy.lib import sessions 
 10  from cherrypy.lib.httputil import response_codes 
 11   
 12   
13 -def http_methods_allowed(methods=['GET', 'HEAD']):
14 method = cherrypy.request.method.upper() 15 if method not in methods: 16 cherrypy.response.headers['Allow'] = ", ".join(methods) 17 raise cherrypy.HTTPError(405)
18 19 cherrypy.tools.allow = cherrypy.Tool('on_start_resource', http_methods_allowed) 20 21
22 -def setup_server():
23 24 class Root: 25 26 _cp_config = {'tools.sessions.on': True, 27 'tools.sessions.storage_type': 'ram', 28 'tools.sessions.storage_path': localDir, 29 'tools.sessions.timeout': (1.0 / 60), 30 'tools.sessions.clean_freq': (1.0 / 60), 31 } 32 33 def clear(self): 34 cherrypy.session.cache.clear()
35 clear.exposed = True 36 37 def data(self): 38 cherrypy.session['aha'] = 'foo' 39 return repr(cherrypy.session._data) 40 data.exposed = True 41 42 def testGen(self): 43 counter = cherrypy.session.get('counter', 0) + 1 44 cherrypy.session['counter'] = counter 45 yield str(counter) 46 testGen.exposed = True 47 48 def testStr(self): 49 counter = cherrypy.session.get('counter', 0) + 1 50 cherrypy.session['counter'] = counter 51 return str(counter) 52 testStr.exposed = True 53 54 def setsessiontype(self, newtype): 55 self.__class__._cp_config.update( 56 {'tools.sessions.storage_type': newtype}) 57 if hasattr(cherrypy, "session"): 58 del cherrypy.session 59 cls = getattr(sessions, newtype.title() + 'Session') 60 if cls.clean_thread: 61 cls.clean_thread.stop() 62 cls.clean_thread.unsubscribe() 63 del cls.clean_thread 64 setsessiontype.exposed = True 65 setsessiontype._cp_config = {'tools.sessions.on': False} 66 67 def index(self): 68 sess = cherrypy.session 69 c = sess.get('counter', 0) + 1 70 time.sleep(0.01) 71 sess['counter'] = c 72 return str(c) 73 index.exposed = True 74 75 def keyin(self, key): 76 return str(key in cherrypy.session) 77 keyin.exposed = True 78 79 def delete(self): 80 cherrypy.session.delete() 81 sessions.expire() 82 return "done" 83 delete.exposed = True 84 85 def delkey(self, key): 86 del cherrypy.session[key] 87 return "OK" 88 delkey.exposed = True 89 90 def blah(self): 91 return self._cp_config['tools.sessions.storage_type'] 92 blah.exposed = True 93 94 def iredir(self): 95 raise cherrypy.InternalRedirect('/blah') 96 iredir.exposed = True 97 98 def restricted(self): 99 return cherrypy.request.method 100 restricted.exposed = True 101 restricted._cp_config = {'tools.allow.on': True, 102 'tools.allow.methods': ['GET']} 103 104 def regen(self): 105 cherrypy.tools.sessions.regenerate() 106 return "logged in" 107 regen.exposed = True 108 109 def length(self): 110 return str(len(cherrypy.session)) 111 length.exposed = True 112 113 def session_cookie(self): 114 # Must load() to start the clean thread. 115 cherrypy.session.load() 116 return cherrypy.session.id 117 session_cookie.exposed = True 118 session_cookie._cp_config = { 119 'tools.sessions.path': '/session_cookie', 120 'tools.sessions.name': 'temp', 121 'tools.sessions.persistent': False} 122 123 cherrypy.tree.mount(Root()) 124 125 126 from cherrypy.test import helper 127 128
129 -class SessionTest(helper.CPWebCase):
130 setup_server = staticmethod(setup_server) 131
132 - def tearDown(self):
133 # Clean up sessions. 134 for fname in os.listdir(localDir): 135 if fname.startswith(sessions.FileSession.SESSION_PREFIX): 136 os.unlink(os.path.join(localDir, fname))
137
138 - def test_0_Session(self):
139 self.getPage('/setsessiontype/ram') 140 self.getPage('/clear') 141 142 # Test that a normal request gets the same id in the cookies. 143 # Note: this wouldn't work if /data didn't load the session. 144 self.getPage('/data') 145 self.assertBody("{'aha': 'foo'}") 146 c = self.cookies[0] 147 self.getPage('/data', self.cookies) 148 self.assertEqual(self.cookies[0], c) 149 150 self.getPage('/testStr') 151 self.assertBody('1') 152 cookie_parts = dict([p.strip().split('=') 153 for p in self.cookies[0][1].split(";")]) 154 # Assert there is an 'expires' param 155 self.assertEqual(set(cookie_parts.keys()), 156 set(['session_id', 'expires', 'Path'])) 157 self.getPage('/testGen', self.cookies) 158 self.assertBody('2') 159 self.getPage('/testStr', self.cookies) 160 self.assertBody('3') 161 self.getPage('/data', self.cookies) 162 # https://bitbucket.org/cherrypy/cherrypy/issue/1328 163 #self.assertBody("{'aha': 'foo', 'counter': 3}") 164 self.getPage('/length', self.cookies) 165 self.assertBody('2') 166 self.getPage('/delkey?key=counter', self.cookies) 167 self.assertStatus(200) 168 169 self.getPage('/setsessiontype/file') 170 self.getPage('/testStr') 171 self.assertBody('1') 172 self.getPage('/testGen', self.cookies) 173 self.assertBody('2') 174 self.getPage('/testStr', self.cookies) 175 self.assertBody('3') 176 self.getPage('/delkey?key=counter', self.cookies) 177 self.assertStatus(200) 178 179 # Wait for the session.timeout (1 second) 180 time.sleep(2) 181 self.getPage('/') 182 self.assertBody('1') 183 self.getPage('/length', self.cookies) 184 self.assertBody('1') 185 186 # Test session __contains__ 187 self.getPage('/keyin?key=counter', self.cookies) 188 self.assertBody("True") 189 cookieset1 = self.cookies 190 191 # Make a new session and test __len__ again 192 self.getPage('/') 193 self.getPage('/length', self.cookies) 194 self.assertBody('2') 195 196 # Test session delete 197 self.getPage('/delete', self.cookies) 198 self.assertBody("done") 199 self.getPage('/delete', cookieset1) 200 self.assertBody("done") 201 f = lambda: [ 202 x for x in os.listdir(localDir) if x.startswith('session-')] 203 self.assertEqual(f(), []) 204 205 # Wait for the cleanup thread to delete remaining session files 206 self.getPage('/') 207 f = lambda: [ 208 x for x in os.listdir(localDir) if x.startswith('session-')] 209 self.assertNotEqual(f(), []) 210 time.sleep(2) 211 self.assertEqual(f(), [])
212
213 - def test_1_Ram_Concurrency(self):
214 self.getPage('/setsessiontype/ram') 215 self._test_Concurrency()
216
217 - def test_2_File_Concurrency(self):
218 self.getPage('/setsessiontype/file') 219 self._test_Concurrency()
220
221 - def _test_Concurrency(self):
222 client_thread_count = 5 223 request_count = 30 224 225 # Get initial cookie 226 self.getPage("/") 227 self.assertBody("1") 228 cookies = self.cookies 229 230 data_dict = {} 231 errors = [] 232 233 def request(index): 234 if self.scheme == 'https': 235 c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT)) 236 else: 237 c = HTTPConnection('%s:%s' % (self.interface(), self.PORT)) 238 for i in range(request_count): 239 c.putrequest('GET', '/') 240 for k, v in cookies: 241 c.putheader(k, v) 242 c.endheaders() 243 response = c.getresponse() 244 body = response.read() 245 if response.status != 200 or not body.isdigit(): 246 errors.append((response.status, body)) 247 else: 248 data_dict[index] = max(data_dict[index], int(body))
249 # Uncomment the following line to prove threads overlap. 250 ## sys.stdout.write("%d " % index) 251 252 # Start <request_count> requests from each of 253 # <client_thread_count> concurrent clients 254 ts = [] 255 for c in range(client_thread_count): 256 data_dict[c] = 0 257 t = threading.Thread(target=request, args=(c,)) 258 ts.append(t) 259 t.start() 260 261 for t in ts: 262 t.join() 263 264 hitcount = max(data_dict.values()) 265 expected = 1 + (client_thread_count * request_count) 266 267 for e in errors: 268 print(e) 269 self.assertEqual(hitcount, expected)
270
271 - def test_3_Redirect(self):
272 # Start a new session 273 self.getPage('/testStr') 274 self.getPage('/iredir', self.cookies) 275 self.assertBody("file")
276
277 - def test_4_File_deletion(self):
278 # Start a new session 279 self.getPage('/testStr') 280 # Delete the session file manually and retry. 281 id = self.cookies[0][1].split(";", 1)[0].split("=", 1)[1] 282 path = os.path.join(localDir, "session-" + id) 283 os.unlink(path) 284 self.getPage('/testStr', self.cookies)
285
286 - def test_5_Error_paths(self):
287 self.getPage('/unknown/page') 288 self.assertErrorPage(404, "The path '/unknown/page' was not found.") 289 290 # Note: this path is *not* the same as above. The above 291 # takes a normal route through the session code; this one 292 # skips the session code's before_handler and only calls 293 # before_finalize (save) and on_end (close). So the session 294 # code has to survive calling save/close without init. 295 self.getPage('/restricted', self.cookies, method='POST') 296 self.assertErrorPage(405, response_codes[405][1])
297
298 - def test_6_regenerate(self):
299 self.getPage('/testStr') 300 # grab the cookie ID 301 id1 = self.cookies[0][1].split(";", 1)[0].split("=", 1)[1] 302 self.getPage('/regen') 303 self.assertBody('logged in') 304 id2 = self.cookies[0][1].split(";", 1)[0].split("=", 1)[1] 305 self.assertNotEqual(id1, id2) 306 307 self.getPage('/testStr') 308 # grab the cookie ID 309 id1 = self.cookies[0][1].split(";", 1)[0].split("=", 1)[1] 310 self.getPage('/testStr', 311 headers=[ 312 ('Cookie', 313 'session_id=maliciousid; ' 314 'expires=Sat, 27 Oct 2017 04:18:28 GMT; Path=/;')]) 315 id2 = self.cookies[0][1].split(";", 1)[0].split("=", 1)[1] 316 self.assertNotEqual(id1, id2) 317 self.assertNotEqual(id2, 'maliciousid')
318
319 - def test_7_session_cookies(self):
320 self.getPage('/setsessiontype/ram') 321 self.getPage('/clear') 322 self.getPage('/session_cookie') 323 # grab the cookie ID 324 cookie_parts = dict([p.strip().split('=') 325 for p in self.cookies[0][1].split(";")]) 326 # Assert there is no 'expires' param 327 self.assertEqual(set(cookie_parts.keys()), set(['temp', 'Path'])) 328 id1 = cookie_parts['temp'] 329 self.assertEqual(copykeys(sessions.RamSession.cache), [id1]) 330 331 # Send another request in the same "browser session". 332 self.getPage('/session_cookie', self.cookies) 333 cookie_parts = dict([p.strip().split('=') 334 for p in self.cookies[0][1].split(";")]) 335 # Assert there is no 'expires' param 336 self.assertEqual(set(cookie_parts.keys()), set(['temp', 'Path'])) 337 self.assertBody(id1) 338 self.assertEqual(copykeys(sessions.RamSession.cache), [id1]) 339 340 # Simulate a browser close by just not sending the cookies 341 self.getPage('/session_cookie') 342 # grab the cookie ID 343 cookie_parts = dict([p.strip().split('=') 344 for p in self.cookies[0][1].split(";")]) 345 # Assert there is no 'expires' param 346 self.assertEqual(set(cookie_parts.keys()), set(['temp', 'Path'])) 347 # Assert a new id has been generated... 348 id2 = cookie_parts['temp'] 349 self.assertNotEqual(id1, id2) 350 self.assertEqual(set(sessions.RamSession.cache.keys()), 351 set([id1, id2])) 352 353 # Wait for the session.timeout on both sessions 354 time.sleep(2.5) 355 cache = copykeys(sessions.RamSession.cache) 356 if cache: 357 if cache == [id2]: 358 self.fail("The second session did not time out.") 359 else: 360 self.fail("Unknown session id in cache: %r", cache)
361 362 363 import socket 364 try: 365 import memcache 366 367 host, port = '127.0.0.1', 11211 368 for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, 369 socket.SOCK_STREAM): 370 af, socktype, proto, canonname, sa = res 371 s = None 372 try: 373 s = socket.socket(af, socktype, proto) 374 # See http://groups.google.com/group/cherrypy-users/ 375 # browse_frm/thread/bbfe5eb39c904fe0 376 s.settimeout(1.0) 377 s.connect((host, port)) 378 s.close() 379 except socket.error: 380 if s: 381 s.close() 382 raise 383 break 384 except (ImportError, socket.error):
385 - class MemcachedSessionTest(helper.CPWebCase):
386 setup_server = staticmethod(setup_server) 387
388 - def test(self):
389 return self.skip("memcached not reachable ")
390 else:
391 - class MemcachedSessionTest(helper.CPWebCase):
392 setup_server = staticmethod(setup_server) 393
394 - def test_0_Session(self):
395 self.getPage('/setsessiontype/memcached') 396 397 self.getPage('/testStr') 398 self.assertBody('1') 399 self.getPage('/testGen', self.cookies) 400 self.assertBody('2') 401 self.getPage('/testStr', self.cookies) 402 self.assertBody('3') 403 self.getPage('/length', self.cookies) 404 self.assertErrorPage(500) 405 self.assertInBody("NotImplementedError") 406 self.getPage('/delkey?key=counter', self.cookies) 407 self.assertStatus(200) 408 409 # Wait for the session.timeout (1 second) 410 time.sleep(1.25) 411 self.getPage('/') 412 self.assertBody('1') 413 414 # Test session __contains__ 415 self.getPage('/keyin?key=counter', self.cookies) 416 self.assertBody("True") 417 418 # Test session delete 419 self.getPage('/delete', self.cookies) 420 self.assertBody("done")
421
422 - def test_1_Concurrency(self):
423 client_thread_count = 5 424 request_count = 30 425 426 # Get initial cookie 427 self.getPage("/") 428 self.assertBody("1") 429 cookies = self.cookies 430 431 data_dict = {} 432 433 def request(index): 434 for i in range(request_count): 435 self.getPage("/", cookies) 436 # Uncomment the following line to prove threads overlap. 437 ## sys.stdout.write("%d " % index) 438 if not self.body.isdigit(): 439 self.fail(self.body) 440 data_dict[index] = v = int(self.body)
441 442 # Start <request_count> concurrent requests from 443 # each of <client_thread_count> clients 444 ts = [] 445 for c in range(client_thread_count): 446 data_dict[c] = 0 447 t = threading.Thread(target=request, args=(c,)) 448 ts.append(t) 449 t.start() 450 451 for t in ts: 452 t.join() 453 454 hitcount = max(data_dict.values()) 455 expected = 1 + (client_thread_count * request_count) 456 self.assertEqual(hitcount, expected)
457
458 - def test_3_Redirect(self):
459 # Start a new session 460 self.getPage('/testStr') 461 self.getPage('/iredir', self.cookies) 462 self.assertBody("memcached")
463
464 - def test_5_Error_paths(self):
465 self.getPage('/unknown/page') 466 self.assertErrorPage( 467 404, "The path '/unknown/page' was not found.") 468 469 # Note: this path is *not* the same as above. The above 470 # takes a normal route through the session code; this one 471 # skips the session code's before_handler and only calls 472 # before_finalize (save) and on_end (close). So the session 473 # code has to survive calling save/close without init. 474 self.getPage('/restricted', self.cookies, method='POST') 475 self.assertErrorPage(405, response_codes[405][1])
476