rmtew avatar rmtew committed 74dfb04 Merge

Merged in the latest changes from gevent-websocket.
- The graph updating example works.
- The autobahn test suite does not work - but it works the same as the gevent-websocket version does.

Comments (0)

Files changed (16)

-This Websocket library for Gevent is written and maintained by
+threaded-websocket was forked and mangled by
+
+  Richard Tew <richard.m.tew@gmail.com>
+
+gevent-websocket is written and maintained by
 
   Jeffrey Gelens <jeffrey at noppo.pro>
 

examples/echoserver.py

+import sys
+sys.path.append("..")
+
 import os
-from gevent.pywsgi import WSGIServer
+from geventwebsocket.geventx.pywsgi import WSGIServer
 import geventwebsocket
 
 
 
 
 path = os.path.dirname(geventwebsocket.__file__)
-agent = "gevent-websocket/%s" % (geventwebsocket.__version__)
+agent = "threading-websocket/%s" % (geventwebsocket.__version__)
 print "Running %s from %s" % (agent, path)
 WSGIServer(("", 8000), echo, handler_class=geventwebsocket.WebSocketHandler).serve_forever()

examples/plot_graph.py

 """
 This example generates random data and plots a graph in the browser.
 
+
+
+
+
 Run it using Gevent directly using:
     $ python plot_grapg.py
 
         plot_graph:app
 """
 
-import gevent
+import sys
+#from stacklesslib import monkeypatch
+#monkeypatch.patch_all()
+sys.path.append(".")
+import time
+
 import os
 import random
 
-from gevent import pywsgi
+from geventwebsocket.geventx import pywsgi
 from geventwebsocket.handler import WebSocketHandler
 
 
     elif ws.path == "/data":
         for i in xrange(10000):
             ws.send("0 %s %s\n" % (i, random.random()))
-            gevent.sleep(0.1)
+            time.sleep(0.1)
 
 
 def app(environ, start_response):

geventwebsocket/__init__.py

 
 __all__ = ['WebSocketHandler', 'WebSocketError']
 
-from geventwebsocket.handler import WebSocketHandler
+from .handler import WebSocketHandler
 from geventwebsocket.websocket import WebSocketError

geventwebsocket/geventx/__init__.py

+#

geventwebsocket/geventx/baseserver.py

+"""Base class for implementing servers"""
+# Copyright (c) 2009-2010 Denis Bilenko. See LICENSE for details.
+import threading
+from threading import Event
+import socket
+import sys
+
+
+__all__ = ['BaseServer']
+
+# DUPLICATED FROM: gevent.greenlet.getfuncname.
+def getfuncname(func):
+    try:
+        six.get_method_self(func)
+        is_bound_method = True
+    except AttributeError:
+        is_bound_method = False
+
+    if not is_bound_method:
+        try:
+            funcname = func.__name__
+        except AttributeError:
+            pass
+        else:
+            if funcname != '<lambda>':
+                return funcname
+    return repr(func)
+
+
+class BaseServer(object):
+    """An abstract base class that implements some common functionality for the servers in gevent.
+
+    *listener* can either be an address that the server should bind on or a :class:`gevent.socket.socket`
+    instance that is already bound and put into listening mode. In the former case, *backlog* argument specifies the
+    length of the backlog queue. If not provided, the default (256) is used.
+
+    *spawn*, if provided, is called to create a new thread to run the handler. 
+    """
+
+    @staticmethod
+    def _spawn(func, *args, **kwargs):
+        t = threading.Thread(target=func, args=args, kwargs=kwargs)
+        t.start()
+
+    # the default backlog to use if none was provided in __init__
+    backlog = 256
+
+    reuse_addr = 1
+
+    # the default timeout that we wait for the client connections to close in stop()
+    stop_timeout = 1
+
+    def __init__(self, listener, handle=None, backlog=None, spawn='default'):
+        self._stopped_event = Event()
+        self.set_listener(listener, backlog=backlog)
+        self.set_spawn(spawn)
+        self.set_handle(handle)
+        self.started = None
+
+    def set_listener(self, listener, backlog=None):
+        if hasattr(listener, 'accept'):
+            if hasattr(listener, 'do_handshake'):
+                raise TypeError('Expected a regular socket, not SSLSocket: %r' % (listener, ))
+            if backlog is not None:
+                raise TypeError('backlog must be None when a socket instance is passed')
+            self.address = listener.getsockname()
+            self.socket = listener
+        else:
+            if not isinstance(listener, tuple):
+                raise TypeError('Expected a socket instance or an address (tuple of 2 elements): %r' % (listener, ))
+            if backlog is not None:
+                self.backlog = backlog
+            self.address = listener
+
+    def set_spawn(self, spawn):
+        if spawn == 'default':
+            self.pool = None
+            self._spawn = self._spawn
+        elif hasattr(spawn, 'spawn'):
+            self.pool = spawn
+            self._spawn = spawn.spawn
+        elif isinstance(spawn, (int, long)):
+            from gevent.pool import Pool
+            self.pool = Pool(spawn)
+            self._spawn = self.pool.spawn
+        else:
+            self.pool = None
+            self._spawn = spawn
+        if hasattr(self.pool, 'full'):
+            self.full = self.pool.full
+
+    def set_handle(self, handle):
+        if handle is not None:
+            self.handle = handle
+
+    def full(self):
+        return False
+
+    def __repr__(self):
+        return '<%s at %s %s>' % (type(self).__name__, hex(id(self)), self._formatinfo())
+
+    def __str__(self):
+        return '<%s %s>' % (type(self).__name__, self._formatinfo())
+
+    def _formatinfo(self):
+        if hasattr(self, 'socket'):
+            try:
+                fileno = self.socket.fileno()
+            except Exception:
+                ex = sys.exc_info()[1]
+                fileno = str(ex)
+            result = 'fileno=%s ' % fileno
+        else:
+            result = ''
+        try:
+            if isinstance(self.address, tuple) and len(self.address) == 2:
+                result += 'address=%s:%s' % self.address
+            else:
+                result += 'address=%s' % (self.address, )
+        except Exception:
+            ex = sys.exc_info()[1]
+            result += str(ex) or '<error>'
+        try:
+            handle = getfuncname(self.__dict__['handle'])
+        except Exception:
+            handle = None
+        if handle is not None:
+            result += ' handle=' + handle
+        return result
+
+    @property
+    def server_host(self):
+        """IP address that the server is bound to (string)."""
+        if isinstance(self.address, tuple):
+            return self.address[0]
+
+    @property
+    def server_port(self):
+        """Port that the server is bound to (an integer)."""
+        if isinstance(self.address, tuple):
+            return self.address[1]
+
+    def pre_start(self):
+        """If the user initialized the server with an address rather than socket,
+        then this function will create a socket, bind it and put it into listening mode.
+
+        It is not supposed to be called by the user, it is called by :meth:`start` before starting
+        the accept loop."""
+        if not hasattr(self, 'socket'):
+            self.socket = _tcp_listener(self.address, backlog=self.backlog, reuse_addr=self.reuse_addr)
+            self.address = self.socket.getsockname()
+        self._stopped_event.clear()
+
+    def start(self):
+        """Start accepting the connections.
+
+        If an address was provided in the constructor, then also create a socket, bind it and put it into the listening mode.
+        """
+        assert not self.started, '%s already started' % self.__class__.__name__
+        self.pre_start()
+        self.started = True
+        try:
+            self.start_accepting()
+        except:
+            self.kill()
+            raise
+
+    def close(self):
+        """Close the listener socket and stop accepting."""
+        self.started = False
+        try:
+            self.stop_accepting()
+        finally:
+            try:
+                self.socket.close()
+            except Exception:
+                pass
+            self.__dict__.pop('socket', None)
+            self.__dict__.pop('handle', None)
+
+    kill = close   # this is deprecated
+
+    def stop(self, timeout=None):
+        """Stop accepting the connections and close the listening socket.
+
+        If the server uses a pool to spawn the requests, then :meth:`stop` also waits
+        for all the handlers to exit. If there are still handlers executing after *timeout*
+        has expired (default 1 second), then the currently running handlers in the pool are killed."""
+        self.kill()
+        if timeout is None:
+            timeout = self.stop_timeout
+        self.post_stop()
+
+    def post_stop(self):
+        self._stopped_event.set()
+
+    def serve_forever(self, stop_timeout=None):
+        """Start the server if it hasn't been already started and wait until it's stopped."""
+        # add test that serve_forever exists on stop()
+        if not self.started:
+            self.start()
+        try:
+            self._stopped_event.wait()
+        except:
+            self.stop(timeout=stop_timeout)
+            raise
+
+
+def _tcp_listener(address, backlog=50, reuse_addr=None):
+    """A shortcut to create a TCP socket, bind it and put it into listening state.
+
+    The difference from :meth:`gevent.socket.tcp_listener` is that this function returns
+    an unwrapped :class:`_socket.socket` instance.
+    """
+    sock = socket.socket()
+    if reuse_addr is not None:
+        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, reuse_addr)
+    try:
+        sock.bind(address)
+    except socket.error:
+        ex = sys.exc_info()[1]
+        strerror = getattr(ex, 'strerror', None)
+        if strerror is not None:
+            ex.strerror = strerror + ': ' + repr(address)
+        raise
+    sock.listen(backlog)
+    # We want blocking! :-)
+    # sock.setblocking(0)
+    return sock

