Skip to content

Commit 05f28dd

Browse files
committed
Added session implementation
1 parent 0804a94 commit 05f28dd

6 files changed

Lines changed: 210 additions & 0 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*.swp
2+
*.swo
3+
*.pyc
4+
pyenv

README.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
========
2+
TornadIO
3+
========
4+
5+
Credits
6+
-------
7+
Authors of SocketTornad.IO project:
8+
- Brendan W. McAdams bwmcadams@evilmonkeylabs.com
9+
- `Matt Swanson <http://github.com/swanson>`_

flashpolicy.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0"?>
2+
<!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">
3+
<cross-domain-policy>
4+
<allow-access-from domain="*" to-ports="*" />
5+
</cross-domain-policy>
6+

setup.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/usr/bin/env python
2+
3+
try:
4+
from setuptools import setup, find_packages
5+
except ImportError:
6+
from distribute_setup import use_setuptools
7+
use_setuptools()
8+
from setuptools import setup, find_packages
9+
10+
try:
11+
license = open('LICENSE').read()
12+
except:
13+
license = None
14+
15+
try:
16+
readme = open('README.rst').read()
17+
except:
18+
readme = None
19+
20+
setup(
21+
name='TornadIO',
22+
version='0.0.1',
23+
author='Serge S. Koval',
24+
author_email='serge@in-square.net',
25+
packages=['tornadio'],
26+
scripts=[],
27+
url='http://github.com/MrJoes/tornadio/',
28+
license=license,
29+
description='Socket.io server implementation on top of Tornado framework',
30+
long_description=readme,
31+
requires=['simplejson', 'tornado'],
32+
install_requires=[
33+
'simplejson >= 2.1.0',
34+
'tornado >= 1.1.0'
35+
]
36+
)
File renamed without changes.

tornadio/session.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
from heapq import heappush, heappop, heapify
2+
from time import time, sleep, clock
3+
from hashlib import md5
4+
from random import random, randint
5+
6+
class Session(object):
7+
def __init__(self, session_id, expiry=None, on_delete=None):
8+
self.session_id = session_id
9+
self._items = dict()
10+
self.promoted = None
11+
self.expiry = expiry
12+
self.on_delete = on_delete
13+
if self.expiry is not None:
14+
self.expiry_date = time() + self.expiry
15+
16+
def promote(self):
17+
if self.expiry is not None:
18+
self.promoted = time() + self.expiry
19+
20+
def _item_deleted(self):
21+
self.promoted = -1
22+
23+
if self.on_delete is not None:
24+
self.on_delete()
25+
26+
def get(self, key, default=None):
27+
return self._items.get(key, default)
28+
29+
def set(self, key, value):
30+
self._items[key] = value
31+
32+
def __cmp__(self, other):
33+
return cmp(self.expiry_date, other.expiry_date)
34+
35+
def __repr__(self):
36+
return '%f %s %d' % (getattr(self, 'expiry_date', -1), self.session_id, self.promoted or 0)
37+
38+
class SessionManager(object):
39+
_items = dict()
40+
_queue = []
41+
42+
@classmethod
43+
def _random_key(cls):
44+
m = md5()
45+
m.update('%s%s' % (random(), time()))
46+
return m.hexdigest()
47+
48+
@classmethod
49+
def create(cls, expiry=None, on_delete=None):
50+
session = Session(cls._random_key(), expiry, on_delete)
51+
cls._items[session.session_id] = session
52+
53+
if expiry is not None:
54+
heappush(cls._queue, session)
55+
56+
return session
57+
58+
@classmethod
59+
def get(cls, session_id, promote=True):
60+
session = cls._items.get(session_id, None)
61+
62+
if session is not None and promote:
63+
session.promote()
64+
65+
return session
66+
67+
@classmethod
68+
def remove(cls, session_id):
69+
session = cls._items.get(session_id, None)
70+
71+
if session is not None:
72+
del cls._items[session_id]
73+
session._item_deleted()
74+
return True
75+
76+
return False
77+
78+
@classmethod
79+
def expire(cls, current_time=None):
80+
if not cls._queue:
81+
return
82+
83+
if current_time is None:
84+
current_time = time()
85+
86+
while cls._queue:
87+
# Top most item is not expired yet
88+
top = cls._queue[0]
89+
90+
# Early exit if item was not promoted and its expiration time
91+
# is greater than now.
92+
if top.promoted is None and top.expiry_date > current_time:
93+
break
94+
95+
# Pop item from the stack
96+
top = heappop(cls._queue)
97+
98+
# If item is promoted and expiration time somewhere in future
99+
# just reschedule it
100+
if top.promoted is not None and top.promoted > current_time:
101+
top.current_time = top.promoted
102+
top.promoted = None
103+
heappush(cls._queue, top)
104+
else:
105+
# Otherwise - remove session
106+
del cls._items[top.session_id]
107+
top._item_deleted()
108+
109+
def test():
110+
while True:
111+
for j in xrange(0, 1000):
112+
op = randint(0, 5)
113+
114+
if op == 0:
115+
SessionManager.create(randint(1, 10))
116+
elif op == 1 and SessionManager._queue:
117+
idx = randint(0, len(SessionManager._queue) - 1)
118+
item = SessionManager._queue[idx]
119+
item.promote()
120+
sleep(randint(0,2))
121+
122+
t = time()
123+
124+
print 'Before: %d' % (len(SessionManager._queue))
125+
126+
start = clock()
127+
128+
SessionManager.expire(t)
129+
130+
delta = clock() - start
131+
132+
print 'Queue size: %d, %f' % (len(SessionManager._queue), delta)
133+
134+
nl = []
135+
idx = 0
136+
errored = False
137+
while SessionManager._queue:
138+
x = heappop(SessionManager._queue)
139+
140+
if t > x.expiry_date:
141+
print 'Error: (%d) %f vs %s' % (idx, t, x)
142+
errored = True
143+
144+
nl.append(x)
145+
idx += 1;
146+
147+
if errored:
148+
import pdb
149+
pdb.set_trace()
150+
151+
heapify(nl)
152+
SessionManager._queue = nl
153+
154+
if __name__ == "__main__":
155+
test()

0 commit comments

Comments
 (0)