PageRenderTime 25ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/examples/networking/socketlibevent.py

http://stacklessexamples.googlecode.com/
Python | 247 lines | 170 code | 43 blank | 34 comment | 29 complexity | 7650298f582a49fba8fb0e66361c0598 MD5 | raw file
  1. # socketlibevent.py - MIT License
  2. # phoenix@burninglabs.com
  3. #
  4. # Non-blocking socket I/O for Stackless Python using libevent, via pyevent.
  5. #
  6. # Usage:
  7. # import sys, socketlibevent; sys.modules['socket'] = socketlibevent
  8. #
  9. # Based on Richard Tew's stacklesssocket module.
  10. # Uses Dug Song's pyevent.
  11. #
  12. # Thanks a Heap !
  13. import stackless, sys, time, traceback
  14. from weakref import WeakValueDictionary
  15. import socket as stdsocket
  16. from socket import _fileobject
  17. try:
  18. import event
  19. except:
  20. try:
  21. import rel; rel.override()
  22. import event
  23. except:
  24. print "please install libevent and pyevent"
  25. # http://code.google.com/p/pyevent/
  26. print "(or 'stackless ez_setup.py rel' for quick testing)"
  27. # http://code.google.com/p/registeredeventlistener/
  28. sys.exit()
  29. # For SSL support, this module uses the 'ssl' module (built in from 2.6 up):
  30. # ('back-port' for Python < 2.6: http://pypi.python.org/pypi/ssl/)
  31. try:
  32. import ssl as ssl_
  33. ssl_enabled = True
  34. except:
  35. ssl_enabled = False
  36. # Nice socket globals import ripped from Minor Gordon's Yield.
  37. if "__all__" in stdsocket.__dict__:
  38. __all__ = stdsocket.__dict__["__all__"]
  39. globals().update((key, value) for key, value in\
  40. stdsocket.__dict__.iteritems() if key in __all__ or key == "EBADF")
  41. else:
  42. other_keys = ("error", "timeout", "getaddrinfo")
  43. globals().update((key, value) for key, value in\
  44. stdsocket.__dict__.iteritems() if key.upper() == key or key in\
  45. other_keys)
  46. _GLOBAL_DEFAULT_TIMEOUT = 0.1
  47. # simple decorator to run a function in a tasklet
  48. def tasklet(task):
  49. def run(*args, **kwargs):
  50. stackless.tasklet(task)(*args, **kwargs)
  51. return run
  52. # Event Loop Management
  53. loop_running = False
  54. sockets = WeakValueDictionary()
  55. def die():
  56. global sockets
  57. sockets = {}
  58. sys.exit()
  59. @tasklet
  60. def eventLoop():
  61. global loop_running
  62. global event_errors
  63. while sockets.values():
  64. # If there are other tasklets scheduled:
  65. # use the nonblocking loop
  66. # else: use the blocking loop
  67. if stackless.getruncount() > 2: # main tasklet + this one
  68. event.loop(True)
  69. else:
  70. event.loop(False)
  71. stackless.schedule()
  72. loop_running = False
  73. def runEventLoop():
  74. global loop_running
  75. if not loop_running:
  76. event.init()
  77. event.signal(2, die)
  78. event.signal(3, die)
  79. eventLoop()
  80. loop_running = True
  81. # Replacement Socket Module Functions
  82. def socket(family=AF_INET, type=SOCK_STREAM, proto=0):
  83. return evsocket(stdsocket.socket(family, type, proto))
  84. def create_connection(address, timeout=0.1):
  85. s = socket()
  86. s.connect(address, timeout)
  87. return s
  88. def ssl(sock, keyfile=None, certfile=None):
  89. if ssl_enabled:
  90. return evsocketssl(sock, keyfile, certfile)
  91. else:
  92. raise RuntimeError(\
  93. "SSL requires the 'ssl' module: 'http://pypi.python.org/pypi/ssl/'")
  94. # Socket Proxy Class
  95. class evsocket():
  96. # XXX Not all socketobject methods are implemented!
  97. # XXX Currently, the sockets are using the default, blocking mode.
  98. def __init__(self, sock):
  99. self.sock = sock
  100. self.accepting = False
  101. self.connected = False
  102. self.remote_addr = None
  103. self.fileobject = None
  104. self.read_channel = stackless.channel()
  105. self.write_channel = stackless.channel()
  106. self.accept_channel = None
  107. global sockets
  108. sockets[id(self)] = self
  109. runEventLoop()
  110. def __getattr__(self, attr):
  111. return getattr(self.sock, attr)
  112. def listen(self, backlog=255):
  113. self.accepting = True
  114. return self.sock.listen(backlog)
  115. def accept(self):
  116. if not self.accept_channel:
  117. self.accept_channel = stackless.channel()
  118. event.event(self.handle_accept, handle=self.sock,
  119. evtype=event.EV_READ | event.EV_PERSIST).add()
  120. return self.accept_channel.receive()
  121. @tasklet
  122. def handle_accept(self, ev, sock, event_type, *arg):
  123. s, a = self.sock.accept()
  124. s.setsockopt(stdsocket.SOL_SOCKET, stdsocket.SO_REUSEADDR, 1)
  125. s = evsocket(s)
  126. self.accept_channel.send((s,a))
  127. def connect(self, address, timeout=0.1):
  128. endtime = time.time() + timeout
  129. while time.time() < endtime:
  130. if self.sock.connect_ex(address) == 0:
  131. self.connected = True
  132. self.remote_addr = address
  133. return
  134. if not self.connected:
  135. # One last try, just to raise an error
  136. return self.sock.connect(address)
  137. def send(self, data, *args):
  138. event.write(self.sock, self.handle_send, data)
  139. return self.write_channel.receive()
  140. @tasklet
  141. def handle_send(self, data):
  142. self.write_channel.send(self.sock.send(data))
  143. def sendall(self, data, *args):
  144. while data:
  145. try:
  146. sent = self.send(data)
  147. data = data[sent + 1:]
  148. except:
  149. raise
  150. return None
  151. def recv(self, bytes, *args):
  152. event.read(self.sock, self.handle_recv, bytes)
  153. return self.read_channel.receive()
  154. @tasklet
  155. def handle_recv(self, bytes):
  156. self.read_channel.send(self.sock.recv(bytes))
  157. def recvfrom(self, bytes, *args):
  158. event.read(self.sock, self.handle_recv, bytes)
  159. return self.read_channel.receive()
  160. @tasklet
  161. def handle_recvfrom(self, bytes):
  162. self.read_channel.send(self.sock.recvfrom(bytes))
  163. def makefile(self, mode='r', bufsize=-1):
  164. self.fileobject = stdsocket._fileobject(self, mode, bufsize)
  165. return self.fileobject
  166. def close(self):
  167. # XXX Stupid workaround
  168. # Don't close while the fileobject is still using the fakesocket
  169. def _close():
  170. while self.fileobject._sock == self:
  171. stackless.schedule()
  172. self._sock.close()
  173. del sockets[id(self)]
  174. if self.fileobject:
  175. stackless.tasklet(_close)()
  176. # SSL Proxy Class
  177. class evsocketssl(evsocket):
  178. def __init__(self, sock, keyfile=None, certfile=None):
  179. if certfile:
  180. server_side = True
  181. else:
  182. server_side = False
  183. # XXX This currently performs a BLOCKING handshake operation
  184. # TODO Implement a non-blocking handshake
  185. self.sock = ssl_.wrap_socket(sock, keyfile, certfile, server_side)
  186. @tasklet
  187. def handle_accept(self, ev, sock, event_type, *arg):
  188. s, a = self.sock.accept()
  189. s.setsockopt(stdsocket.SOL_SOCKET, stdsocket.SO_REUSEADDR, 1)
  190. s.setsockopt(stdsocket.IPPROTO_TCP, stdsocket.TCP_NODELAY, 1)
  191. s = evsocketssl(s)
  192. self.accept_channel.send((s,a))
  193. if __name__ == "__main__":
  194. sys.modules["socket"] = __import__(__name__)
  195. # Minimal Client Test
  196. # TODO: Add a Minimal Server Test
  197. import urllib2
  198. @tasklet
  199. def test(i):
  200. print "url read", i
  201. print urllib2.urlopen("http://www.google.com").read(12)
  202. for i in range(5):
  203. test(i)
  204. stackless.run()