geventwebsocket/geventx/hub.py

+import sys
+
+if sys.version_info[0] >= 3:
+    basestring = (str, bytes)
+else:
+    basestring = basestring

geventwebsocket/geventx/pywsgi.py

+# Copyright (c) 2005-2009, eventlet contributors
+# Copyright (c) 2009-2011, gevent contributors
+
+gevent_version_info = (1, 0, 0, 'dev', None)
+
+import errno
+import sys
+import time
+import traceback
+import mimetools
+from datetime import datetime
+from urllib import unquote
+
+import socket
+from . import server
+from .server import StreamServer
+
+
+__all__ = ['WSGIHandler', 'WSGIServer']
+
+
+MAX_REQUEST_LINE = 8192
+# Weekday and month names for HTTP date/time formatting; always English!
+_WEEKDAYNAME = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
+_MONTHNAME = [None,  # Dummy so we can use 1-based month numbers
+              "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+              "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+_INTERNAL_ERROR_STATUS = '500 Internal Server Error'
+_INTERNAL_ERROR_BODY = 'Internal Server Error'
+_INTERNAL_ERROR_HEADERS = [('Content-Type', 'text/plain'),
+                           ('Connection', 'close'),
+                           ('Content-Length', str(len(_INTERNAL_ERROR_BODY)))]
+_REQUEST_TOO_LONG_RESPONSE = "HTTP/1.0 414 Request URI Too Long\r\nConnection: close\r\nContent-length: 0\r\n\r\n"
+_BAD_REQUEST_RESPONSE = "HTTP/1.0 400 Bad Request\r\nConnection: close\r\nContent-length: 0\r\n\r\n"
+_CONTINUE_RESPONSE = "HTTP/1.1 100 Continue\r\n\r\n"
+
+
+def format_date_time(timestamp):
+    year, month, day, hh, mm, ss, wd, _y, _z = time.gmtime(timestamp)
+    return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (_WEEKDAYNAME[wd], day, _MONTHNAME[month], year, hh, mm, ss)
+
+
+class Input(object):
+
+    def __init__(self, rfile, content_length, socket=None, chunked_input=False):
+        self.rfile = rfile
+        self.content_length = content_length
+        self.socket = socket
+        self.position = 0
+        self.chunked_input = chunked_input
+        self.chunk_length = -1
+
+    def _discard(self):
+        if self.socket is None and (self.position < (self.content_length or 0) or self.chunked_input):
+            # ## Read and discard body
+            while 1:
+                d = self.read(16384)
+                if not d:
+                    break
+
+    def _send_100_continue(self):
+        if self.socket is not None:
+            self.socket.sendall(_CONTINUE_RESPONSE)
+            self.socket = None
+
+    def _do_read(self, length=None, use_readline=False):
+        if use_readline:
+            reader = self.rfile.readline
+        else:
+            reader = self.rfile.read
+        content_length = self.content_length
+        if content_length is None:
+            # Either Content-Length or "Transfer-Encoding: chunked" must be present in a request with a body
+            # if it was chunked, then this function would have not been called
+            return ''
+        self._send_100_continue()
+        left = content_length - self.position
+        if length is None:
+            length = left
+        elif length > left:
+            length = left
+        if not length:
+            return ''
+        read = reader(length)
+        self.position += len(read)
+        if len(read) < length:
+            if (use_readline and not read.endswith("\n")) or not use_readline:
+                raise IOError("unexpected end of file while reading request at position %s" % (self.position,))
+
+        return read
+
+    def _chunked_read(self, length=None, use_readline=False):
+        rfile = self.rfile
+        self._send_100_continue()
+
+        if length == 0:
+            return ""
+
+        if length < 0:
+            length = None
+
+        if use_readline:
+            reader = self.rfile.readline
+        else:
+            reader = self.rfile.read
+
+        response = []
+        while self.chunk_length != 0:
+            maxreadlen = self.chunk_length - self.position
+            if length is not None and length < maxreadlen:
+                maxreadlen = length
+
+            if maxreadlen > 0:
+                data = reader(maxreadlen)
+                if not data:
+                    self.chunk_length = 0
+                    raise IOError("unexpected end of file while parsing chunked data")
+
+                datalen = len(data)
+                response.append(data)
+
+                self.position += datalen
+                if self.chunk_length == self.position:
+                    rfile.readline()
+
+                if length is not None:
+                    length -= datalen
+                    if length == 0:
+                        break
+                if use_readline and data[-1] == "\n":
+                    break
+            else:
+                line = rfile.readline()
+                if not line.endswith("\n"):
+                    self.chunk_length = 0
+                    raise IOError("unexpected end of file while reading chunked data header")
+                self.chunk_length = int(line.split(";", 1)[0], 16)
+                self.position = 0
+                if self.chunk_length == 0:
+                    rfile.readline()
+
+        return ''.join(response)
+
+    def read(self, length=None):
+        if self.chunked_input:
+            return self._chunked_read(length)
+        return self._do_read(length)
+
+    def readline(self, size=None):
+        if self.chunked_input:
+            return self._chunked_read(size, True)
+        else:
+            return self._do_read(size, use_readline=True)
+
+    def readlines(self, hint=None):
+        return list(self)
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        line = self.readline()
+        if not line:
+            raise StopIteration
+        return line
+
+
+class WSGIHandler(object):
+    protocol_version = 'HTTP/1.1'
+    MessageClass = mimetools.Message
+
+    def __init__(self, socket, address, server, rfile=None):
+        self.socket = socket
+        self.client_address = address
+        self.server = server
+        if rfile is None:
+            self.rfile = socket.makefile('rb', -1)
+        else:
+            self.rfile = rfile
+
+    @property
+    def wfile(self):
+        # DEPRECATED, UNTESTED, TO BE REMOVED
+        wfile = getattr(self, '_wfile', None)
+        if wfile is None:
+            wfile = self._wfile = self.socket.makefile('wb', 0)
+        return wfile
+
+    def handle(self):
+        try:
+            while self.socket is not None:
+                self.time_start = time.time()
+                self.time_finish = 0
+                result = self.handle_one_request()
+                if result is None:
+                    break
+                if result is True:
+                    continue
+                self.status, response_body = result
+                self.socket.sendall(response_body)
+                if self.time_finish == 0:
+                    self.time_finish = time.time()
+                self.log_request()
+                break
+        finally:
+            if self.socket is not None:
+                try:
+                    self.socket._sock.close()  # do not rely on garbage collection
+                    self.socket.close()
+                except socket.error:
+                    pass
+            self.__dict__.pop('socket', None)
+            self.__dict__.pop('rfile', None)
+            self.__dict__.pop('_wfile', None)  # XXX remove once wfile property is gone
+
+    def _check_http_version(self):
+        version = self.request_version
+        if not version.startswith("HTTP/"):
+            return False
+        version = tuple(int(x) for x in version[5:].split("."))  # "HTTP/"
+        if version[1] < 0 or version < (0, 9) or version >= (2, 0):
+            return False
+        return True
+
+    def read_request(self, raw_requestline):
+        self.requestline = raw_requestline.rstrip()
+        words = self.requestline.split()
+        if len(words) == 3:
+            self.command, self.path, self.request_version = words
+            if not self._check_http_version():
+                self.log_error('Invalid http version: %r', raw_requestline)
+                return
+        elif len(words) == 2:
+            self.command, self.path = words
+            if self.command != "GET":
+                self.log_error('Expected GET method: %r', raw_requestline)
+                return
+            self.request_version = "HTTP/0.9"
+            # QQQ I'm pretty sure we can drop support for HTTP/0.9
+        else:
+            self.log_error('Invalid HTTP method: %r', raw_requestline)
+            return
+
+        self.headers = self.MessageClass(self.rfile, 0)
+        if self.headers.status:
+            self.log_error('Invalid headers status: %r', self.headers.status)
+            return
+
+        if self.headers.get("transfer-encoding", "").lower() == "chunked":
+            try:
+                del self.headers["content-length"]
+            except KeyError:
+                pass
+
+        content_length = self.headers.get("content-length")
+        if content_length is not None:
+            content_length = int(content_length)
+            if content_length < 0:
+                self.log_error('Invalid Content-Length: %r', content_length)
+                return
+            if content_length and self.command in ('GET', 'HEAD'):
+                self.log_error('Unexpected Content-Length')
+                return
+
+        self.content_length = content_length
+
+        if self.request_version == "HTTP/1.1":
+            conntype = self.headers.get("Connection", "").lower()
+            if conntype == "close":
+                self.close_connection = True
+            else:
+                self.close_connection = False
+        else:
+            self.close_connection = True
+
+        return True
+
+    def log_error(self, msg, *args):
+        try:
+            message = msg % args
+        except Exception:
+            traceback.print_exc()
+            message = '%r %r' % (msg, args)
+        try:
+            message = '%s: %s' % (self.socket, message)
+        except Exception:
+            pass
+        try:
+            sys.stderr.write(message + '\n')
+        except Exception:
+            traceback.print_exc()
+
+    def read_requestline(self):
+        return self.rfile.readline(MAX_REQUEST_LINE)
+
+    def handle_one_request(self):
+        if self.rfile.closed:
+            return
+
+        try:
+            raw_requestline = self.read_requestline()
+        except socket.error:
+            # "Connection reset by peer" or other socket errors aren't interesting here
+            return
+
+        if not raw_requestline:
+            return
+
+        self.response_length = 0
+
+        if len(raw_requestline) >= MAX_REQUEST_LINE:
+            return ('414', _REQUEST_TOO_LONG_RESPONSE)
+
+        try:
+            if not self.read_request(raw_requestline):
+                return ('400', _BAD_REQUEST_RESPONSE)
+        except Exception:
+            ex = sys.exc_info()[1]
+            if not isinstance(ex, ValueError):
+                traceback.print_exc()
+            self.log_error('Invalid request: %s', str(ex) or ex.__class__.__name__)
+            return ('400', _BAD_REQUEST_RESPONSE)
+
+        self.environ = self.get_environ()
+        self.application = self.server.application
+        try:
+            self.handle_one_response()
+        except socket.error:
+            ex = sys.exc_info()[1]
+            # Broken pipe, connection reset by peer
+            if ex.args[0] in (errno.EPIPE, errno.ECONNRESET):
+                sys.exc_clear()
+                return
+            else:
+                raise
+
+        if self.close_connection:
+            return
+
+        if self.rfile.closed:
+            return
+
+        return True  # read more requests
+
+    def finalize_headers(self):
+        response_headers_list = [x[0] for x in self.response_headers]
+        if 'Date' not in response_headers_list:
+            self.response_headers.append(('Date', format_date_time(time.time())))
+
+        if self.request_version == 'HTTP/1.0' and 'Connection' not in response_headers_list:
+            self.response_headers.append(('Connection', 'close'))
+            self.close_connection = True
+        elif ('Connection', 'close') in self.response_headers:
+            self.close_connection = True
+
+        if self.code not in [204, 304]:
+            # the reply will include message-body; make sure we have either Content-Length or chunked
+            if 'Content-Length' not in response_headers_list:
+                if hasattr(self.result, '__len__'):
+                    self.response_headers.append(('Content-Length', str(sum(len(chunk) for chunk in self.result))))
+                else:
+                    if self.request_version != 'HTTP/1.0':
+                        self.response_use_chunked = True
+                        self.response_headers.append(('Transfer-Encoding', 'chunked'))
+
+    def write(self, data):
+        towrite = []
+        if not self.status:
+            raise AssertionError("The application did not call start_response()")
+        if not self.headers_sent:
+            self.headers_sent = True
+            self.finalize_headers()
+
+            towrite.append('%s %s\r\n' % (self.request_version, self.status))
+            for header in self.response_headers:
+                towrite.append('%s: %s\r\n' % header)
+
+            towrite.append('\r\n')
+
+        if data:
+            if self.response_use_chunked:
+                ## Write the chunked encoding
+                towrite.append("%x\r\n%s\r\n" % (len(data), data))
+            else:
+                towrite.append(data)
+
+        msg = ''.join(towrite)
+        try:
+            self.socket.sendall(msg)
+        except socket.error, ex:
+            self.status = 'socket error: %s' % ex
+            if self.code > 0:
+                self.code = -self.code
+            raise
+        self.response_length += len(msg)
+
+    def start_response(self, status, headers, exc_info=None):
+        if exc_info:
+            try:
+                if self.headers_sent:
+                    # Re-raise original exception if headers sent
+                    raise exc_info[0], exc_info[1], exc_info[2]
+            finally:
+                # Avoid dangling circular ref
+                exc_info = None
+        self.code = int(status.split(' ', 1)[0])
+        self.status = status
+        self.response_headers = [('-'.join([x.capitalize() for x in key.split('-')]), value) for key, value in headers]
+        return self.write
+
+    def log_request(self):
+        log = self.server.log
+        if log:
+            log.write(self.format_request() + '\n')
+
+    def format_request(self):
+        now = datetime.now().replace(microsecond=0)
+        if self.time_finish:
+            delta = '%.6f' % (self.time_finish - self.time_start)
+            length = self.response_length
+        else:
+            delta = '-'
+            if not self.response_length:
+                length = '-'
+        return '%s - - [%s] "%s" %s %s %s' % (
+            self.client_address[0],
+            now,
+            self.requestline,
+            (self.status or '000').split()[0],
+            length,
+            delta)
+
+    def process_result(self):
+        for data in self.result:
+            if data:
+                self.write(data)
+        if self.status and not self.headers_sent:
+            self.write('')
+        if self.response_use_chunked:
+            self.socket.sendall('0\r\n\r\n')
+            self.response_length += 5
+
+    def run_application(self):
+        self.result = self.application(self.environ, self.start_response)
+        self.process_result()
+
+    def handle_one_response(self):
+        self.time_start = time.time()
+        self.status = None
+        self.headers_sent = False
+
+        self.result = None
+        self.response_use_chunked = False
+        self.response_length = 0
+
+        try:
+            try:
+                self.run_application()
+            finally:
+                close = getattr(self.result, 'close', None)
+                if close is not None:
+                    close()
+                self.wsgi_input._discard()
+        except:
+            self.handle_error(*sys.exc_info())
+        finally:
+            self.time_finish = time.time()
+            self.log_request()
+
+    def handle_error(self, type, value, tb):
+        if not issubclass(type, SystemExit):
+            self.server.loop.handle_error(self.environ, type, value, tb)
+        del tb
+        if self.response_length:
+            self.close_connection = True
+        else:
+            self.start_response(_INTERNAL_ERROR_STATUS, _INTERNAL_ERROR_HEADERS)
+            self.write(_INTERNAL_ERROR_BODY)
+
+    def get_environ(self):
+        env = self.server.get_environ()
+        env['REQUEST_METHOD'] = self.command
+        env['SCRIPT_NAME'] = ''
+
+        if '?' in self.path:
+            path, query = self.path.split('?', 1)
+        else:
+            path, query = self.path, ''
+        env['PATH_INFO'] = unquote(path)
+        env['QUERY_STRING'] = query
+
+        if self.headers.typeheader is not None:
+            env['CONTENT_TYPE'] = self.headers.typeheader
+
+        length = self.headers.getheader('content-length')
+        if length:
+            env['CONTENT_LENGTH'] = length
+        env['SERVER_PROTOCOL'] = 'HTTP/1.0'
+
+        env['REMOTE_ADDR'] = self.client_address[0]
+
+        for header in self.headers.headers:
+            key, value = header.split(':', 1)
+            key = key.replace('-', '_').upper()
+            if key not in ('CONTENT_TYPE', 'CONTENT_LENGTH'):
+                value = value.strip()
+                key = 'HTTP_' + key
+                if key in env:
+                    if 'COOKIE' in key:
+                        env[key] += '; ' + value
+                    else:
+                        env[key] += ',' + value
+                else:
+                    env[key] = value
+
+        if env.get('HTTP_EXPECT') == '100-continue':
+            socket = self.socket
+        else:
+            socket = None
+        chunked = env.get('HTTP_TRANSFER_ENCODING', '').lower() == 'chunked'
+        self.wsgi_input = Input(self.rfile, self.content_length, socket=socket, chunked_input=chunked)
+        env['wsgi.input'] = self.wsgi_input
+        return env
+
+
+class WSGIServer(StreamServer):
+    """A WSGI server based on :class:`StreamServer` that supports HTTPS."""
+
+    handler_class = WSGIHandler
+    base_env = {'GATEWAY_INTERFACE': 'CGI/1.1',
+                'SERVER_SOFTWARE': 'gevent/%d.%d Python/%d.%d' % (gevent_version_info[:2] + sys.version_info[:2]),
+                'SCRIPT_NAME': '',
+                'wsgi.version': (1, 0),
+                'wsgi.multithread': False,
+                'wsgi.multiprocess': False,
+                'wsgi.run_once': False}
+
+    def __init__(self, listener, application=None, backlog=None, spawn='default', log='default', handler_class=None,
+                 environ=None, **ssl_args):
+        StreamServer.__init__(self, listener, backlog=backlog, spawn=spawn, **ssl_args)
+        if application is not None:
+            self.application = application
+        if handler_class is not None:
+            self.handler_class = handler_class
+        if log == 'default':
+            self.log = sys.stderr
+        else:
+            self.log = log
+        self.set_environ(environ)
+        self.set_max_accept()
+
+    def set_environ(self, environ=None):
+        if environ is not None:
+            self.environ = environ
+        environ_update = getattr(self, 'environ', None)
+        self.environ = self.base_env.copy()
+        if self.ssl_enabled:
+            self.environ['wsgi.url_scheme'] = 'https'
+        else:
+            self.environ['wsgi.url_scheme'] = 'http'
+        if environ_update is not None:
+            self.environ.update(environ_update)
+        if self.environ.get('wsgi.errors') is None:
+            self.environ['wsgi.errors'] = sys.stderr
+
+    def set_max_accept(self):
+        if self.max_accept is None:
+            if self.environ.get('wsgi.multiprocess'):
+                self.max_accept = 1
+            else:
+                self.max_accept = server.DEFAULT_MAX_ACCEPT
+
+    def get_environ(self):
+        return self.environ.copy()
+
+    def pre_start(self):
+        StreamServer.pre_start(self)
+        self.update_environ()
+
+    def update_environ(self):
+        address = self.address
+        if isinstance(address, tuple):
+            if 'SERVER_NAME' not in self.environ:
+                try:
+                    name = socket.getfqdn(address[0])
+                except socket.error:
+                    name = str(address[0])
+                self.environ['SERVER_NAME'] = name
+            self.environ.setdefault('SERVER_PORT', str(address[1]))
+        else:
+            self.environ.setdefault('SERVER_NAME', '')
+            self.environ.setdefault('SERVER_PORT', '')
+
+    def handle(self, socket, address):
+        handler = self.handler_class(socket, address, self)
+        handler.handle()

geventwebsocket/geventx/server.py

+# Copyright (c) 2009-2011 Denis Bilenko. See LICENSE for details.
+"""TCP/SSL server"""
+import sys
+import errno
+import socket
+import threading
+from .baseserver import BaseServer
+from errno import EWOULDBLOCK
+
+
+__all__ = ['StreamServer']
+
+
+DEFAULT_MAX_ACCEPT = 100
+
+
+class StreamServer(BaseServer):
+    """A generic TCP server. Accepts connections on a listening socket and spawns user-provided *handle*
+    for each connection with 2 arguments: the client socket and the client address.
+
+    If any of the following keyword arguments are present, then the server assumes SSL mode and uses these arguments
+    to create an SSL wrapper for the client socket before passing it to *handle*:
+
+    - keyfile
+    - certfile
+    - cert_reqs
+    - ssl_version
+    - ca_certs
+    - suppress_ragged_eofs
+    - do_handshake_on_connect
+    - ciphers
+
+    Note that although the errors in a successfully spawned handler will not affect the server or other connections,
+    the errors raised by :func:`accept` and *spawn* cause the server to stop accepting for a short amount of time. The
+    exact period depends on the values of :attr:`min_delay` and :attr:`max_delay` attributes.
+
+    The delay starts with :attr:`min_delay` and doubles with each successive error until it reaches :attr:`max_delay`.
+    A successful :func:`accept` resets the delay to :attr:`min_delay` again.
+    """
+    # Sets the maximum number of consecutive accepts that a process may perform on
+    # a single wake up. High values give higher priority to high connection rates,
+    # while lower values give higher priority to already established connections.
+    # Default is 100. Note, that in case of multiple working processes on the same
+    # listening value, it should be set to a lower value. (pywsgi.WSGIServer sets it
+    # to 1 when environ["wsgi.multiprocess"] is true)
+    max_accept = None
+
+    # the number of seconds to sleep in case there was an error in accept() call
+    # for consecutive errors the delay will double until it reaches max_delay
+    # when accept() finally succeeds the delay will be reset to min_delay again
+    min_delay = 0.01
+    max_delay = 1
+
+    def __init__(self, listener, handle=None, backlog=None, spawn='default', **ssl_args):
+        if ssl_args:
+            ssl_args.setdefault('server_side', True)
+            from .ssl import wrap_socket
+            self.wrap_socket = wrap_socket
+            self.ssl_args = ssl_args
+            self.ssl_enabled = True
+        else:
+            self.ssl_enabled = False
+        BaseServer.__init__(self, listener, handle=handle, backlog=backlog, spawn=spawn)
+        self.delay = self.min_delay
+        self._accept_event = None
+        self._start_accepting_timer = None
+
+    def set_listener(self, listener, backlog=None):
+        BaseServer.set_listener(self, listener, backlog=backlog)
+        try:
+            self.socket = self.socket._sock
+        except AttributeError:
+            pass
+
+    def set_spawn(self, spawn):
+        BaseServer.set_spawn(self, spawn)
+
+    def close(self):
+        try:
+            BaseServer.close(self)
+        finally:
+            self.__dict__.pop('_handle', None)
+
+    kill = close  # this is deprecated
+
+    def pre_start(self):
+        BaseServer.pre_start(self)
+        # make SSL work:
+        if self.ssl_enabled:
+            self._handle = self.wrap_socket_and_handle
+        else:
+            self._handle = self.handle
+
+    def start_accepting(self):
+        if self._accept_event is None:
+            if self.max_accept is None:
+                self.max_accept = DEFAULT_MAX_ACCEPT
+            self.accept_event = threading.Event()
+            t = threading.Thread(target=self._do_accept)
+            t.start()
+
+    def _start_accepting_if_started(self, _event=None):
+        if self.started:
+            self.start_accepting()
+
+    def stop_accepting(self):
+        if self._accept_event is not None:
+            self._accept_event.stop()
+            self._accept_event = None
+        if self._start_accepting_timer is not None:
+            self._start_accepting_timer.stop()
+            self._start_accepting_timer = None
+
+    def _do_accept(self):
+        for _ in xrange(self.max_accept):
+            address = None
+            try:
+                if self.full():
+                    self.stop_accepting()
+                    return
+                try:
+                    client_socket, address = self.socket.accept()
+                except socket.error, err:
+                    if err[0] == EWOULDBLOCK:
+                        return
+                    raise
+                self.delay = self.min_delay
+                # Superfluous for stacklesslib.replacements.socket module.
+                # client_socket = socket.socket(_sock=client_socket)
+                spawn = self._spawn
+                if spawn is None:
+                    self._handle(client_socket, address)
+                else:
+                    from .pywsgi import WSGIServer
+                    if isinstance(self._handle, WSGIServer):
+                        raise RuntimeError("BAD", self._handle)
+                    spawn(self._handle, client_socket, address)
+            except:
+                # TODO: Second rate fix.
+                import traceback
+                traceback.print_exc()
+                #self.loop.handle_error((address, self), *sys.exc_info())
+                ex = sys.exc_info()[1]
+                if self.is_fatal_error(ex):
+                    self.close()
+                    sys.stderr.write('ERROR: %s failed with %s\n' % (self, str(ex) or repr(ex)))
+                    return
+                if self.delay >= 0:
+                    self.stop_accepting()
+                    # TODO: Revisit this.
+                    #self._start_accepting_timer = self.loop.timer(self.delay)
+                    #self._start_accepting_timer.start(self._start_accepting_if_started)
+                    #self.delay = min(self.max_delay, self.delay * 2)
+                break
+
+    def is_fatal_error(self, ex):
+        return isinstance(ex, socket.error) and ex[0] in (errno.EBADF, errno.EINVAL, errno.ENOTSOCK)
+
+    def wrap_socket_and_handle(self, client_socket, address):
+        # used in case of ssl sockets
+        ssl_socket = self.wrap_socket(client_socket, **self.ssl_args)
+        return self.handle(ssl_socket, address)

geventwebsocket/geventx/six.py

+"""Utilities for writing code that runs on Python 2 and 3"""
+
+import operator
+import sys
+import types
+
+__author__ = "Benjamin Peterson <benjamin@python.org>"
+__version__ = "1.1.0"
+
+
+# True if we are running on Python 3.
+PY3 = sys.version_info[0] == 3
+
+if PY3:
+    string_types = str,
+    integer_types = int,
+    class_types = type,
+    text_type = str
+    binary_type = bytes
+
+    MAXSIZE = sys.maxsize
+else:
+    string_types = basestring,
+    integer_types = (int, long)
+    class_types = (type, types.ClassType)
+    text_type = unicode
+    binary_type = str
+
+    # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
+    class X(object):
+        def __len__(self):
+            return 1 << 31
+    try:
+        len(X())
+    except OverflowError:
+        # 32-bit
+        MAXSIZE = int((1 << 31) - 1)
+    else:
+        # 64-bit
+        MAXSIZE = int((1 << 63) - 1)
+    del X
+
+
+def _add_doc(func, doc):
+    """Add documentation to a function."""
+    func.__doc__ = doc
+
+
+def _import_module(name):
+    """Import module, returning the module after the last dot."""
+    __import__(name)
+    return sys.modules[name]
+
+
+class _LazyDescr(object):
+
+    def __init__(self, name):
+        self.name = name
+
+    def __get__(self, obj, tp):
+        result = self._resolve()
+        setattr(obj, self.name, result)
+        # This is a bit ugly, but it avoids running this again.
+        delattr(tp, self.name)
+        return result
+
+
+class MovedModule(_LazyDescr):
+
+    def __init__(self, name, old, new=None):
+        super(MovedModule, self).__init__(name)
+        if PY3:
+            if new is None:
+                new = name
+            self.mod = new
+        else:
+            self.mod = old
+
+    def _resolve(self):
+        return _import_module(self.mod)
+
+
+class MovedAttribute(_LazyDescr):
+
+    def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
+        super(MovedAttribute, self).__init__(name)
+        if PY3:
+            if new_mod is None:
+                new_mod = name
+            self.mod = new_mod
+            if new_attr is None:
+                if old_attr is None:
+                    new_attr = name
+                else:
+                    new_attr = old_attr
+            self.attr = new_attr
+        else:
+            self.mod = old_mod
+            if old_attr is None:
+                old_attr = name
+            self.attr = old_attr
+
+    def _resolve(self):
+        module = _import_module(self.mod)
+        return getattr(module, self.attr)
+
+
+
+class _MovedItems(types.ModuleType):
+    """Lazy loading of moved objects"""
+
+
+_moved_attributes = [
+    MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
+    MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
+    MovedAttribute("map", "itertools", "builtins", "imap", "map"),
+    MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
+    MovedAttribute("reduce", "__builtin__", "functools"),
+    MovedAttribute("StringIO", "StringIO", "io"),
+    MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
+    MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
+
+    MovedModule("builtins", "__builtin__"),
+    MovedModule("configparser", "ConfigParser"),
+    MovedModule("copyreg", "copy_reg"),
+    MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
+    MovedModule("http_cookies", "Cookie", "http.cookies"),
+    MovedModule("html_entities", "htmlentitydefs", "html.entities"),
+    MovedModule("html_parser", "HTMLParser", "html.parser"),
+    MovedModule("http_client", "httplib", "http.client"),
+    MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
+    MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
+    MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
+    MovedModule("cPickle", "cPickle", "pickle"),
+    MovedModule("queue", "Queue"),
+    MovedModule("reprlib", "repr"),
+    MovedModule("socketserver", "SocketServer"),
+    MovedModule("tkinter", "Tkinter"),
+    MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
+    MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
+    MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
+    MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
+    MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
+    MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
+    MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
+    MovedModule("tkinter_colorchooser", "tkColorChooser",
+                "tkinter.colorchooser"),
+    MovedModule("tkinter_commondialog", "tkCommonDialog",
+                "tkinter.commondialog"),
+    MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
+    MovedModule("tkinter_font", "tkFont", "tkinter.font"),
+    MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
+    MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
+                "tkinter.simpledialog"),
+    MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
+    MovedModule("winreg", "_winreg"),
+]
+for attr in _moved_attributes:
+    setattr(_MovedItems, attr.name, attr)
+del attr
+
+moves = sys.modules["six.moves"] = _MovedItems("moves")
+
+
+def add_move(move):
+    """Add an item to six.moves."""
+    setattr(_MovedItems, move.name, move)
+
+
+def remove_move(name):
+    """Remove item from six.moves."""
+    try:
+        delattr(_MovedItems, name)
+    except AttributeError:
+        try:
+            del moves.__dict__[name]
+        except KeyError:
+            raise AttributeError("no such move, %r" % (name,))
+
+
+if PY3:
+    _meth_func = "__func__"
+    _meth_self = "__self__"
+
+    _func_code = "__code__"
+    _func_defaults = "__defaults__"
+
+    _iterkeys = "keys"
+    _itervalues = "values"
+    _iteritems = "items"
+else:
+    _meth_func = "im_func"
+    _meth_self = "im_self"
+
+    _func_code = "func_code"
+    _func_defaults = "func_defaults"
+
+    _iterkeys = "iterkeys"
+    _itervalues = "itervalues"
+    _iteritems = "iteritems"
+
+
+if PY3:
+    def get_unbound_function(unbound):
+        return unbound
+
+
+    advance_iterator = next
+
+    def callable(obj):
+        return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
+else:
+    def get_unbound_function(unbound):
+        return unbound.im_func
+
+
+    def advance_iterator(it):
+        return it.next()
+
+    callable = callable
+_add_doc(get_unbound_function,
+         """Get the function out of a possibly unbound function""")
+
+
+get_method_function = operator.attrgetter(_meth_func)
+get_method_self = operator.attrgetter(_meth_self)
+get_function_code = operator.attrgetter(_func_code)
+get_function_defaults = operator.attrgetter(_func_defaults)
+
+
+def iterkeys(d):
+    """Return an iterator over the keys of a dictionary."""
+    return getattr(d, _iterkeys)()
+
+def itervalues(d):
+    """Return an iterator over the values of a dictionary."""
+    return getattr(d, _itervalues)()
+
+def iteritems(d):
+    """Return an iterator over the (key, value) pairs of a dictionary."""
+    return getattr(d, _iteritems)()
+
+
+if PY3:
+    def b(s):
+        return s.encode("latin-1")
+    def u(s):
+        return s
+    if sys.version_info[1] <= 1:
+        def int2byte(i):
+            return bytes((i,))
+    else:
+        # This is about 2x faster than the implementation above on 3.2+
+        int2byte = operator.methodcaller("to_bytes", 1, "big")
+    import io
+    StringIO = io.StringIO
+    BytesIO = io.BytesIO
+else:
+    def b(s):
+        return s
+    def u(s):
+        return unicode(s, "unicode_escape")
+    int2byte = chr
+    import StringIO
+    StringIO = BytesIO = StringIO.StringIO
+_add_doc(b, """Byte literal""")
+_add_doc(u, """Text literal""")
+
+
+if PY3:
+    import builtins
+    exec_ = getattr(builtins, "exec")
+
+
+    def reraise(tp, value, tb=None):
+        if value.__traceback__ is not tb:
+            raise value.with_traceback(tb)
+        raise value
+
+
+    print_ = getattr(builtins, "print")
+    del builtins
+
+else:
+    def exec_(code, globs=None, locs=None):
+        """Execute code in a namespace."""
+        if globs is None:
+            frame = sys._getframe(1)
+            globs = frame.f_globals
+            if locs is None:
+                locs = frame.f_locals
+            del frame
+        elif locs is None:
+            locs = globs
+        exec("""exec code in globs, locs""")
+
+
+    exec_("""def reraise(tp, value, tb=None):
+    raise tp, value, tb
+""")
+
+
+    def print_(*args, **kwargs):
+        """The new-style print function."""
+        fp = kwargs.pop("file", sys.stdout)
+        if fp is None:
+            return
+        def write(data):
+            if not isinstance(data, basestring):
+                data = str(data)
+            fp.write(data)
+        want_unicode = False
+        sep = kwargs.pop("sep", None)
+        if sep is not None:
+            if isinstance(sep, unicode):
+                want_unicode = True
+            elif not isinstance(sep, str):
+                raise TypeError("sep must be None or a string")
+        end = kwargs.pop("end", None)
+        if end is not None:
+            if isinstance(end, unicode):
+                want_unicode = True
+            elif not isinstance(end, str):
+                raise TypeError("end must be None or a string")
+        if kwargs:
+            raise TypeError("invalid keyword arguments to print()")
+        if not want_unicode:
+            for arg in args:
+                if isinstance(arg, unicode):
+                    want_unicode = True
+                    break
+        if want_unicode:
+            newline = unicode("\n")
+            space = unicode(" ")
+        else:
+            newline = "\n"
+            space = " "
+        if sep is None:
+            sep = space
+        if end is None:
+            end = newline
+        for i, arg in enumerate(args):
+            if i:
+                write(sep)
+            write(arg)
+        write(end)
+
+_add_doc(reraise, """Reraise an exception.""")
+
+
+def with_metaclass(meta, base=object):
+    """Create a base class with a metaclass."""
+    return meta("NewBase", (base,), {})

