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

Source Code for Module cherrypy.test.test_session

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