geventwebsocket/geventx/ssl.py

+# Wrapper module for _ssl. Written by Bill Janssen.
+# Ported to gevent by Denis Bilenko.
+"""SSL wrapper for socket objects.
+
+For the documentation, refer to :mod:`ssl` module manual.
+
+This module implements cooperative SSL socket wrappers.
+On Python 2.6 and newer it uses Python's native :mod:`ssl` module. On Python 2.5 and 2.4
+it requires `ssl package`_ to be installed.
+
+.. _`ssl package`: http://pypi.python.org/pypi/ssl
+"""
+
+__ssl__ = __import__('ssl')
+
+try:
+    _ssl = __ssl__._ssl
+except AttributeError:
+    _ssl = __ssl__._ssl2
+
+import sys
+import errno
+from socket import socket, _fileobject, timeout_default
+from socket import error as socket_error, EBADF
+from .hub import basestring
+
+__implements__ = ['SSLSocket',
+                  'wrap_socket',
+                  'get_server_certificate',
+                  'sslwrap_simple']
+
+__imports__ = ['SSLError',
+               'RAND_status',
+               'RAND_egd',
+               'RAND_add',
+               'cert_time_to_seconds',
+               'get_protocol_name',
+               'DER_cert_to_PEM_cert',
+               'PEM_cert_to_DER_cert']
+
+for name in __imports__[:]:
+    try:
+        value = getattr(__ssl__, name)
+        globals()[name] = value
+    except AttributeError:
+        __imports__.remove(name)
+
+for name in dir(__ssl__):
+    if not name.startswith('_'):
+        value = getattr(__ssl__, name)
+        if isinstance(value, (int, long, basestring, tuple)):
+            globals()[name] = value
+            __imports__.append(name)
+
+del name, value
+
+__all__ = __implements__ + __imports__
+
+
+class SSLSocket(socket):
+
+    def __init__(self, sock, keyfile=None, certfile=None,
+                 server_side=False, cert_reqs=CERT_NONE,
+                 ssl_version=PROTOCOL_SSLv23, ca_certs=None,
+                 do_handshake_on_connect=True,
+                 suppress_ragged_eofs=True,
+                 ciphers=None):
+        socket.__init__(self, _sock=sock)
+
+        if certfile and not keyfile:
+            keyfile = certfile
+        # see if it's connected
+        try:
+            socket.getpeername(self)
+        except socket_error, e:
+            if e[0] != errno.ENOTCONN:
+                raise
+            # no, no connection yet
+            self._sslobj = None
+        else:
+            # yes, create the SSL object
+            if ciphers is None:
+                self._sslobj = _ssl.sslwrap(self._sock, server_side,
+                                            keyfile, certfile,
+                                            cert_reqs, ssl_version, ca_certs)
+            else:
+                self._sslobj = _ssl.sslwrap(self._sock, server_side,
+                                            keyfile, certfile,
+                                            cert_reqs, ssl_version, ca_certs,
+                                            ciphers)
+            if do_handshake_on_connect:
+                self.do_handshake()
+        self.keyfile = keyfile
+        self.certfile = certfile
+        self.cert_reqs = cert_reqs
+        self.ssl_version = ssl_version
+        self.ca_certs = ca_certs
+        self.ciphers = ciphers
+        self.do_handshake_on_connect = do_handshake_on_connect
+        self.suppress_ragged_eofs = suppress_ragged_eofs
+        self._makefile_refs = 0
+
+    def read(self, len=1024):
+        """Read up to LEN bytes and return them.
+        Return zero-length string on EOF."""
+        while True:
+            try:
+                return self._sslobj.read(len)
+            except SSLError:
+                ex = sys.exc_info()[1]
+                if ex.args[0] == SSL_ERROR_EOF and self.suppress_ragged_eofs:
+                    return ''
+                elif ex.args[0] == SSL_ERROR_WANT_READ:
+                    if self.timeout == 0.0:
+                        raise
+                    sys.exc_clear()
+                    try:
+                        self._wait(self._read_event, timeout_exc=_SSLErrorReadTimeout)
+                    except socket_error:
+                        ex = sys.exc_info()[1]
+                        if ex.args[0] == EBADF:
+                            return ''
+                        raise
+                elif ex.args[0] == SSL_ERROR_WANT_WRITE:
+                    if self.timeout == 0.0:
+                        raise
+                    sys.exc_clear()
+                    try:
+                        # note: using _SSLErrorReadTimeout rather than _SSLErrorWriteTimeout below is intentional
+                        self._wait(self._write_event, timeout_exc=_SSLErrorReadTimeout)
+                    except socket_error:
+                        ex = sys.exc_info()[1]
+                        if ex.args[0] == EBADF:
+                            return ''
+                        raise
+                else:
+                    raise
+
+    def write(self, data):
+        """Write DATA to the underlying SSL channel.  Returns
+        number of bytes of DATA actually transmitted."""
+        while True:
+            try:
+                return self._sslobj.write(data)
+            except SSLError:
+                ex = sys.exc_info()[1]
+                if ex.args[0] == SSL_ERROR_WANT_READ:
+                    if self.timeout == 0.0:
+                        raise
+                    sys.exc_clear()
+                    try:
+                        self._wait(self._read_event, timeout_exc=_SSLErrorWriteTimeout)
+                    except socket_error:
+                        ex = sys.exc_info()[1]
+                        if ex.args[0] == EBADF:
+                            return 0
+                        raise
+                elif ex.args[0] == SSL_ERROR_WANT_WRITE:
+                    if self.timeout == 0.0:
+                        raise
+                    sys.exc_clear()
+                    try:
+                        self._wait(self._write_event, timeout_exc=_SSLErrorWriteTimeout)
+                    except socket_error:
+                        ex = sys.exc_info()[1]
+                        if ex.args[0] == EBADF:
+                            return 0
+                        raise
+                else:
+                    raise
+
+    def getpeercert(self, binary_form=False):
+        """Returns a formatted version of the data in the
+        certificate provided by the other end of the SSL channel.
+        Return None if no certificate was provided, {} if a
+        certificate was provided, but not validated."""
+        return self._sslobj.peer_certificate(binary_form)
+
+    def cipher(self):
+        if not self._sslobj:
+            return None
+        else:
+            return self._sslobj.cipher()
+
+    def send(self, data, flags=0, timeout=timeout_default):
+        if timeout is timeout_default:
+            timeout = self.timeout
+        if self._sslobj:
+            if flags != 0:
+                raise ValueError(
+                    "non-zero flags not allowed in calls to send() on %s" %
+                    self.__class__)
+            while True:
+                try:
+                    v = self._sslobj.write(data)
+                except SSLError:
+                    x = sys.exc_info()[1]
+                    if x.args[0] == SSL_ERROR_WANT_READ:
+                        if self.timeout == 0.0:
+                            return 0
+                        sys.exc_clear()
+                        try:
+                            self._wait(self._read_event)
+                        except socket_error:
+                            ex = sys.exc_info()[1]
+                            if ex.args[0] == EBADF:
+                                return 0
+                            raise
+                    elif x.args[0] == SSL_ERROR_WANT_WRITE:
+                        if self.timeout == 0.0:
+                            return 0
+                        sys.exc_clear()
+                        try:
+                            self._wait(self._write_event)
+                        except socket_error:
+                            ex = sys.exc_info()[1]
+                            if ex.args[0] == EBADF:
+                                return 0
+                            raise
+                    else:
+                        raise
+                else:
+                    return v
+        else:
+            return socket.send(self, data, flags, timeout)
+    # is it possible for sendall() to send some data without encryption if another end shut down SSL?
+
+    def sendto(self, *args):
+        if self._sslobj:
+            raise ValueError("sendto not allowed on instances of %s" %
+                             self.__class__)
+        else:
+            return socket.sendto(self, *args)
+
+    def recv(self, buflen=1024, flags=0):
+        if self._sslobj:
+            if flags != 0:
+                raise ValueError(
+                    "non-zero flags not allowed in calls to recv() on %s" %
+                    self.__class__)
+            # QQQ Shouldn't we wrap the SSL_WANT_READ errors as socket.timeout errors to match socket.recv's behavior?
+            return self.read(buflen)
+        else:
+            return socket.recv(self, buflen, flags)
+
+    def recv_into(self, buffer, nbytes=None, flags=0):
+        if buffer and (nbytes is None):
+            nbytes = len(buffer)
+        elif nbytes is None:
+            nbytes = 1024
+        if self._sslobj:
+            if flags != 0:
+                raise ValueError(
+                  "non-zero flags not allowed in calls to recv_into() on %s" %
+                  self.__class__)
+            while True:
+                try:
+                    tmp_buffer = self.read(nbytes)
+                    v = len(tmp_buffer)
+                    buffer[:v] = tmp_buffer
+                    return v
+                except SSLError:
+                    x = sys.exc_info()[1]
+                    if x.args[0] == SSL_ERROR_WANT_READ:
+                        sys.exc_clear()
+                        if self.timeout == 0.0:
+                            raise
+                        try:
+                            self._wait(self._read_event)
+                        except socket_error:
+                            ex = sys.exc_info()[1]
+                            if ex.args[0] == EBADF:
+                                return 0
+                            raise
+                        continue
+                    else:
+                        raise
+        else:
+            return socket.recv_into(self, buffer, nbytes, flags)
+
+    def recvfrom(self, *args):
+        if self._sslobj:
+            raise ValueError("recvfrom not allowed on instances of %s" %
+                             self.__class__)
+        else:
+            return socket.recvfrom(self, *args)
+
+    def recvfrom_into(self, *args):
+        if self._sslobj:
+            raise ValueError("recvfrom_into not allowed on instances of %s" %
+                             self.__class__)
+        else:
+            return socket.recvfrom_into(self, *args)
+
+    def pending(self):
+        if self._sslobj:
+            return self._sslobj.pending()
+        else:
+            return 0
+
+    def _sslobj_shutdown(self):
+        while True:
+            try:
+                return self._sslobj.shutdown()
+            except SSLError:
+                ex = sys.exc_info()[1]
+                if ex.args[0] == SSL_ERROR_EOF and self.suppress_ragged_eofs:
+                    return ''
+                elif ex.args[0] == SSL_ERROR_WANT_READ:
+                    if self.timeout == 0.0:
+                        raise
+                    sys.exc_clear()
+                    try:
+                        self._wait(self._read_event, timeout_exc=_SSLErrorReadTimeout)
+                    except socket_error:
+                        ex = sys.exc_info()[1]
+                        if ex.args[0] == EBADF:
+                            return ''
+                        raise
+                elif ex.args[0] == SSL_ERROR_WANT_WRITE:
+                    if self.timeout == 0.0:
+                        raise
+                    sys.exc_clear()
+                    try:
+                        self._wait(self._write_event, timeout_exc=_SSLErrorWriteTimeout)
+                    except socket_error:
+                        ex = sys.exc_info()[1]
+                        if ex.args[0] == EBADF:
+                            return ''
+                        raise
+                else:
+                    raise
+
+    def unwrap(self):
+        if self._sslobj:
+            s = self._sslobj_shutdown()
+            self._sslobj = None
+            return socket(_sock=s)
+        else:
+            raise ValueError("No SSL wrapper around " + str(self))
+
+    def shutdown(self, how):
+        self._sslobj = None
+        socket.shutdown(self, how)
+
+    def close(self):
+        if self._makefile_refs < 1:
+            self._sslobj = None
+            socket.close(self)
+        else:
+            self._makefile_refs -= 1
+
+    def do_handshake(self):
+        """Perform a TLS/SSL handshake."""
+        while True:
+            try:
+                return self._sslobj.do_handshake()
+            except SSLError:
+                ex = sys.exc_info()[1]
+                if ex.args[0] == SSL_ERROR_WANT_READ:
+                    if self.timeout == 0.0:
+                        raise
+                    sys.exc_clear()
+                    self._wait(self._read_event, timeout_exc=_SSLErrorHandshakeTimeout)
+                elif ex.args[0] == SSL_ERROR_WANT_WRITE:
+                    if self.timeout == 0.0:
+                        raise
+                    sys.exc_clear()
+                    self._wait(self._write_event, timeout_exc=_SSLErrorHandshakeTimeout)
+                else:
+                    raise
+
+    def connect(self, addr):
+        """Connects to remote ADDR, and then wraps the connection in
+        an SSL channel."""
+        # Here we assume that the socket is client-side, and not
+        # connected at the time of the call.  We connect it, then wrap it.
+        if self._sslobj:
+            raise ValueError("attempt to connect already-connected SSLSocket!")
+        socket.connect(self, addr)
+        if self.ciphers is None:
+            self._sslobj = _ssl.sslwrap(self._sock, False, self.keyfile, self.certfile,
+                                        self.cert_reqs, self.ssl_version,
+                                        self.ca_certs)
+        else:
+            self._sslobj = _ssl.sslwrap(self._sock, False, self.keyfile, self.certfile,
+                                        self.cert_reqs, self.ssl_version,
+                                        self.ca_certs, self.ciphers)
+        if self.do_handshake_on_connect:
+            self.do_handshake()
+
+    def accept(self):
+        """Accepts a new connection from a remote client, and returns
+        a tuple containing that new connection wrapped with a server-side
+        SSL channel, and the address of the remote client."""
+        newsock, addr = socket.accept(self)
+        return (SSLSocket(newsock._sock,
+                          keyfile=self.keyfile,
+                          certfile=self.certfile,
+                          server_side=True,
+                          cert_reqs=self.cert_reqs,
+                          ssl_version=self.ssl_version,
+                          ca_certs=self.ca_certs,
+                          do_handshake_on_connect=self.do_handshake_on_connect,
+                          suppress_ragged_eofs=self.suppress_ragged_eofs,
+                          ciphers=self.ciphers),
+                addr)
+
+    def makefile(self, mode='r', bufsize=-1):
+        """Make and return a file-like object that
+        works with the SSL connection.  Just use the code
+        from the socket module."""
+        self._makefile_refs += 1
+        # close=True so as to decrement the reference count when done with
+        # the file-like object.
+        return _fileobject(self, mode, bufsize, close=True)
+
+
+_SSLErrorReadTimeout = SSLError('The read operation timed out')
+_SSLErrorWriteTimeout = SSLError('The write operation timed out')
+_SSLErrorHandshakeTimeout = SSLError('The handshake operation timed out')
+
+
+def wrap_socket(sock, keyfile=None, certfile=None,
+                server_side=False, cert_reqs=CERT_NONE,
+                ssl_version=PROTOCOL_SSLv23, ca_certs=None,
+                do_handshake_on_connect=True,
+                suppress_ragged_eofs=True, ciphers=None):
+    """Create a new :class:`SSLSocket` instance."""
+    return SSLSocket(sock, keyfile=keyfile, certfile=certfile,
+                     server_side=server_side, cert_reqs=cert_reqs,
+                     ssl_version=ssl_version, ca_certs=ca_certs,
+                     do_handshake_on_connect=do_handshake_on_connect,
+                     suppress_ragged_eofs=suppress_ragged_eofs,
+                     ciphers=ciphers)
+
+
+def get_server_certificate(addr, ssl_version=PROTOCOL_SSLv3, ca_certs=None):
+    """Retrieve the certificate from the server at the specified address,
+    and return it as a PEM-encoded string.
+    If 'ca_certs' is specified, validate the server cert against it.
+    If 'ssl_version' is specified, use it in the connection attempt."""
+
+    host, port = addr
+    if (ca_certs is not None):
+        cert_reqs = CERT_REQUIRED
+    else:
+        cert_reqs = CERT_NONE
+    s = wrap_socket(socket(), ssl_version=ssl_version,
+                    cert_reqs=cert_reqs, ca_certs=ca_certs)
+    s.connect(addr)
+    dercert = s.getpeercert(True)
+    s.close()
+    return DER_cert_to_PEM_cert(dercert)
+
+
+def sslwrap_simple(sock, keyfile=None, certfile=None):
+    """A replacement for the old socket.ssl function.  Designed
+    for compability with Python 2.5 and earlier.  Will disappear in
+    Python 3.0."""
+    return SSLSocket(sock, keyfile, certfile)

geventwebsocket/handler.py

 from socket import error as socket_error
 from urllib import quote
 
-from gevent.pywsgi import WSGIHandler
-from geventwebsocket.websocket import WebSocketHybi, WebSocketHixie
+from .geventx.pywsgi import WSGIHandler
+from .websocket import WebSocketHybi, WebSocketHixie
 
 
 class WebSocketHandler(WSGIHandler):

geventwebsocket/websocket.py

 import struct
 
 from errno import EINTR
-from gevent.coros import Semaphore
+from threading import Semaphore
 
 from python_fixes import makefile, is_closed
 from exceptions import FrameTooLargeException, WebSocketError
+c:\python27\python examples\test.py -v
+pause
+c:\python27\python tests\test__websocket.py -v
 from setuptools import setup, find_packages
 
 setup(
-    name="gevent-websocket",
+    name="threaded-websocket",
     version="0.3.0",
-    description="Websocket handler for the gevent pywsgi server, a Python network library",
+    description="Websocket handler for the standard library",
     long_description=open("README.rst").read(),
-    author="Jeffrey Gelens",
-    author_email="jeffrey@noppo.pro",
+    author="Richard Tew",
+    author_email="richard.m.tew@gmail.com",
     license="BSD",
-    url="https://bitbucket.org/Jeffrey/gevent-websocket",
-    download_url="https://bitbucket.org/Jeffrey/gevent-websocket",
-    install_requires=("gevent", "greenlet"),
+    #url="https://bitbucket.org/Jeffrey/gevent-websocket",
+    #download_url="https://bitbucket.org/Jeffrey/gevent-websocket",
+    install_requires=(),
     packages=find_packages(exclude=["examples","tests"]),
     classifiers=[
         "Development Status :: 4 - Beta",
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.