summaryrefslogtreecommitdiff
path: root/game/python-extra
diff options
context:
space:
mode:
authorJesusaves <cpntb1@ymail.com>2020-12-17 02:50:12 -0300
committerJesusaves <cpntb1@ymail.com>2020-12-17 02:50:12 -0300
commite1793335a756f491593a3f36e3d6b6eee2c7a005 (patch)
tree5a39ccb2a59dc3db6d582a2763ee72649f4b2c8d /game/python-extra
parentc707470f1e3b5ddfe82ef09d9b79905d09684ebe (diff)
downloadclient-e1793335a756f491593a3f36e3d6b6eee2c7a005.tar.gz
client-e1793335a756f491593a3f36e3d6b6eee2c7a005.tar.bz2
client-e1793335a756f491593a3f36e3d6b6eee2c7a005.tar.xz
client-e1793335a756f491593a3f36e3d6b6eee2c7a005.zip
Replace websocket-client with ws4py
Both are dead but well, Python 2.7 is dead, soooo
Diffstat (limited to 'game/python-extra')
-rw-r--r--game/python-extra/enum.py241
-rw-r--r--game/python-extra/six.py982
-rw-r--r--game/python-extra/websock/__init__.py29
-rw-r--r--game/python-extra/websock/_abnf.py447
-rw-r--r--game/python-extra/websock/_app.py352
-rw-r--r--game/python-extra/websock/_cookiejar.py52
-rw-r--r--game/python-extra/websock/_core.py522
-rw-r--r--game/python-extra/websock/_exceptions.py88
-rw-r--r--game/python-extra/websock/_handshake.py211
-rw-r--r--game/python-extra/websock/_http.py330
-rw-r--r--game/python-extra/websock/_logging.py84
-rw-r--r--game/python-extra/websock/_socket.py166
-rw-r--r--game/python-extra/websock/_ssl_compat.py54
-rw-r--r--game/python-extra/websock/_url.py164
-rw-r--r--game/python-extra/websock/_utils.py111
-rw-r--r--game/python-extra/websock/tests/data/header01.txt6
-rw-r--r--game/python-extra/websock/tests/data/header02.txt6
-rw-r--r--game/python-extra/websock/tests/test_cookiejar.py98
-rw-r--r--game/python-extra/websock/tests/test_websocket.py665
-rw-r--r--game/python-extra/ws4py/__init__.py71
-rw-r--r--game/python-extra/ws4py/async_websocket.py126
-rw-r--r--game/python-extra/ws4py/client/__init__.py344
-rw-r--r--game/python-extra/ws4py/client/geventclient.py96
-rw-r--r--game/python-extra/ws4py/client/threadedclient.py98
-rw-r--r--game/python-extra/ws4py/client/tornadoclient.py155
-rw-r--r--game/python-extra/ws4py/compat.py46
-rw-r--r--game/python-extra/ws4py/exc.py27
-rw-r--r--game/python-extra/ws4py/framing.py273
-rw-r--r--game/python-extra/ws4py/manager.py368
-rw-r--r--game/python-extra/ws4py/messaging.py169
-rw-r--r--game/python-extra/ws4py/server/__init__.py (renamed from game/python-extra/websock/tests/__init__.py)0
-rw-r--r--game/python-extra/ws4py/server/cherrypyserver.py382
-rw-r--r--game/python-extra/ws4py/server/geventserver.py121
-rw-r--r--game/python-extra/ws4py/server/tulipserver.py224
-rw-r--r--game/python-extra/ws4py/server/wsgirefserver.py157
-rw-r--r--game/python-extra/ws4py/server/wsgiutils.py162
-rw-r--r--game/python-extra/ws4py/streaming.py319
-rw-r--r--game/python-extra/ws4py/utf8validator.py117
-rw-r--r--game/python-extra/ws4py/websocket.py615
39 files changed, 3870 insertions, 4608 deletions
diff --git a/game/python-extra/enum.py b/game/python-extra/enum.py
deleted file mode 100644
index 94020fb..0000000
--- a/game/python-extra/enum.py
+++ /dev/null
@@ -1,241 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# enum.py
-# Part of ‘enum’, a package providing enumerated types for Python.
-#
-# Copyright © 2007–2018 Ben Finney <ben+python@benfinney.id.au>
-# This is free software: you may copy, modify, and/or distribute this work
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation; version 3 of that license or any later version.
-# No warranty expressed or implied. See the file ‘LICENSE.GPL-3’ for details.
-
-""" Robust enumerated type support in Python.
-
-This package provides a module for robust enumerations in Python.
-
-An enumeration object is created with a sequence of string arguments
-to the Enum() constructor::
-
- >>> from enum import Enum
- >>> Colours = Enum('red', 'blue', 'green')
- >>> Weekdays = Enum('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun')
-
-The return value is an immutable sequence object with a value for each
-of the string arguments. Each value is also available as an attribute
-named from the corresponding string argument::
-
- >>> pizza_night = Weekdays[4]
- >>> shirt_colour = Colours.green
-
-The values are constants that can be compared only with values from
-the same enumeration; comparison with other values will invoke
-Python's fallback comparisons::
-
- >>> pizza_night == Weekdays.fri
- True
- >>> shirt_colour > Colours.red
- True
- >>> shirt_colour == "green"
- False
-
-Each value from an enumeration exports its sequence index
-as an integer, and can be coerced to a simple string matching the
-original arguments used to create the enumeration::
-
- >>> str(pizza_night)
- 'fri'
- >>> shirt_colour.index
- 2
-
-"""
-
-
-__author_name__ = "Ben Finney"
-__author_email__ = "ben+python@benfinney.id.au"
-__author__ = "%(__author_name__)s <%(__author_email__)s>" % vars()
-
-_copyright_year_begin = "2007"
-__date__ = "2018-08-22"
-_copyright_year_latest = __date__.split('-')[0]
-_copyright_year_range = _copyright_year_begin
-if _copyright_year_latest > _copyright_year_begin:
- _copyright_year_range += "–%(_copyright_year_latest)s" % vars()
-__copyright__ = (
- "Copyright © %(_copyright_year_range)s"
- " %(__author_name__)s") % vars()
-__license__ = "GPL-3.0+"
-
-__url__ = "https://pypi.org/project/enum/"
-__version__ = "0.4.7"
-
-
-class EnumException(Exception):
- """ Base class for all exceptions in this module. """
-
- def __init__(self, *args, **kwargs):
- if self.__class__ is EnumException:
- class_name = self.__class__.__name__
- raise NotImplementedError(
- "%(class_name)s is an abstract base class" % vars())
- super(EnumException, self).__init__(*args, **kwargs)
-
-
-class EnumEmptyError(AssertionError, EnumException):
- """ Raised when attempting to create an empty enumeration. """
-
- def __str__(self):
- return "Enumerations cannot be empty"
-
-
-class EnumBadKeyError(TypeError, EnumException):
- """ Raised when creating an Enum with non-string keys. """
-
- def __init__(self, key):
- self.key = key
-
- def __str__(self):
- return "Enumeration keys must be strings: %(key)r" % vars(self)
-
-
-class EnumImmutableError(TypeError, EnumException):
- """ Raised when attempting to modify an Enum. """
-
- def __init__(self, *args):
- self.args = args
-
- def __str__(self):
- return "Enumeration does not allow modification"
-
-
-def _comparator(func):
- """ Decorator for EnumValue rich comparison methods. """
- def comparator_wrapper(self, other):
- try:
- assert self.enumtype == other.enumtype
- result = func(self.index, other.index)
- except (AssertionError, AttributeError):
- result = NotImplemented
-
- return result
- comparator_wrapper.__name__ = func.__name__
- comparator_wrapper.__doc__ = getattr(float, func.__name__).__doc__
- return comparator_wrapper
-
-class EnumValue(object):
- """ A specific value of an enumerated type. """
-
- def __init__(self, enumtype, index, key):
- """ Set up a new instance. """
- self._enumtype = enumtype
- self._index = index
- self._key = key
-
- @property
- def enumtype(self):
- return self._enumtype
-
- @property
- def key(self):
- return self._key
-
- def __str__(self):
- return str(self.key)
-
- @property
- def index(self):
- return self._index
-
- def __repr__(self):
- return "EnumValue(%(_enumtype)r, %(_index)r, %(_key)r)" % vars(self)
-
- def __hash__(self):
- return hash(self._index)
-
- @_comparator
- def __eq__(self, other):
- return (self == other)
-
- @_comparator
- def __ne__(self, other):
- return (self != other)
-
- @_comparator
- def __lt__(self, other):
- return (self < other)
-
- @_comparator
- def __le__(self, other):
- return (self <= other)
-
- @_comparator
- def __gt__(self, other):
- return (self > other)
-
- @_comparator
- def __ge__(self, other):
- return (self >= other)
-
-
-class Enum(object):
- """ Enumerated type. """
-
- def __init__(self, *keys, **kwargs):
- """ Create an enumeration instance. """
-
- value_type = kwargs.get('value_type', EnumValue)
-
- if not keys:
- raise EnumEmptyError()
-
- keys = tuple(keys)
- values = [None] * len(keys)
-
- for i, key in enumerate(keys):
- value = value_type(self, i, key)
- values[i] = value
- try:
- super(Enum, self).__setattr__(key, value)
- except TypeError:
- raise EnumBadKeyError(key)
-
- self.__dict__['_keys'] = keys
- self.__dict__['_values'] = values
-
- def __setattr__(self, name, value):
- raise EnumImmutableError(name)
-
- def __delattr__(self, name):
- raise EnumImmutableError(name)
-
- def __len__(self):
- return len(self._values)
-
- def __getitem__(self, index):
- return self._values[index]
-
- def __setitem__(self, index, value):
- raise EnumImmutableError(index)
-
- def __delitem__(self, index):
- raise EnumImmutableError(index)
-
- def __iter__(self):
- return iter(self._values)
-
- def __contains__(self, value):
- is_member = False
- if isinstance(value, basestring):
- is_member = (value in self._keys)
- else:
- is_member = (value in self._values)
- return is_member
-
-
-# Local variables:
-# mode: python
-# time-stamp-format: "%:y-%02m-%02d"
-# time-stamp-start: "__date__ = \""
-# time-stamp-end: "\"$"
-# time-stamp-line-limit: 200
-# End:
-# vim: filetype=python fileencoding=utf-8 :
diff --git a/game/python-extra/six.py b/game/python-extra/six.py
deleted file mode 100644
index 83f6978..0000000
--- a/game/python-extra/six.py
+++ /dev/null
@@ -1,982 +0,0 @@
-# Copyright (c) 2010-2020 Benjamin Peterson
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-
-"""Utilities for writing code that runs on Python 2 and 3"""
-
-from __future__ import absolute_import
-
-import functools
-import itertools
-import operator
-import sys
-import types
-
-__author__ = "Benjamin Peterson <benjamin@python.org>"
-__version__ = "1.15.0"
-
-
-# Useful for very coarse version differentiation.
-PY2 = sys.version_info[0] == 2
-PY3 = sys.version_info[0] == 3
-PY34 = sys.version_info[0:2] >= (3, 4)
-
-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
-
- if sys.platform.startswith("java"):
- # Jython always uses 32 bits.
- MAXSIZE = int((1 << 31) - 1)
- else:
- # 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) # Invokes __set__.
- try:
- # This is a bit ugly, but it avoids running this again by
- # removing this descriptor.
- delattr(obj.__class__, self.name)
- except AttributeError:
- pass
- 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)
-
- def __getattr__(self, attr):
- _module = self._resolve()
- value = getattr(_module, attr)
- setattr(self, attr, value)
- return value
-
-
-class _LazyModule(types.ModuleType):
-
- def __init__(self, name):
- super(_LazyModule, self).__init__(name)
- self.__doc__ = self.__class__.__doc__
-
- def __dir__(self):
- attrs = ["__doc__", "__name__"]
- attrs += [attr.name for attr in self._moved_attributes]
- return attrs
-
- # Subclasses should override this
- _moved_attributes = []
-
-
-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 _SixMetaPathImporter(object):
-
- """
- A meta path importer to import six.moves and its submodules.
-
- This class implements a PEP302 finder and loader. It should be compatible
- with Python 2.5 and all existing versions of Python3
- """
-
- def __init__(self, six_module_name):
- self.name = six_module_name
- self.known_modules = {}
-
- def _add_module(self, mod, *fullnames):
- for fullname in fullnames:
- self.known_modules[self.name + "." + fullname] = mod
-
- def _get_module(self, fullname):
- return self.known_modules[self.name + "." + fullname]
-
- def find_module(self, fullname, path=None):
- if fullname in self.known_modules:
- return self
- return None
-
- def __get_module(self, fullname):
- try:
- return self.known_modules[fullname]
- except KeyError:
- raise ImportError("This loader does not know module " + fullname)
-
- def load_module(self, fullname):
- try:
- # in case of a reload
- return sys.modules[fullname]
- except KeyError:
- pass
- mod = self.__get_module(fullname)
- if isinstance(mod, MovedModule):
- mod = mod._resolve()
- else:
- mod.__loader__ = self
- sys.modules[fullname] = mod
- return mod
-
- def is_package(self, fullname):
- """
- Return true, if the named module is a package.
-
- We need this method to get correct spec objects with
- Python 3.4 (see PEP451)
- """
- return hasattr(self.__get_module(fullname), "__path__")
-
- def get_code(self, fullname):
- """Return None
-
- Required, if is_package is implemented"""
- self.__get_module(fullname) # eventually raises ImportError
- return None
- get_source = get_code # same as get_code
-
-_importer = _SixMetaPathImporter(__name__)
-
-
-class _MovedItems(_LazyModule):
-
- """Lazy loading of moved objects"""
- __path__ = [] # mark as package
-
-
-_moved_attributes = [
- MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
- MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
- MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
- MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
- MovedAttribute("intern", "__builtin__", "sys"),
- MovedAttribute("map", "itertools", "builtins", "imap", "map"),
- MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"),
- MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"),
- MovedAttribute("getoutput", "commands", "subprocess"),
- MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
- MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"),
- MovedAttribute("reduce", "__builtin__", "functools"),
- MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
- MovedAttribute("StringIO", "StringIO", "io"),
- MovedAttribute("UserDict", "UserDict", "collections"),
- MovedAttribute("UserList", "UserList", "collections"),
- MovedAttribute("UserString", "UserString", "collections"),
- MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
- MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
- MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
- MovedModule("builtins", "__builtin__"),
- MovedModule("configparser", "ConfigParser"),
- MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"),
- MovedModule("copyreg", "copy_reg"),
- MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
- MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"),
- MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"),
- 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("email_mime_base", "email.MIMEBase", "email.mime.base"),
- MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"),
- MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
- MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
- MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
- 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("_thread", "thread", "_thread"),
- 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_ttk", "ttk", "tkinter.ttk"),
- 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_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
- MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
- MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
- MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
- MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
- MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
-]
-# Add windows specific modules.
-if sys.platform == "win32":
- _moved_attributes += [
- MovedModule("winreg", "_winreg"),
- ]
-
-for attr in _moved_attributes:
- setattr(_MovedItems, attr.name, attr)
- if isinstance(attr, MovedModule):
- _importer._add_module(attr, "moves." + attr.name)
-del attr
-
-_MovedItems._moved_attributes = _moved_attributes
-
-moves = _MovedItems(__name__ + ".moves")
-_importer._add_module(moves, "moves")
-
-
-class Module_six_moves_urllib_parse(_LazyModule):
-
- """Lazy loading of moved objects in six.moves.urllib_parse"""
-
-
-_urllib_parse_moved_attributes = [
- MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
- MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
- MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
- MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
- MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
- MovedAttribute("urljoin", "urlparse", "urllib.parse"),
- MovedAttribute("urlparse", "urlparse", "urllib.parse"),
- MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
- MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
- MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
- MovedAttribute("quote", "urllib", "urllib.parse"),
- MovedAttribute("quote_plus", "urllib", "urllib.parse"),
- MovedAttribute("unquote", "urllib", "urllib.parse"),
- MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
- MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"),
- MovedAttribute("urlencode", "urllib", "urllib.parse"),
- MovedAttribute("splitquery", "urllib", "urllib.parse"),
- MovedAttribute("splittag", "urllib", "urllib.parse"),
- MovedAttribute("splituser", "urllib", "urllib.parse"),
- MovedAttribute("splitvalue", "urllib", "urllib.parse"),
- MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
- MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
- MovedAttribute("uses_params", "urlparse", "urllib.parse"),
- MovedAttribute("uses_query", "urlparse", "urllib.parse"),
- MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
-]
-for attr in _urllib_parse_moved_attributes:
- setattr(Module_six_moves_urllib_parse, attr.name, attr)
-del attr
-
-Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
-
-_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
- "moves.urllib_parse", "moves.urllib.parse")
-
-
-class Module_six_moves_urllib_error(_LazyModule):
-
- """Lazy loading of moved objects in six.moves.urllib_error"""
-
-
-_urllib_error_moved_attributes = [
- MovedAttribute("URLError", "urllib2", "urllib.error"),
- MovedAttribute("HTTPError", "urllib2", "urllib.error"),
- MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
-]
-for attr in _urllib_error_moved_attributes:
- setattr(Module_six_moves_urllib_error, attr.name, attr)
-del attr
-
-Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
-
-_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
- "moves.urllib_error", "moves.urllib.error")
-
-
-class Module_six_moves_urllib_request(_LazyModule):
-
- """Lazy loading of moved objects in six.moves.urllib_request"""
-
-
-_urllib_request_moved_attributes = [
- MovedAttribute("urlopen", "urllib2", "urllib.request"),
- MovedAttribute("install_opener", "urllib2", "urllib.request"),
- MovedAttribute("build_opener", "urllib2", "urllib.request"),
- MovedAttribute("pathname2url", "urllib", "urllib.request"),
- MovedAttribute("url2pathname", "urllib", "urllib.request"),
- MovedAttribute("getproxies", "urllib", "urllib.request"),
- MovedAttribute("Request", "urllib2", "urllib.request"),
- MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
- MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
- MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
- MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
- MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
- MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
- MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
- MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
- MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
- MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
- MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
- MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
- MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
- MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
- MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
- MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
- MovedAttribute("FileHandler", "urllib2", "urllib.request"),
- MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
- MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
- MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
- MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
- MovedAttribute("urlretrieve", "urllib", "urllib.request"),
- MovedAttribute("urlcleanup", "urllib", "urllib.request"),
- MovedAttribute("URLopener", "urllib", "urllib.request"),
- MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
- MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
- MovedAttribute("parse_http_list", "urllib2", "urllib.request"),
- MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"),
-]
-for attr in _urllib_request_moved_attributes:
- setattr(Module_six_moves_urllib_request, attr.name, attr)
-del attr
-
-Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
-
-_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
- "moves.urllib_request", "moves.urllib.request")
-
-
-class Module_six_moves_urllib_response(_LazyModule):
-
- """Lazy loading of moved objects in six.moves.urllib_response"""
-
-
-_urllib_response_moved_attributes = [
- MovedAttribute("addbase", "urllib", "urllib.response"),
- MovedAttribute("addclosehook", "urllib", "urllib.response"),
- MovedAttribute("addinfo", "urllib", "urllib.response"),
- MovedAttribute("addinfourl", "urllib", "urllib.response"),
-]
-for attr in _urllib_response_moved_attributes:
- setattr(Module_six_moves_urllib_response, attr.name, attr)
-del attr
-
-Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
-
-_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
- "moves.urllib_response", "moves.urllib.response")
-
-
-class Module_six_moves_urllib_robotparser(_LazyModule):
-
- """Lazy loading of moved objects in six.moves.urllib_robotparser"""
-
-
-_urllib_robotparser_moved_attributes = [
- MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
-]
-for attr in _urllib_robotparser_moved_attributes:
- setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
-del attr
-
-Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
-
-_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
- "moves.urllib_robotparser", "moves.urllib.robotparser")
-
-
-class Module_six_moves_urllib(types.ModuleType):
-
- """Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
- __path__ = [] # mark as package
- parse = _importer._get_module("moves.urllib_parse")
- error = _importer._get_module("moves.urllib_error")
- request = _importer._get_module("moves.urllib_request")
- response = _importer._get_module("moves.urllib_response")
- robotparser = _importer._get_module("moves.urllib_robotparser")
-
- def __dir__(self):
- return ['parse', 'error', 'request', 'response', 'robotparser']
-
-_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
- "moves.urllib")
-
-
-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_closure = "__closure__"
- _func_code = "__code__"
- _func_defaults = "__defaults__"
- _func_globals = "__globals__"
-else:
- _meth_func = "im_func"
- _meth_self = "im_self"
-
- _func_closure = "func_closure"
- _func_code = "func_code"
- _func_defaults = "func_defaults"
- _func_globals = "func_globals"
-
-
-try:
- advance_iterator = next
-except NameError:
- def advance_iterator(it):
- return it.next()
-next = advance_iterator
-
-
-try:
- callable = callable
-except NameError:
- def callable(obj):
- return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
-
-
-if PY3:
- def get_unbound_function(unbound):
- return unbound
-
- create_bound_method = types.MethodType
-
- def create_unbound_method(func, cls):
- return func
-
- Iterator = object
-else:
- def get_unbound_function(unbound):
- return unbound.im_func
-
- def create_bound_method(func, obj):
- return types.MethodType(func, obj, obj.__class__)
-
- def create_unbound_method(func, cls):
- return types.MethodType(func, None, cls)
-
- class Iterator(object):
-
- def next(self):
- return type(self).__next__(self)
-
- 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_closure = operator.attrgetter(_func_closure)
-get_function_code = operator.attrgetter(_func_code)
-get_function_defaults = operator.attrgetter(_func_defaults)
-get_function_globals = operator.attrgetter(_func_globals)
-
-
-if PY3:
- def iterkeys(d, **kw):
- return iter(d.keys(**kw))
-
- def itervalues(d, **kw):
- return iter(d.values(**kw))
-
- def iteritems(d, **kw):
- return iter(d.items(**kw))
-
- def iterlists(d, **kw):
- return iter(d.lists(**kw))
-
- viewkeys = operator.methodcaller("keys")
-
- viewvalues = operator.methodcaller("values")
-
- viewitems = operator.methodcaller("items")
-else:
- def iterkeys(d, **kw):
- return d.iterkeys(**kw)
-
- def itervalues(d, **kw):
- return d.itervalues(**kw)
-
- def iteritems(d, **kw):
- return d.iteritems(**kw)
-
- def iterlists(d, **kw):
- return d.iterlists(**kw)
-
- viewkeys = operator.methodcaller("viewkeys")
-
- viewvalues = operator.methodcaller("viewvalues")
-
- viewitems = operator.methodcaller("viewitems")
-
-_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
-_add_doc(itervalues, "Return an iterator over the values of a dictionary.")
-_add_doc(iteritems,
- "Return an iterator over the (key, value) pairs of a dictionary.")
-_add_doc(iterlists,
- "Return an iterator over the (key, [values]) pairs of a dictionary.")
-
-
-if PY3:
- def b(s):
- return s.encode("latin-1")
-
- def u(s):
- return s
- unichr = chr
- import struct
- int2byte = struct.Struct(">B").pack
- del struct
- byte2int = operator.itemgetter(0)
- indexbytes = operator.getitem
- iterbytes = iter
- import io
- StringIO = io.StringIO
- BytesIO = io.BytesIO
- del io
- _assertCountEqual = "assertCountEqual"
- if sys.version_info[1] <= 1:
- _assertRaisesRegex = "assertRaisesRegexp"
- _assertRegex = "assertRegexpMatches"
- _assertNotRegex = "assertNotRegexpMatches"
- else:
- _assertRaisesRegex = "assertRaisesRegex"
- _assertRegex = "assertRegex"
- _assertNotRegex = "assertNotRegex"
-else:
- def b(s):
- return s
- # Workaround for standalone backslash
-
- def u(s):
- return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
- unichr = unichr
- int2byte = chr
-
- def byte2int(bs):
- return ord(bs[0])
-
- def indexbytes(buf, i):
- return ord(buf[i])
- iterbytes = functools.partial(itertools.imap, ord)
- import StringIO
- StringIO = BytesIO = StringIO.StringIO
- _assertCountEqual = "assertItemsEqual"
- _assertRaisesRegex = "assertRaisesRegexp"
- _assertRegex = "assertRegexpMatches"
- _assertNotRegex = "assertNotRegexpMatches"
-_add_doc(b, """Byte literal""")
-_add_doc(u, """Text literal""")
-
-
-def assertCountEqual(self, *args, **kwargs):
- return getattr(self, _assertCountEqual)(*args, **kwargs)
-
-
-def assertRaisesRegex(self, *args, **kwargs):
- return getattr(self, _assertRaisesRegex)(*args, **kwargs)
-
-
-def assertRegex(self, *args, **kwargs):
- return getattr(self, _assertRegex)(*args, **kwargs)
-
-
-def assertNotRegex(self, *args, **kwargs):
- return getattr(self, _assertNotRegex)(*args, **kwargs)
-
-
-if PY3:
- exec_ = getattr(moves.builtins, "exec")
-
- def reraise(tp, value, tb=None):
- try:
- if value is None:
- value = tp()
- if value.__traceback__ is not tb:
- raise value.with_traceback(tb)
- raise value
- finally:
- value = None
- tb = None
-
-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):
- try:
- raise tp, value, tb
- finally:
- tb = None
-""")
-
-
-if sys.version_info[:2] > (3,):
- exec_("""def raise_from(value, from_value):
- try:
- raise value from from_value
- finally:
- value = None
-""")
-else:
- def raise_from(value, from_value):
- raise value
-
-
-print_ = getattr(moves.builtins, "print", None)
-if print_ is None:
- def print_(*args, **kwargs):
- """The new-style print function for Python 2.4 and 2.5."""
- fp = kwargs.pop("file", sys.stdout)
- if fp is None:
- return
-
- def write(data):
- if not isinstance(data, basestring):
- data = str(data)
- # If the file has an encoding, encode unicode with it.
- if (isinstance(fp, file) and
- isinstance(data, unicode) and
- fp.encoding is not None):
- errors = getattr(fp, "errors", None)
- if errors is None:
- errors = "strict"
- data = data.encode(fp.encoding, errors)
- 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)
-if sys.version_info[:2] < (3, 3):
- _print = print_
-
- def print_(*args, **kwargs):
- fp = kwargs.get("file", sys.stdout)
- flush = kwargs.pop("flush", False)
- _print(*args, **kwargs)
- if flush and fp is not None:
- fp.flush()
-
-_add_doc(reraise, """Reraise an exception.""")
-
-if sys.version_info[0:2] < (3, 4):
- # This does exactly the same what the :func:`py3:functools.update_wrapper`
- # function does on Python versions after 3.2. It sets the ``__wrapped__``
- # attribute on ``wrapper`` object and it doesn't raise an error if any of
- # the attributes mentioned in ``assigned`` and ``updated`` are missing on
- # ``wrapped`` object.
- def _update_wrapper(wrapper, wrapped,
- assigned=functools.WRAPPER_ASSIGNMENTS,
- updated=functools.WRAPPER_UPDATES):
- for attr in assigned:
- try:
- value = getattr(wrapped, attr)
- except AttributeError:
- continue
- else:
- setattr(wrapper, attr, value)
- for attr in updated:
- getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
- wrapper.__wrapped__ = wrapped
- return wrapper
- _update_wrapper.__doc__ = functools.update_wrapper.__doc__
-
- def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
- updated=functools.WRAPPER_UPDATES):
- return functools.partial(_update_wrapper, wrapped=wrapped,
- assigned=assigned, updated=updated)
- wraps.__doc__ = functools.wraps.__doc__
-
-else:
- wraps = functools.wraps
-
-
-def with_metaclass(meta, *bases):
- """Create a base class with a metaclass."""
- # This requires a bit of explanation: the basic idea is to make a dummy
- # metaclass for one level of class instantiation that replaces itself with
- # the actual metaclass.
- class metaclass(type):
-
- def __new__(cls, name, this_bases, d):
- if sys.version_info[:2] >= (3, 7):
- # This version introduced PEP 560 that requires a bit
- # of extra care (we mimic what is done by __build_class__).
- resolved_bases = types.resolve_bases(bases)
- if resolved_bases is not bases:
- d['__orig_bases__'] = bases
- else:
- resolved_bases = bases
- return meta(name, resolved_bases, d)
-
- @classmethod
- def __prepare__(cls, name, this_bases):
- return meta.__prepare__(name, bases)
- return type.__new__(metaclass, 'temporary_class', (), {})
-
-
-def add_metaclass(metaclass):
- """Class decorator for creating a class with a metaclass."""
- def wrapper(cls):
- orig_vars = cls.__dict__.copy()
- slots = orig_vars.get('__slots__')
- if slots is not None:
- if isinstance(slots, str):
- slots = [slots]
- for slots_var in slots:
- orig_vars.pop(slots_var)
- orig_vars.pop('__dict__', None)
- orig_vars.pop('__weakref__', None)
- if hasattr(cls, '__qualname__'):
- orig_vars['__qualname__'] = cls.__qualname__
- return metaclass(cls.__name__, cls.__bases__, orig_vars)
- return wrapper
-
-
-def ensure_binary(s, encoding='utf-8', errors='strict'):
- """Coerce **s** to six.binary_type.
-
- For Python 2:
- - `unicode` -> encoded to `str`
- - `str` -> `str`
-
- For Python 3:
- - `str` -> encoded to `bytes`
- - `bytes` -> `bytes`
- """
- if isinstance(s, binary_type):
- return s
- if isinstance(s, text_type):
- return s.encode(encoding, errors)
- raise TypeError("not expecting type '%s'" % type(s))
-
-
-def ensure_str(s, encoding='utf-8', errors='strict'):
- """Coerce *s* to `str`.
-
- For Python 2:
- - `unicode` -> encoded to `str`
- - `str` -> `str`
-
- For Python 3:
- - `str` -> `str`
- - `bytes` -> decoded to `str`
- """
- # Optimization: Fast return for the common case.
- if type(s) is str:
- return s
- if PY2 and isinstance(s, text_type):
- return s.encode(encoding, errors)
- elif PY3 and isinstance(s, binary_type):
- return s.decode(encoding, errors)
- elif not isinstance(s, (text_type, binary_type)):
- raise TypeError("not expecting type '%s'" % type(s))
- return s
-
-
-def ensure_text(s, encoding='utf-8', errors='strict'):
- """Coerce *s* to six.text_type.
-
- For Python 2:
- - `unicode` -> `unicode`
- - `str` -> `unicode`
-
- For Python 3:
- - `str` -> `str`
- - `bytes` -> decoded to `str`
- """
- if isinstance(s, binary_type):
- return s.decode(encoding, errors)
- elif isinstance(s, text_type):
- return s
- else:
- raise TypeError("not expecting type '%s'" % type(s))
-
-
-def python_2_unicode_compatible(klass):
- """
- A class decorator that defines __unicode__ and __str__ methods under Python 2.
- Under Python 3 it does nothing.
-
- To support Python 2 and 3 with a single code base, define a __str__ method
- returning text and apply this decorator to the class.
- """
- if PY2:
- if '__str__' not in klass.__dict__:
- raise ValueError("@python_2_unicode_compatible cannot be applied "
- "to %s because it doesn't define __str__()." %
- klass.__name__)
- klass.__unicode__ = klass.__str__
- klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
- return klass
-
-
-# Complete the moves implementation.
-# This code is at the end of this module to speed up module loading.
-# Turn this module into a package.
-__path__ = [] # required for PEP 302 and PEP 451
-__package__ = __name__ # see PEP 366 @ReservedAssignment
-if globals().get("__spec__") is not None:
- __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable
-# Remove other six meta path importers, since they cause problems. This can
-# happen if six is removed from sys.modules and then reloaded. (Setuptools does
-# this for some reason.)
-if sys.meta_path:
- for i, importer in enumerate(sys.meta_path):
- # Here's some real nastiness: Another "instance" of the six module might
- # be floating around. Therefore, we can't use isinstance() to check for
- # the six meta path importer, since the other six instance will have
- # inserted an importer with different class.
- if (type(importer).__name__ == "_SixMetaPathImporter" and
- importer.name == __name__):
- del sys.meta_path[i]
- break
- del i, importer
-# Finally, add the importer to the meta path import hook.
-sys.meta_path.append(_importer)
diff --git a/game/python-extra/websock/__init__.py b/game/python-extra/websock/__init__.py
deleted file mode 100644
index 605f76c..0000000
--- a/game/python-extra/websock/__init__.py
+++ /dev/null
@@ -1,29 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1335 USA
-
-"""
-from ._abnf import *
-from ._app import WebSocketApp
-from ._core import *
-from ._exceptions import *
-from ._logging import *
-from ._socket import *
-
-__version__ = "0.57.0"
diff --git a/game/python-extra/websock/_abnf.py b/game/python-extra/websock/_abnf.py
deleted file mode 100644
index a0000fa..0000000
--- a/game/python-extra/websock/_abnf.py
+++ /dev/null
@@ -1,447 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1335 USA
-
-"""
-import array
-import os
-import struct
-
-import six
-
-from ._exceptions import *
-from ._utils import validate_utf8
-from threading import Lock
-
-try:
- if six.PY3:
- import numpy
- else:
- numpy = None
-except ImportError:
- numpy = None
-
-try:
- # If wsaccel is available we use compiled routines to mask data.
- if not numpy:
- from wsaccel.xormask import XorMaskerSimple
-
- def _mask(_m, _d):
- return XorMaskerSimple(_m).process(_d)
-except ImportError:
- # wsaccel is not available, we rely on python implementations.
- def _mask(_m, _d):
- for i in range(len(_d)):
- _d[i] ^= _m[i % 4]
-
- if six.PY3:
- return _d.tobytes()
- else:
- return _d.tostring()
-
-
-__all__ = [
- 'ABNF', 'continuous_frame', 'frame_buffer',
- 'STATUS_NORMAL',
- 'STATUS_GOING_AWAY',
- 'STATUS_PROTOCOL_ERROR',
- 'STATUS_UNSUPPORTED_DATA_TYPE',
- 'STATUS_STATUS_NOT_AVAILABLE',
- 'STATUS_ABNORMAL_CLOSED',
- 'STATUS_INVALID_PAYLOAD',
- 'STATUS_POLICY_VIOLATION',
- 'STATUS_MESSAGE_TOO_BIG',
- 'STATUS_INVALID_EXTENSION',
- 'STATUS_UNEXPECTED_CONDITION',
- 'STATUS_BAD_GATEWAY',
- 'STATUS_TLS_HANDSHAKE_ERROR',
-]
-
-# closing frame status codes.
-STATUS_NORMAL = 1000
-STATUS_GOING_AWAY = 1001
-STATUS_PROTOCOL_ERROR = 1002
-STATUS_UNSUPPORTED_DATA_TYPE = 1003
-STATUS_STATUS_NOT_AVAILABLE = 1005
-STATUS_ABNORMAL_CLOSED = 1006
-STATUS_INVALID_PAYLOAD = 1007
-STATUS_POLICY_VIOLATION = 1008
-STATUS_MESSAGE_TOO_BIG = 1009
-STATUS_INVALID_EXTENSION = 1010
-STATUS_UNEXPECTED_CONDITION = 1011
-STATUS_BAD_GATEWAY = 1014
-STATUS_TLS_HANDSHAKE_ERROR = 1015
-
-VALID_CLOSE_STATUS = (
- STATUS_NORMAL,
- STATUS_GOING_AWAY,
- STATUS_PROTOCOL_ERROR,
- STATUS_UNSUPPORTED_DATA_TYPE,
- STATUS_INVALID_PAYLOAD,
- STATUS_POLICY_VIOLATION,
- STATUS_MESSAGE_TOO_BIG,
- STATUS_INVALID_EXTENSION,
- STATUS_UNEXPECTED_CONDITION,
- STATUS_BAD_GATEWAY,
-)
-
-
-class ABNF(object):
- """
- ABNF frame class.
- see http://tools.ietf.org/html/rfc5234
- and http://tools.ietf.org/html/rfc6455#section-5.2
- """
-
- # operation code values.
- OPCODE_CONT = 0x0
- OPCODE_TEXT = 0x1
- OPCODE_BINARY = 0x2
- OPCODE_CLOSE = 0x8
- OPCODE_PING = 0x9
- OPCODE_PONG = 0xa
-
- # available operation code value tuple
- OPCODES = (OPCODE_CONT, OPCODE_TEXT, OPCODE_BINARY, OPCODE_CLOSE,
- OPCODE_PING, OPCODE_PONG)
-
- # opcode human readable string
- OPCODE_MAP = {
- OPCODE_CONT: "cont",
- OPCODE_TEXT: "text",
- OPCODE_BINARY: "binary",
- OPCODE_CLOSE: "close",
- OPCODE_PING: "ping",
- OPCODE_PONG: "pong"
- }
-
- # data length threshold.
- LENGTH_7 = 0x7e
- LENGTH_16 = 1 << 16
- LENGTH_63 = 1 << 63
-
- def __init__(self, fin=0, rsv1=0, rsv2=0, rsv3=0,
- opcode=OPCODE_TEXT, mask=1, data=""):
- """
- Constructor for ABNF.
- please check RFC for arguments.
- """
- self.fin = fin
- self.rsv1 = rsv1
- self.rsv2 = rsv2
- self.rsv3 = rsv3
- self.opcode = opcode
- self.mask = mask
- if data is None:
- data = ""
- self.data = data
- self.get_mask_key = os.urandom
-
- def validate(self, skip_utf8_validation=False):
- """
- validate the ABNF frame.
- skip_utf8_validation: skip utf8 validation.
- """
- if self.rsv1 or self.rsv2 or self.rsv3:
- raise WebSocketProtocolException("rsv is not implemented, yet")
-
- if self.opcode not in ABNF.OPCODES:
- raise WebSocketProtocolException("Invalid opcode %r", self.opcode)
-
- if self.opcode == ABNF.OPCODE_PING and not self.fin:
- raise WebSocketProtocolException("Invalid ping frame.")
-
- if self.opcode == ABNF.OPCODE_CLOSE:
- l = len(self.data)
- if not l:
- return
- if l == 1 or l >= 126:
- raise WebSocketProtocolException("Invalid close frame.")
- if l > 2 and not skip_utf8_validation and not validate_utf8(self.data[2:]):
- raise WebSocketProtocolException("Invalid close frame.")
-
- code = 256 * \
- six.byte2int(self.data[0:1]) + six.byte2int(self.data[1:2])
- if not self._is_valid_close_status(code):
- raise WebSocketProtocolException("Invalid close opcode.")
-
- @staticmethod
- def _is_valid_close_status(code):
- return code in VALID_CLOSE_STATUS or (3000 <= code < 5000)
-
- def __str__(self):
- return "fin=" + str(self.fin) \
- + " opcode=" + str(self.opcode) \
- + " data=" + str(self.data)
-
- @staticmethod
- def create_frame(data, opcode, fin=1):
- """
- create frame to send text, binary and other data.
-
- data: data to send. This is string value(byte array).
- if opcode is OPCODE_TEXT and this value is unicode,
- data value is converted into unicode string, automatically.
-
- opcode: operation code. please see OPCODE_XXX.
-
- fin: fin flag. if set to 0, create continue fragmentation.
- """
- if opcode == ABNF.OPCODE_TEXT and isinstance(data, six.text_type):
- data = data.encode("utf-8")
- # mask must be set if send data from client
- return ABNF(fin, 0, 0, 0, opcode, 1, data)
-
- def format(self):
- """
- format this object to string(byte array) to send data to server.
- """
- if any(x not in (0, 1) for x in [self.fin, self.rsv1, self.rsv2, self.rsv3]):
- raise ValueError("not 0 or 1")
- if self.opcode not in ABNF.OPCODES:
- raise ValueError("Invalid OPCODE")
- length = len(self.data)
- if length >= ABNF.LENGTH_63:
- raise ValueError("data is too long")
-
- frame_header = chr(self.fin << 7
- | self.rsv1 << 6 | self.rsv2 << 5 | self.rsv3 << 4
- | self.opcode)
- if length < ABNF.LENGTH_7:
- frame_header += chr(self.mask << 7 | length)
- frame_header = six.b(frame_header)
- elif length < ABNF.LENGTH_16:
- frame_header += chr(self.mask << 7 | 0x7e)
- frame_header = six.b(frame_header)
- frame_header += struct.pack("!H", length)
- else:
- frame_header += chr(self.mask << 7 | 0x7f)
- frame_header = six.b(frame_header)
- frame_header += struct.pack("!Q", length)
-
- if not self.mask:
- return frame_header + self.data
- else:
- mask_key = self.get_mask_key(4)
- return frame_header + self._get_masked(mask_key)
-
- def _get_masked(self, mask_key):
- s = ABNF.mask(mask_key, self.data)
-
- if isinstance(mask_key, six.text_type):
- mask_key = mask_key.encode('utf-8')
-
- return mask_key + s
-
- @staticmethod
- def mask(mask_key, data):
- """
- mask or unmask data. Just do xor for each byte
-
- mask_key: 4 byte string(byte).
-
- data: data to mask/unmask.
- """
- if data is None:
- data = ""
-
- if isinstance(mask_key, six.text_type):
- mask_key = six.b(mask_key)
-
- if isinstance(data, six.text_type):
- data = six.b(data)
-
- if numpy:
- origlen = len(data)
- _mask_key = mask_key[3] << 24 | mask_key[2] << 16 | mask_key[1] << 8 | mask_key[0]
-
- # We need data to be a multiple of four...
- data += bytes(" " * (4 - (len(data) % 4)), "us-ascii")
- a = numpy.frombuffer(data, dtype="uint32")
- masked = numpy.bitwise_xor(a, [_mask_key]).astype("uint32")
- if len(data) > origlen:
- return masked.tobytes()[:origlen]
- return masked.tobytes()
- else:
- _m = array.array("B", mask_key)
- _d = array.array("B", data)
- return _mask(_m, _d)
-
-
-class frame_buffer(object):
- _HEADER_MASK_INDEX = 5
- _HEADER_LENGTH_INDEX = 6
-
- def __init__(self, recv_fn, skip_utf8_validation):
- self.recv = recv_fn
- self.skip_utf8_validation = skip_utf8_validation
- # Buffers over the packets from the layer beneath until desired amount
- # bytes of bytes are received.
- self.recv_buffer = []
- self.clear()
- self.lock = Lock()
-
- def clear(self):
- self.header = None
- self.length = None
- self.mask = None
-
- def has_received_header(self):
- return self.header is None
-
- def recv_header(self):
- header = self.recv_strict(2)
- b1 = header[0]
-
- if six.PY2:
- b1 = ord(b1)
-
- fin = b1 >> 7 & 1
- rsv1 = b1 >> 6 & 1
- rsv2 = b1 >> 5 & 1
- rsv3 = b1 >> 4 & 1
- opcode = b1 & 0xf
- b2 = header[1]
-
- if six.PY2:
- b2 = ord(b2)
-
- has_mask = b2 >> 7 & 1
- length_bits = b2 & 0x7f
-
- self.header = (fin, rsv1, rsv2, rsv3, opcode, has_mask, length_bits)
-
- def has_mask(self):
- if not self.header:
- return False
- return self.header[frame_buffer._HEADER_MASK_INDEX]
-
- def has_received_length(self):
- return self.length is None
-
- def recv_length(self):
- bits = self.header[frame_buffer._HEADER_LENGTH_INDEX]
- length_bits = bits & 0x7f
- if length_bits == 0x7e:
- v = self.recv_strict(2)
- self.length = struct.unpack("!H", v)[0]
- elif length_bits == 0x7f:
- v = self.recv_strict(8)
- self.length = struct.unpack("!Q", v)[0]
- else:
- self.length = length_bits
-
- def has_received_mask(self):
- return self.mask is None
-
- def recv_mask(self):
- self.mask = self.recv_strict(4) if self.has_mask() else ""
-
- def recv_frame(self):
-
- with self.lock:
- # Header
- if self.has_received_header():
- self.recv_header()
- (fin, rsv1, rsv2, rsv3, opcode, has_mask, _) = self.header
-
- # Frame length
- if self.has_received_length():
- self.recv_length()
- length = self.length
-
- # Mask
- if self.has_received_mask():
- self.recv_mask()
- mask = self.mask
-
- # Payload
- payload = self.recv_strict(length)
- if has_mask:
- payload = ABNF.mask(mask, payload)
-
- # Reset for next frame
- self.clear()
-
- frame = ABNF(fin, rsv1, rsv2, rsv3, opcode, has_mask, payload)
- frame.validate(self.skip_utf8_validation)
-
- return frame
-
- def recv_strict(self, bufsize):
- shortage = bufsize - sum(len(x) for x in self.recv_buffer)
- while shortage > 0:
- # Limit buffer size that we pass to socket.recv() to avoid
- # fragmenting the heap -- the number of bytes recv() actually
- # reads is limited by socket buffer and is relatively small,
- # yet passing large numbers repeatedly causes lots of large
- # buffers allocated and then shrunk, which results in
- # fragmentation.
- bytes_ = self.recv(min(16384, shortage))
- self.recv_buffer.append(bytes_)
- shortage -= len(bytes_)
-
- unified = six.b("").join(self.recv_buffer)
-
- if shortage == 0:
- self.recv_buffer = []
- return unified
- else:
- self.recv_buffer = [unified[bufsize:]]
- return unified[:bufsize]
-
-
-class continuous_frame(object):
-
- def __init__(self, fire_cont_frame, skip_utf8_validation):
- self.fire_cont_frame = fire_cont_frame
- self.skip_utf8_validation = skip_utf8_validation
- self.cont_data = None
- self.recving_frames = None
-
- def validate(self, frame):
- if not self.recving_frames and frame.opcode == ABNF.OPCODE_CONT:
- raise WebSocketProtocolException("Illegal frame")
- if self.recving_frames and \
- frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY):
- raise WebSocketProtocolException("Illegal frame")
-
- def add(self, frame):
- if self.cont_data:
- self.cont_data[1] += frame.data
- else:
- if frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY):
- self.recving_frames = frame.opcode
- self.cont_data = [frame.opcode, frame.data]
-
- if frame.fin:
- self.recving_frames = None
-
- def is_fire(self, frame):
- return frame.fin or self.fire_cont_frame
-
- def extract(self, frame):
- data = self.cont_data
- self.cont_data = None
- frame.data = data[1]
- if not self.fire_cont_frame and data[0] == ABNF.OPCODE_TEXT and not self.skip_utf8_validation and not validate_utf8(frame.data):
- raise WebSocketPayloadException(
- "cannot decode: " + repr(frame.data))
-
- return [data[0], frame]
diff --git a/game/python-extra/websock/_app.py b/game/python-extra/websock/_app.py
deleted file mode 100644
index e4e9f99..0000000
--- a/game/python-extra/websock/_app.py
+++ /dev/null
@@ -1,352 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1335 USA
-
-"""
-
-"""
-WebSocketApp provides higher level APIs.
-"""
-import inspect
-import select
-import sys
-import threading
-import time
-import traceback
-
-import six
-
-from ._abnf import ABNF
-from ._core import WebSocket, getdefaulttimeout
-from ._exceptions import *
-from . import _logging
-
-
-__all__ = ["WebSocketApp"]
-
-class Dispatcher:
- def __init__(self, app, ping_timeout):
- self.app = app
- self.ping_timeout = ping_timeout
-
- def read(self, sock, read_callback, check_callback):
- while self.app.keep_running:
- r, w, e = select.select(
- (self.app.sock.sock, ), (), (), self.ping_timeout)
- if r:
- if not read_callback():
- break
- check_callback()
-
-class SSLDispatcher:
- def __init__(self, app, ping_timeout):
- self.app = app
- self.ping_timeout = ping_timeout
-
- def read(self, sock, read_callback, check_callback):
- while self.app.keep_running:
- r = self.select()
- if r:
- if not read_callback():
- break
- check_callback()
-
- def select(self):
- sock = self.app.sock.sock
- if sock.pending():
- return [sock,]
-
- r, w, e = select.select((sock, ), (), (), self.ping_timeout)
- return r
-
-
-class WebSocketApp(object):
- """
- Higher level of APIs are provided.
- The interface is like JavaScript WebSocket object.
- """
-
- def __init__(self, url, header=None,
- on_open=None, on_message=None, on_error=None,
- on_close=None, on_ping=None, on_pong=None,
- on_cont_message=None,
- keep_running=True, get_mask_key=None, cookie=None,
- subprotocols=None,
- on_data=None):
- """
- url: websocket url.
- header: custom header for websocket handshake.
- on_open: callable object which is called at opening websocket.
- this function has one argument. The argument is this class object.
- on_message: callable object which is called when received data.
- on_message has 2 arguments.
- The 1st argument is this class object.
- The 2nd argument is utf-8 string which we get from the server.
- on_error: callable object which is called when we get error.
- on_error has 2 arguments.
- The 1st argument is this class object.
- The 2nd argument is exception object.
- on_close: callable object which is called when closed the connection.
- this function has one argument. The argument is this class object.
- on_cont_message: callback object which is called when receive continued
- frame data.
- on_cont_message has 3 arguments.
- The 1st argument is this class object.
- The 2nd argument is utf-8 string which we get from the server.
- The 3rd argument is continue flag. if 0, the data continue
- to next frame data
- on_data: callback object which is called when a message received.
- This is called before on_message or on_cont_message,
- and then on_message or on_cont_message is called.
- on_data has 4 argument.
- The 1st argument is this class object.
- The 2nd argument is utf-8 string which we get from the server.
- The 3rd argument is data type. ABNF.OPCODE_TEXT or ABNF.OPCODE_BINARY will be came.
- The 4th argument is continue flag. if 0, the data continue
- keep_running: this parameter is obsolete and ignored.
- get_mask_key: a callable to produce new mask keys,
- see the WebSocket.set_mask_key's docstring for more information
- subprotocols: array of available sub protocols. default is None.
- """
- self.url = url
- self.header = header if header is not None else []
- self.cookie = cookie
-
- self.on_open = on_open
- self.on_message = on_message
- self.on_data = on_data
- self.on_error = on_error
- self.on_close = on_close
- self.on_ping = on_ping
- self.on_pong = on_pong
- self.on_cont_message = on_cont_message
- self.keep_running = False
- self.get_mask_key = get_mask_key
- self.sock = None
- self.last_ping_tm = 0
- self.last_pong_tm = 0
- self.subprotocols = subprotocols
-
- def send(self, data, opcode=ABNF.OPCODE_TEXT):
- """
- send message.
- data: message to send. If you set opcode to OPCODE_TEXT,
- data must be utf-8 string or unicode.
- opcode: operation code of data. default is OPCODE_TEXT.
- """
-
- if not self.sock or self.sock.send(data, opcode) == 0:
- raise WebSocketConnectionClosedException(
- "Connection is already closed.")
-
- def close(self, **kwargs):
- """
- close websocket connection.
- """
- self.keep_running = False
- if self.sock:
- self.sock.close(**kwargs)
- self.sock = None
-
- def _send_ping(self, interval, event):
- while not event.wait(interval):
- self.last_ping_tm = time.time()
- if self.sock:
- try:
- self.sock.ping()
- except Exception as ex:
- _logging.warning("send_ping routine terminated: {}".format(ex))
- break
-
- def run_forever(self, sockopt=None, sslopt=None,
- ping_interval=0, ping_timeout=None,
- http_proxy_host=None, http_proxy_port=None,
- http_no_proxy=None, http_proxy_auth=None,
- skip_utf8_validation=False,
- host=None, origin=None, dispatcher=None,
- suppress_origin=False, proxy_type=None):
- """
- run event loop for WebSocket framework.
- This loop is infinite loop and is alive during websocket is available.
- sockopt: values for socket.setsockopt.
- sockopt must be tuple
- and each element is argument of sock.setsockopt.
- sslopt: ssl socket optional dict.
- ping_interval: automatically send "ping" command
- every specified period(second)
- if set to 0, not send automatically.
- ping_timeout: timeout(second) if the pong message is not received.
- http_proxy_host: http proxy host name.
- http_proxy_port: http proxy port. If not set, set to 80.
- http_no_proxy: host names, which doesn't use proxy.
- skip_utf8_validation: skip utf8 validation.
- host: update host header.
- origin: update origin header.
- dispatcher: customize reading data from socket.
- suppress_origin: suppress outputting origin header.
-
- Returns
- -------
- False if caught KeyboardInterrupt
- True if other exception was raised during a loop
- """
-
- if ping_timeout is not None and ping_timeout <= 0:
- ping_timeout = None
- if ping_timeout and ping_interval and ping_interval <= ping_timeout:
- raise WebSocketException("Ensure ping_interval > ping_timeout")
- if not sockopt:
- sockopt = []
- if not sslopt:
- sslopt = {}
- if self.sock:
- raise WebSocketException("socket is already opened")
- thread = None
- self.keep_running = True
- self.last_ping_tm = 0
- self.last_pong_tm = 0
-
- def teardown(close_frame=None):
- """
- Tears down the connection.
- If close_frame is set, we will invoke the on_close handler with the
- statusCode and reason from there.
- """
- if thread and thread.isAlive():
- event.set()
- thread.join()
- self.keep_running = False
- if self.sock:
- self.sock.close()
- close_args = self._get_close_args(
- close_frame.data if close_frame else None)
- self._callback(self.on_close, *close_args)
- self.sock = None
-
- try:
- self.sock = WebSocket(
- self.get_mask_key, sockopt=sockopt, sslopt=sslopt,
- fire_cont_frame=self.on_cont_message is not None,
- skip_utf8_validation=skip_utf8_validation,
- enable_multithread=True if ping_interval else False)
- self.sock.settimeout(getdefaulttimeout())
- self.sock.connect(
- self.url, header=self.header, cookie=self.cookie,
- http_proxy_host=http_proxy_host,
- http_proxy_port=http_proxy_port, http_no_proxy=http_no_proxy,
- http_proxy_auth=http_proxy_auth, subprotocols=self.subprotocols,
- host=host, origin=origin, suppress_origin=suppress_origin,
- proxy_type=proxy_type)
- if not dispatcher:
- dispatcher = self.create_dispatcher(ping_timeout)
-
- self._callback(self.on_open)
-
- if ping_interval:
- event = threading.Event()
- thread = threading.Thread(
- target=self._send_ping, args=(ping_interval, event))
- thread.setDaemon(True)
- thread.start()
-
- def read():
- if not self.keep_running:
- return teardown()
-
- op_code, frame = self.sock.recv_data_frame(True)
- if op_code == ABNF.OPCODE_CLOSE:
- return teardown(frame)
- elif op_code == ABNF.OPCODE_PING:
- self._callback(self.on_ping, frame.data)
- elif op_code == ABNF.OPCODE_PONG:
- self.last_pong_tm = time.time()
- self._callback(self.on_pong, frame.data)
- elif op_code == ABNF.OPCODE_CONT and self.on_cont_message:
- self._callback(self.on_data, frame.data,
- frame.opcode, frame.fin)
- self._callback(self.on_cont_message,
- frame.data, frame.fin)
- else:
- data = frame.data
- if six.PY3 and op_code == ABNF.OPCODE_TEXT:
- data = data.decode("utf-8")
- self._callback(self.on_data, data, frame.opcode, True)
- self._callback(self.on_message, data)
-
- return True
-
- def check():
- if (ping_timeout):
- has_timeout_expired = time.time() - self.last_ping_tm > ping_timeout
- has_pong_not_arrived_after_last_ping = self.last_pong_tm - self.last_ping_tm < 0
- has_pong_arrived_too_late = self.last_pong_tm - self.last_ping_tm > ping_timeout
-
- if (self.last_ping_tm
- and has_timeout_expired
- and (has_pong_not_arrived_after_last_ping or has_pong_arrived_too_late)):
- raise WebSocketTimeoutException("ping/pong timed out")
- return True
-
- dispatcher.read(self.sock.sock, read, check)
- except (Exception, KeyboardInterrupt, SystemExit) as e:
- self._callback(self.on_error, e)
- if isinstance(e, SystemExit):
- # propagate SystemExit further
- raise
- teardown()
- return not isinstance(e, KeyboardInterrupt)
-
- def create_dispatcher(self, ping_timeout):
- timeout = ping_timeout or 10
- if self.sock.is_ssl():
- return SSLDispatcher(self, timeout)
-
- return Dispatcher(self, timeout)
-
- def _get_close_args(self, data):
- """ this functions extracts the code, reason from the close body
- if they exists, and if the self.on_close except three arguments """
- # if the on_close callback is "old", just return empty list
- if sys.version_info < (3, 0):
- if not self.on_close or len(inspect.getargspec(self.on_close).args) != 3:
- return []
- else:
- if not self.on_close or len(inspect.getfullargspec(self.on_close).args) != 3:
- return []
-
- if data and len(data) >= 2:
- code = 256 * six.byte2int(data[0:1]) + six.byte2int(data[1:2])
- reason = data[2:].decode('utf-8')
- return [code, reason]
-
- return [None, None]
-
- def _callback(self, callback, *args):
- if callback:
- try:
- if inspect.ismethod(callback):
- callback(*args)
- else:
- callback(self, *args)
-
- except Exception as e:
- _logging.error("error from callback {}: {}".format(callback, e))
- if _logging.isEnabledForDebug():
- _, _, tb = sys.exc_info()
- traceback.print_tb(tb)
diff --git a/game/python-extra/websock/_cookiejar.py b/game/python-extra/websock/_cookiejar.py
deleted file mode 100644
index 3efeb0f..0000000
--- a/game/python-extra/websock/_cookiejar.py
+++ /dev/null
@@ -1,52 +0,0 @@
-try:
- import Cookie
-except:
- import http.cookies as Cookie
-
-
-class SimpleCookieJar(object):
- def __init__(self):
- self.jar = dict()
-
- def add(self, set_cookie):
- if set_cookie:
- try:
- simpleCookie = Cookie.SimpleCookie(set_cookie)
- except:
- simpleCookie = Cookie.SimpleCookie(set_cookie.encode('ascii', 'ignore'))
-
- for k, v in simpleCookie.items():
- domain = v.get("domain")
- if domain:
- if not domain.startswith("."):
- domain = "." + domain
- cookie = self.jar.get(domain) if self.jar.get(domain) else Cookie.SimpleCookie()
- cookie.update(simpleCookie)
- self.jar[domain.lower()] = cookie
-
- def set(self, set_cookie):
- if set_cookie:
- try:
- simpleCookie = Cookie.SimpleCookie(set_cookie)
- except:
- simpleCookie = Cookie.SimpleCookie(set_cookie.encode('ascii', 'ignore'))
-
- for k, v in simpleCookie.items():
- domain = v.get("domain")
- if domain:
- if not domain.startswith("."):
- domain = "." + domain
- self.jar[domain.lower()] = simpleCookie
-
- def get(self, host):
- if not host:
- return ""
-
- cookies = []
- for domain, simpleCookie in self.jar.items():
- host = host.lower()
- if host.endswith(domain) or host == domain[1:]:
- cookies.append(self.jar.get(domain))
-
- return "; ".join(filter(None, ["%s=%s" % (k, v.value) for cookie in filter(None, sorted(cookies)) for k, v in
- sorted(cookie.items())]))
diff --git a/game/python-extra/websock/_core.py b/game/python-extra/websock/_core.py
deleted file mode 100644
index 6442188..0000000
--- a/game/python-extra/websock/_core.py
+++ /dev/null
@@ -1,522 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1335 USA
-
-"""
-from __future__ import print_function
-
-import socket
-import struct
-import threading
-import time
-
-import six
-
-# websocket modules
-from ._abnf import *
-from ._exceptions import *
-from ._handshake import *
-from ._http import *
-from ._logging import *
-from ._socket import *
-from ._ssl_compat import *
-from ._utils import *
-
-__all__ = ['WebSocket', 'create_connection']
-
-"""
-websocket python client.
-=========================
-
-This version support only hybi-13.
-Please see http://tools.ietf.org/html/rfc6455 for protocol.
-"""
-
-
-class WebSocket(object):
- """
- Low level WebSocket interface.
- This class is based on
- The WebSocket protocol draft-hixie-thewebsocketprotocol-76
- http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
-
- We can connect to the websocket server and send/receive data.
- The following example is an echo client.
-
- >>> import websocket
- >>> ws = websocket.WebSocket()
- >>> ws.connect("ws://echo.websocket.org")
- >>> ws.send("Hello, Server")
- >>> ws.recv()
- 'Hello, Server'
- >>> ws.close()
-
- get_mask_key: a callable to produce new mask keys, see the set_mask_key
- function's docstring for more details
- sockopt: values for socket.setsockopt.
- sockopt must be tuple and each element is argument of sock.setsockopt.
- sslopt: dict object for ssl socket option.
- fire_cont_frame: fire recv event for each cont frame. default is False
- enable_multithread: if set to True, lock send method.
- skip_utf8_validation: skip utf8 validation.
- """
-
- def __init__(self, get_mask_key=None, sockopt=None, sslopt=None,
- fire_cont_frame=False, enable_multithread=False,
- skip_utf8_validation=False, **_):
- """
- Initialize WebSocket object.
- """
- self.sock_opt = sock_opt(sockopt, sslopt)
- self.handshake_response = None
- self.sock = None
-
- self.connected = False
- self.get_mask_key = get_mask_key
- # These buffer over the build-up of a single frame.
- self.frame_buffer = frame_buffer(self._recv, skip_utf8_validation)
- self.cont_frame = continuous_frame(
- fire_cont_frame, skip_utf8_validation)
-
- if enable_multithread:
- self.lock = threading.Lock()
- self.readlock = threading.Lock()
- else:
- self.lock = NoLock()
- self.readlock = NoLock()
-
- def __iter__(self):
- """
- Allow iteration over websocket, implying sequential `recv` executions.
- """
- while True:
- yield self.recv()
-
- def __next__(self):
- return self.recv()
-
- def next(self):
- return self.__next__()
-
- def fileno(self):
- return self.sock.fileno()
-
- def set_mask_key(self, func):
- """
- set function to create musk key. You can customize mask key generator.
- Mainly, this is for testing purpose.
-
- func: callable object. the func takes 1 argument as integer.
- The argument means length of mask key.
- This func must return string(byte array),
- which length is argument specified.
- """
- self.get_mask_key = func
-
- def gettimeout(self):
- """
- Get the websocket timeout(second).
- """
- return self.sock_opt.timeout
-
- def settimeout(self, timeout):
- """
- Set the timeout to the websocket.
-
- timeout: timeout time(second).
- """
- self.sock_opt.timeout = timeout
- if self.sock:
- self.sock.settimeout(timeout)
-
- timeout = property(gettimeout, settimeout)
-
- def getsubprotocol(self):
- """
- get subprotocol
- """
- if self.handshake_response:
- return self.handshake_response.subprotocol
- else:
- return None
-
- subprotocol = property(getsubprotocol)
-
- def getstatus(self):
- """
- get handshake status
- """
- if self.handshake_response:
- return self.handshake_response.status
- else:
- return None
-
- status = property(getstatus)
-
- def getheaders(self):
- """
- get handshake response header
- """
- if self.handshake_response:
- return self.handshake_response.headers
- else:
- return None
-
- def is_ssl(self):
- ##########################
- # THIS HAS BEEN MODIFIED #
- ##########################
- try:
- return isinstance(self.sock, ssl.SSLSocket)
- except:
- return False
-
- headers = property(getheaders)
-
- def connect(self, url, **options):
- """
- Connect to url. url is websocket url scheme.
- ie. ws://host:port/resource
- You can customize using 'options'.
- If you set "header" list object, you can set your own custom header.
-
- >>> ws = WebSocket()
- >>> ws.connect("ws://echo.websocket.org/",
- ... header=["User-Agent: MyProgram",
- ... "x-custom: header"])
-
- timeout: socket timeout time. This value is integer.
- if you set None for this value,
- it means "use default_timeout value"
-
- options: "header" -> custom http header list or dict.
- "cookie" -> cookie value.
- "origin" -> custom origin url.
- "suppress_origin" -> suppress outputting origin header.
- "host" -> custom host header string.
- "http_proxy_host" - http proxy host name.
- "http_proxy_port" - http proxy port. If not set, set to 80.
- "http_no_proxy" - host names, which doesn't use proxy.
- "http_proxy_auth" - http proxy auth information.
- tuple of username and password.
- default is None
- "redirect_limit" -> number of redirects to follow.
- "subprotocols" - array of available sub protocols.
- default is None.
- "socket" - pre-initialized stream socket.
-
- """
- # FIXME: "subprotocols" are getting lost, not passed down
- # FIXME: "header", "cookie", "origin" and "host" too
- self.sock_opt.timeout = options.get('timeout', self.sock_opt.timeout)
- self.sock, addrs = connect(url, self.sock_opt, proxy_info(**options),
- options.pop('socket', None))
-
- try:
- self.handshake_response = handshake(self.sock, *addrs, **options)
- for attempt in range(options.pop('redirect_limit', 3)):
- if self.handshake_response.status in SUPPORTED_REDIRECT_STATUSES:
- url = self.handshake_response.headers['location']
- self.sock.close()
- self.sock, addrs = connect(url, self.sock_opt, proxy_info(**options),
- options.pop('socket', None))
- self.handshake_response = handshake(self.sock, *addrs, **options)
- self.connected = True
- except:
- if self.sock:
- self.sock.close()
- self.sock = None
- raise
-
- def send(self, payload, opcode=ABNF.OPCODE_TEXT):
- """
- Send the data as string.
-
- payload: Payload must be utf-8 string or unicode,
- if the opcode is OPCODE_TEXT.
- Otherwise, it must be string(byte array)
-
- opcode: operation code to send. Please see OPCODE_XXX.
- """
-
- frame = ABNF.create_frame(payload, opcode)
- return self.send_frame(frame)
-
- def send_frame(self, frame):
- """
- Send the data frame.
-
- frame: frame data created by ABNF.create_frame
-
- >>> ws = create_connection("ws://echo.websocket.org/")
- >>> frame = ABNF.create_frame("Hello", ABNF.OPCODE_TEXT)
- >>> ws.send_frame(frame)
- >>> cont_frame = ABNF.create_frame("My name is ", ABNF.OPCODE_CONT, 0)
- >>> ws.send_frame(frame)
- >>> cont_frame = ABNF.create_frame("Foo Bar", ABNF.OPCODE_CONT, 1)
- >>> ws.send_frame(frame)
-
- """
- if self.get_mask_key:
- frame.get_mask_key = self.get_mask_key
- data = frame.format()
- length = len(data)
- if (isEnabledForTrace()):
- trace("send: " + repr(data))
-
- with self.lock:
- while data:
- l = self._send(data)
- data = data[l:]
-
- return length
-
- def send_binary(self, payload):
- return self.send(payload, ABNF.OPCODE_BINARY)
-
- def ping(self, payload=""):
- """
- send ping data.
-
- payload: data payload to send server.
- """
- if isinstance(payload, six.text_type):
- payload = payload.encode("utf-8")
- self.send(payload, ABNF.OPCODE_PING)
-
- def pong(self, payload):
- """
- send pong data.
-
- payload: data payload to send server.
- """
- if isinstance(payload, six.text_type):
- payload = payload.encode("utf-8")
- self.send(payload, ABNF.OPCODE_PONG)
-
- def recv(self):
- """
- Receive string data(byte array) from the server.
-
- return value: string(byte array) value.
- """
- with self.readlock:
- opcode, data = self.recv_data()
- if six.PY3 and opcode == ABNF.OPCODE_TEXT:
- return data.decode("utf-8")
- elif opcode == ABNF.OPCODE_TEXT or opcode == ABNF.OPCODE_BINARY:
- return data
- else:
- return ''
-
- def recv_data(self, control_frame=False):
- """
- Receive data with operation code.
-
- control_frame: a boolean flag indicating whether to return control frame
- data, defaults to False
-
- return value: tuple of operation code and string(byte array) value.
- """
- opcode, frame = self.recv_data_frame(control_frame)
- return opcode, frame.data
-
- def recv_data_frame(self, control_frame=False):
- """
- Receive data with operation code.
-
- control_frame: a boolean flag indicating whether to return control frame
- data, defaults to False
-
- return value: tuple of operation code and string(byte array) value.
- """
- while True:
- frame = self.recv_frame()
- if not frame:
- # handle error:
- # 'NoneType' object has no attribute 'opcode'
- raise WebSocketProtocolException(
- "Not a valid frame %s" % frame)
- elif frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY, ABNF.OPCODE_CONT):
- self.cont_frame.validate(frame)
- self.cont_frame.add(frame)
-
- if self.cont_frame.is_fire(frame):
- return self.cont_frame.extract(frame)
-
- elif frame.opcode == ABNF.OPCODE_CLOSE:
- self.send_close()
- return frame.opcode, frame
- elif frame.opcode == ABNF.OPCODE_PING:
- if len(frame.data) < 126:
- self.pong(frame.data)
- else:
- raise WebSocketProtocolException(
- "Ping message is too long")
- if control_frame:
- return frame.opcode, frame
- elif frame.opcode == ABNF.OPCODE_PONG:
- if control_frame:
- return frame.opcode, frame
-
- def recv_frame(self):
- """
- receive data as frame from server.
-
- return value: ABNF frame object.
- """
- return self.frame_buffer.recv_frame()
-
- def send_close(self, status=STATUS_NORMAL, reason=six.b("")):
- """
- send close data to the server.
-
- status: status code to send. see STATUS_XXX.
-
- reason: the reason to close. This must be string or bytes.
- """
- if status < 0 or status >= ABNF.LENGTH_16:
- raise ValueError("code is invalid range")
- self.connected = False
- self.send(struct.pack('!H', status) + reason, ABNF.OPCODE_CLOSE)
-
- def close(self, status=STATUS_NORMAL, reason=six.b(""), timeout=3):
- """
- Close Websocket object
-
- status: status code to send. see STATUS_XXX.
-
- reason: the reason to close. This must be string.
-
- timeout: timeout until receive a close frame.
- If None, it will wait forever until receive a close frame.
- """
- if self.connected:
- if status < 0 or status >= ABNF.LENGTH_16:
- raise ValueError("code is invalid range")
-
- try:
- self.connected = False
- self.send(struct.pack('!H', status) +
- reason, ABNF.OPCODE_CLOSE)
- sock_timeout = self.sock.gettimeout()
- self.sock.settimeout(timeout)
- start_time = time.time()
- while timeout is None or time.time() - start_time < timeout:
- try:
- frame = self.recv_frame()
- if frame.opcode != ABNF.OPCODE_CLOSE:
- continue
- if isEnabledForError():
- recv_status = struct.unpack("!H", frame.data[0:2])[0]
- if recv_status != STATUS_NORMAL:
- error("close status: " + repr(recv_status))
- break
- except:
- break
- self.sock.settimeout(sock_timeout)
- self.sock.shutdown(socket.SHUT_RDWR)
- except:
- pass
-
- self.shutdown()
-
- def abort(self):
- """
- Low-level asynchronous abort, wakes up other threads that are waiting in recv_*
- """
- if self.connected:
- self.sock.shutdown(socket.SHUT_RDWR)
-
- def shutdown(self):
- """close socket, immediately."""
- if self.sock:
- self.sock.close()
- self.sock = None
- self.connected = False
-
- def _send(self, data):
- return send(self.sock, data)
-
- def _recv(self, bufsize):
- try:
- return recv(self.sock, bufsize)
- except WebSocketConnectionClosedException:
- if self.sock:
- self.sock.close()
- self.sock = None
- self.connected = False
- raise
-
-
-def create_connection(url, timeout=None, class_=WebSocket, **options):
- """
- connect to url and return websocket object.
-
- Connect to url and return the WebSocket object.
- Passing optional timeout parameter will set the timeout on the socket.
- If no timeout is supplied,
- the global default timeout setting returned by getdefauttimeout() is used.
- You can customize using 'options'.
- If you set "header" list object, you can set your own custom header.
-
- >>> conn = create_connection("ws://echo.websocket.org/",
- ... header=["User-Agent: MyProgram",
- ... "x-custom: header"])
-
-
- timeout: socket timeout time. This value is integer.
- if you set None for this value,
- it means "use default_timeout value"
-
- class_: class to instantiate when creating the connection. It has to implement
- settimeout and connect. It's __init__ should be compatible with
- WebSocket.__init__, i.e. accept all of it's kwargs.
- options: "header" -> custom http header list or dict.
- "cookie" -> cookie value.
- "origin" -> custom origin url.
- "suppress_origin" -> suppress outputting origin header.
- "host" -> custom host header string.
- "http_proxy_host" - http proxy host name.
- "http_proxy_port" - http proxy port. If not set, set to 80.
- "http_no_proxy" - host names, which doesn't use proxy.
- "http_proxy_auth" - http proxy auth information.
- tuple of username and password.
- default is None
- "enable_multithread" -> enable lock for multithread.
- "redirect_limit" -> number of redirects to follow.
- "sockopt" -> socket options
- "sslopt" -> ssl option
- "subprotocols" - array of available sub protocols.
- default is None.
- "skip_utf8_validation" - skip utf8 validation.
- "socket" - pre-initialized stream socket.
- """
- sockopt = options.pop("sockopt", [])
- sslopt = options.pop("sslopt", {})
- fire_cont_frame = options.pop("fire_cont_frame", False)
- enable_multithread = options.pop("enable_multithread", False)
- skip_utf8_validation = options.pop("skip_utf8_validation", False)
- websock = class_(sockopt=sockopt, sslopt=sslopt,
- fire_cont_frame=fire_cont_frame,
- enable_multithread=enable_multithread,
- skip_utf8_validation=skip_utf8_validation, **options)
- websock.settimeout(timeout if timeout is not None else getdefaulttimeout())
- websock.connect(url, **options)
- return websock
diff --git a/game/python-extra/websock/_exceptions.py b/game/python-extra/websock/_exceptions.py
deleted file mode 100644
index 2070790..0000000
--- a/game/python-extra/websock/_exceptions.py
+++ /dev/null
@@ -1,88 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1335 USA
-
-"""
-
-
-"""
-define websocket exceptions
-"""
-
-
-class WebSocketException(Exception):
- """
- websocket exception class.
- """
- pass
-
-
-class WebSocketProtocolException(WebSocketException):
- """
- If the websocket protocol is invalid, this exception will be raised.
- """
- pass
-
-
-class WebSocketPayloadException(WebSocketException):
- """
- If the websocket payload is invalid, this exception will be raised.
- """
- pass
-
-
-class WebSocketConnectionClosedException(WebSocketException):
- """
- If remote host closed the connection or some network error happened,
- this exception will be raised.
- """
- pass
-
-
-class WebSocketTimeoutException(WebSocketException):
- """
- WebSocketTimeoutException will be raised at socket timeout during read/write data.
- """
- pass
-
-
-class WebSocketProxyException(WebSocketException):
- """
- WebSocketProxyException will be raised when proxy error occurred.
- """
- pass
-
-
-class WebSocketBadStatusException(WebSocketException):
- """
- WebSocketBadStatusException will be raised when we get bad handshake status code.
- """
-
- def __init__(self, message, status_code, status_message=None, resp_headers=None):
- msg = message % (status_code, status_message)
- super(WebSocketBadStatusException, self).__init__(msg)
- self.status_code = status_code
- self.resp_headers = resp_headers
-
-
-class WebSocketAddressException(WebSocketException):
- """
- If the websocket address info cannot be found, this exception will be raised.
- """
- pass
diff --git a/game/python-extra/websock/_handshake.py b/game/python-extra/websock/_handshake.py
deleted file mode 100644
index 7476a07..0000000
--- a/game/python-extra/websock/_handshake.py
+++ /dev/null
@@ -1,211 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1335 USA
-
-"""
-import hashlib
-import hmac
-import os
-
-import six
-
-from ._cookiejar import SimpleCookieJar
-from ._exceptions import *
-from ._http import *
-from ._logging import *
-from ._socket import *
-
-if hasattr(six, 'PY3') and six.PY3:
- from base64 import encodebytes as base64encode
-else:
- from base64 import encodestring as base64encode
-
-if hasattr(six, 'PY3') and six.PY3:
- if hasattr(six, 'PY34') and six.PY34:
- from http import client as HTTPStatus
- else:
- from http import HTTPStatus
-else:
- import httplib as HTTPStatus
-
-__all__ = ["handshake_response", "handshake", "SUPPORTED_REDIRECT_STATUSES"]
-
-if hasattr(hmac, "compare_digest"):
- compare_digest = hmac.compare_digest
-else:
- def compare_digest(s1, s2):
- return s1 == s2
-
-# websocket supported version.
-VERSION = 13
-
-SUPPORTED_REDIRECT_STATUSES = (HTTPStatus.MOVED_PERMANENTLY, HTTPStatus.FOUND, HTTPStatus.SEE_OTHER,)
-SUCCESS_STATUSES = SUPPORTED_REDIRECT_STATUSES + (HTTPStatus.SWITCHING_PROTOCOLS,)
-
-CookieJar = SimpleCookieJar()
-
-
-class handshake_response(object):
-
- def __init__(self, status, headers, subprotocol):
- self.status = status
- self.headers = headers
- self.subprotocol = subprotocol
- CookieJar.add(headers.get("set-cookie"))
-
-
-def handshake(sock, hostname, port, resource, **options):
- headers, key = _get_handshake_headers(resource, hostname, port, options)
-
- header_str = "\r\n".join(headers)
- send(sock, header_str)
- dump("request header", header_str)
-
- status, resp = _get_resp_headers(sock)
- if status in SUPPORTED_REDIRECT_STATUSES:
- return handshake_response(status, resp, None)
- success, subproto = _validate(resp, key, options.get("subprotocols"))
- if not success:
- raise WebSocketException("Invalid WebSocket Header")
-
- return handshake_response(status, resp, subproto)
-
-
-def _pack_hostname(hostname):
- # IPv6 address
- if ':' in hostname:
- return '[' + hostname + ']'
-
- return hostname
-
-def _get_handshake_headers(resource, host, port, options):
- headers = [
- "GET %s HTTP/1.1" % resource,
- "Upgrade: websocket"
- ]
- if port == 80 or port == 443:
- hostport = _pack_hostname(host)
- else:
- hostport = "%s:%d" % (_pack_hostname(host), port)
- if "host" in options and options["host"] is not None:
- headers.append("Host: %s" % options["host"])
- else:
- headers.append("Host: %s" % hostport)
-
- if "suppress_origin" not in options or not options["suppress_origin"]:
- if "origin" in options and options["origin"] is not None:
- headers.append("Origin: %s" % options["origin"])
- else:
- headers.append("Origin: http://%s" % hostport)
-
- key = _create_sec_websocket_key()
-
- # Append Sec-WebSocket-Key & Sec-WebSocket-Version if not manually specified
- if not 'header' in options or 'Sec-WebSocket-Key' not in options['header']:
- key = _create_sec_websocket_key()
- headers.append("Sec-WebSocket-Key: %s" % key)
- else:
- key = options['header']['Sec-WebSocket-Key']
-
- if not 'header' in options or 'Sec-WebSocket-Version' not in options['header']:
- headers.append("Sec-WebSocket-Version: %s" % VERSION)
-
- if not 'connection' in options or options['connection'] is None:
- headers.append('Connection: upgrade')
- else:
- headers.append(options['connection'])
-
- subprotocols = options.get("subprotocols")
- if subprotocols:
- headers.append("Sec-WebSocket-Protocol: %s" % ",".join(subprotocols))
-
- if "header" in options:
- header = options["header"]
- if isinstance(header, dict):
- header = [
- ": ".join([k, v])
- for k, v in header.items()
- if v is not None
- ]
- headers.extend(header)
-
- server_cookie = CookieJar.get(host)
- client_cookie = options.get("cookie", None)
-
- cookie = "; ".join(filter(None, [server_cookie, client_cookie]))
-
- if cookie:
- headers.append("Cookie: %s" % cookie)
-
- headers.append("")
- headers.append("")
-
- return headers, key
-
-
-def _get_resp_headers(sock, success_statuses=SUCCESS_STATUSES):
- status, resp_headers, status_message = read_headers(sock)
- if status not in success_statuses:
- raise WebSocketBadStatusException("Handshake status %d %s", status, status_message, resp_headers)
- return status, resp_headers
-
-
-_HEADERS_TO_CHECK = {
- "upgrade": "websocket",
- "connection": "upgrade",
-}
-
-
-def _validate(headers, key, subprotocols):
- subproto = None
- for k, v in _HEADERS_TO_CHECK.items():
- r = headers.get(k, None)
- if not r:
- return False, None
- r = r.lower()
- if v != r:
- return False, None
-
- if subprotocols:
- subproto = headers.get("sec-websocket-protocol", None).lower()
- if not subproto or subproto not in [s.lower() for s in subprotocols]:
- error("Invalid subprotocol: " + str(subprotocols))
- return False, None
-
- result = headers.get("sec-websocket-accept", None)
- if not result:
- return False, None
- result = result.lower()
-
- if isinstance(result, six.text_type):
- result = result.encode('utf-8')
-
- value = (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").encode('utf-8')
- hashed = base64encode(hashlib.sha1(value).digest()).strip().lower()
- success = compare_digest(hashed, result)
-
- if success:
- return True, subproto
- else:
- return False, None
-
-
-def _create_sec_websocket_key():
- randomness = os.urandom(16)
- return base64encode(randomness).decode('utf-8').strip()
diff --git a/game/python-extra/websock/_http.py b/game/python-extra/websock/_http.py
deleted file mode 100644
index a8777de..0000000
--- a/game/python-extra/websock/_http.py
+++ /dev/null
@@ -1,330 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1335 USA
-
-"""
-import errno
-import os
-import socket
-import sys
-
-import six
-
-from ._exceptions import *
-from ._logging import *
-from ._socket import*
-from ._ssl_compat import *
-from ._url import *
-
-if six.PY3:
- from base64 import encodebytes as base64encode
-else:
- from base64 import encodestring as base64encode
-
-__all__ = ["proxy_info", "connect", "read_headers"]
-
-try:
- import socks
- ProxyConnectionError = socks.ProxyConnectionError
- HAS_PYSOCKS = True
-except:
- class ProxyConnectionError(BaseException):
- pass
- HAS_PYSOCKS = False
-
-class proxy_info(object):
-
- def __init__(self, **options):
- self.type = options.get("proxy_type") or "http"
- if not(self.type in ['http', 'socks4', 'socks5', 'socks5h']):
- raise ValueError("proxy_type must be 'http', 'socks4', 'socks5' or 'socks5h'")
- self.host = options.get("http_proxy_host", None)
- if self.host:
- self.port = options.get("http_proxy_port", 0)
- self.auth = options.get("http_proxy_auth", None)
- self.no_proxy = options.get("http_no_proxy", None)
- else:
- self.port = 0
- self.auth = None
- self.no_proxy = None
-
-
-def _open_proxied_socket(url, options, proxy):
- hostname, port, resource, is_secure = parse_url(url)
-
- if not HAS_PYSOCKS:
- raise WebSocketException("PySocks module not found.")
-
- ptype = socks.SOCKS5
- rdns = False
- if proxy.type == "socks4":
- ptype = socks.SOCKS4
- if proxy.type == "http":
- ptype = socks.HTTP
- if proxy.type[-1] == "h":
- rdns = True
-
- sock = socks.create_connection(
- (hostname, port),
- proxy_type = ptype,
- proxy_addr = proxy.host,
- proxy_port = proxy.port,
- proxy_rdns = rdns,
- proxy_username = proxy.auth[0] if proxy.auth else None,
- proxy_password = proxy.auth[1] if proxy.auth else None,
- timeout = options.timeout,
- socket_options = DEFAULT_SOCKET_OPTION + options.sockopt
- )
-
- if is_secure:
- if HAVE_SSL:
- sock = _ssl_socket(sock, options.sslopt, hostname)
- else:
- raise WebSocketException("SSL not available.")
-
- return sock, (hostname, port, resource)
-
-
-def connect(url, options, proxy, socket):
- if proxy.host and not socket and not (proxy.type == 'http'):
- return _open_proxied_socket(url, options, proxy)
-
- hostname, port, resource, is_secure = parse_url(url)
-
- if socket:
- return socket, (hostname, port, resource)
-
- addrinfo_list, need_tunnel, auth = _get_addrinfo_list(
- hostname, port, is_secure, proxy)
- if not addrinfo_list:
- raise WebSocketException(
- "Host not found.: " + hostname + ":" + str(port))
-
- sock = None
- try:
- sock = _open_socket(addrinfo_list, options.sockopt, options.timeout)
- if need_tunnel:
- sock = _tunnel(sock, hostname, port, auth)
-
- if is_secure:
- if HAVE_SSL:
- sock = _ssl_socket(sock, options.sslopt, hostname)
- else:
- raise WebSocketException("SSL not available.")
-
- return sock, (hostname, port, resource)
- except:
- if sock:
- sock.close()
- raise
-
-
-def _get_addrinfo_list(hostname, port, is_secure, proxy):
- phost, pport, pauth = get_proxy_info(
- hostname, is_secure, proxy.host, proxy.port, proxy.auth, proxy.no_proxy)
- try:
- # when running on windows 10, getaddrinfo without socktype returns a socktype 0.
- # This generates an error exception: `_on_error: exception Socket type must be stream or datagram, not 0`
- # or `OSError: [Errno 22] Invalid argument` when creating socket. Force the socket type to SOCK_STREAM.
- if not phost:
- addrinfo_list = socket.getaddrinfo(
- hostname, port, 0, socket.SOCK_STREAM, socket.SOL_TCP)
- return addrinfo_list, False, None
- else:
- pport = pport and pport or 80
- # when running on windows 10, the getaddrinfo used above
- # returns a socktype 0. This generates an error exception:
- # _on_error: exception Socket type must be stream or datagram, not 0
- # Force the socket type to SOCK_STREAM
- addrinfo_list = socket.getaddrinfo(phost, pport, 0, socket.SOCK_STREAM, socket.SOL_TCP)
- return addrinfo_list, True, pauth
- except socket.gaierror as e:
- raise WebSocketAddressException(e)
-
-
-def _open_socket(addrinfo_list, sockopt, timeout):
- err = None
- for addrinfo in addrinfo_list:
- family, socktype, proto = addrinfo[:3]
- sock = socket.socket(family, socktype, proto)
- sock.settimeout(timeout)
- for opts in DEFAULT_SOCKET_OPTION:
- sock.setsockopt(*opts)
- for opts in sockopt:
- sock.setsockopt(*opts)
-
- address = addrinfo[4]
- err = None
- while not err:
- try:
- sock.connect(address)
- except ProxyConnectionError as error:
- err = WebSocketProxyException(str(error))
- err.remote_ip = str(address[0])
- continue
- except socket.error as error:
- error.remote_ip = str(address[0])
- try:
- eConnRefused = (errno.ECONNREFUSED, errno.WSAECONNREFUSED)
- except:
- eConnRefused = (errno.ECONNREFUSED, )
- if error.errno == errno.EINTR:
- continue
- elif error.errno in eConnRefused:
- err = error
- continue
- else:
- raise error
- else:
- break
- else:
- continue
- break
- else:
- if err:
- raise err
-
- return sock
-
-
-def _can_use_sni():
- return six.PY2 and sys.version_info >= (2, 7, 9) or sys.version_info >= (3, 2)
-
-
-def _wrap_sni_socket(sock, sslopt, hostname, check_hostname):
- context = ssl.SSLContext(sslopt.get('ssl_version', ssl.PROTOCOL_SSLv23))
-
- if sslopt.get('cert_reqs', ssl.CERT_NONE) != ssl.CERT_NONE:
- cafile = sslopt.get('ca_certs', None)
- capath = sslopt.get('ca_cert_path', None)
- if cafile or capath:
- context.load_verify_locations(cafile=cafile, capath=capath)
- elif hasattr(context, 'load_default_certs'):
- context.load_default_certs(ssl.Purpose.SERVER_AUTH)
- if sslopt.get('certfile', None):
- context.load_cert_chain(
- sslopt['certfile'],
- sslopt.get('keyfile', None),
- sslopt.get('password', None),
- )
- # see
- # https://github.com/liris/websocket-client/commit/b96a2e8fa765753e82eea531adb19716b52ca3ca#commitcomment-10803153
- context.verify_mode = sslopt['cert_reqs']
- if HAVE_CONTEXT_CHECK_HOSTNAME:
- context.check_hostname = check_hostname
- if 'ciphers' in sslopt:
- context.set_ciphers(sslopt['ciphers'])
- if 'cert_chain' in sslopt:
- certfile, keyfile, password = sslopt['cert_chain']
- context.load_cert_chain(certfile, keyfile, password)
- if 'ecdh_curve' in sslopt:
- context.set_ecdh_curve(sslopt['ecdh_curve'])
-
- return context.wrap_socket(
- sock,
- do_handshake_on_connect=sslopt.get('do_handshake_on_connect', True),
- suppress_ragged_eofs=sslopt.get('suppress_ragged_eofs', True),
- server_hostname=hostname,
- )
-
-
-def _ssl_socket(sock, user_sslopt, hostname):
- sslopt = dict(cert_reqs=ssl.CERT_REQUIRED)
- sslopt.update(user_sslopt)
-
- certPath = os.environ.get('WEBSOCKET_CLIENT_CA_BUNDLE')
- if certPath and os.path.isfile(certPath) \
- and user_sslopt.get('ca_certs', None) is None \
- and user_sslopt.get('ca_cert', None) is None:
- sslopt['ca_certs'] = certPath
- elif certPath and os.path.isdir(certPath) \
- and user_sslopt.get('ca_cert_path', None) is None:
- sslopt['ca_cert_path'] = certPath
-
- check_hostname = sslopt["cert_reqs"] != ssl.CERT_NONE and sslopt.pop(
- 'check_hostname', True)
-
- if _can_use_sni():
- sock = _wrap_sni_socket(sock, sslopt, hostname, check_hostname)
- else:
- sslopt.pop('check_hostname', True)
- sock = ssl.wrap_socket(sock, **sslopt)
-
- if not HAVE_CONTEXT_CHECK_HOSTNAME and check_hostname:
- match_hostname(sock.getpeercert(), hostname)
-
- return sock
-
-
-def _tunnel(sock, host, port, auth):
- debug("Connecting proxy...")
- connect_header = "CONNECT %s:%d HTTP/1.0\r\n" % (host, port)
- # TODO: support digest auth.
- if auth and auth[0]:
- auth_str = auth[0]
- if auth[1]:
- auth_str += ":" + auth[1]
- encoded_str = base64encode(auth_str.encode()).strip().decode().replace('\n', '')
- connect_header += "Proxy-Authorization: Basic %s\r\n" % encoded_str
- connect_header += "\r\n"
- dump("request header", connect_header)
-
- send(sock, connect_header)
-
- try:
- status, resp_headers, status_message = read_headers(sock)
- except Exception as e:
- raise WebSocketProxyException(str(e))
-
- if status != 200:
- raise WebSocketProxyException(
- "failed CONNECT via proxy status: %r" % status)
-
- return sock
-
-
-def read_headers(sock):
- status = None
- status_message = None
- headers = {}
- trace("--- response header ---")
-
- while True:
- line = recv_line(sock)
- line = line.decode('utf-8').strip()
- if not line:
- break
- trace(line)
- if not status:
-
- status_info = line.split(" ", 2)
- status = int(status_info[1])
- if len(status_info) > 2:
- status_message = status_info[2]
- else:
- kv = line.split(":", 1)
- if len(kv) == 2:
- key, value = kv
- headers[key.lower()] = value.strip()
- else:
- raise WebSocketException("Invalid header")
-
- trace("-----------------------")
-
- return status, headers, status_message
diff --git a/game/python-extra/websock/_logging.py b/game/python-extra/websock/_logging.py
deleted file mode 100644
index c947778..0000000
--- a/game/python-extra/websock/_logging.py
+++ /dev/null
@@ -1,84 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1335 USA
-
-"""
-import logging
-
-_logger = logging.getLogger('websocket')
-try:
- from logging import NullHandler
-except ImportError:
- class NullHandler(logging.Handler):
- def emit(self, record):
- pass
-
-_logger.addHandler(NullHandler())
-
-_traceEnabled = False
-
-__all__ = ["enableTrace", "dump", "error", "warning", "debug", "trace",
- "isEnabledForError", "isEnabledForDebug", "isEnabledForTrace"]
-
-
-def enableTrace(traceable, handler = logging.StreamHandler()):
- """
- turn on/off the traceability.
-
- traceable: boolean value. if set True, traceability is enabled.
- """
- global _traceEnabled
- _traceEnabled = traceable
- if traceable:
- _logger.addHandler(handler)
- _logger.setLevel(logging.DEBUG)
-
-def dump(title, message):
- if _traceEnabled:
- _logger.debug("--- " + title + " ---")
- _logger.debug(message)
- _logger.debug("-----------------------")
-
-
-def error(msg):
- _logger.error(msg)
-
-
-def warning(msg):
- _logger.warning(msg)
-
-
-def debug(msg):
- _logger.debug(msg)
-
-
-def trace(msg):
- if _traceEnabled:
- _logger.debug(msg)
-
-
-def isEnabledForError():
- return _logger.isEnabledFor(logging.ERROR)
-
-
-def isEnabledForDebug():
- return _logger.isEnabledFor(logging.DEBUG)
-
-def isEnabledForTrace():
- return _traceEnabled
diff --git a/game/python-extra/websock/_socket.py b/game/python-extra/websock/_socket.py
deleted file mode 100644
index 7be3913..0000000
--- a/game/python-extra/websock/_socket.py
+++ /dev/null
@@ -1,166 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1335 USA
-
-"""
-import errno
-import select
-import socket
-
-import six
-import sys
-
-from ._exceptions import *
-from ._ssl_compat import *
-from ._utils import *
-
-DEFAULT_SOCKET_OPTION = [(socket.SOL_TCP, socket.TCP_NODELAY, 1)]
-if hasattr(socket, "SO_KEEPALIVE"):
- DEFAULT_SOCKET_OPTION.append((socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1))
-if hasattr(socket, "TCP_KEEPIDLE"):
- DEFAULT_SOCKET_OPTION.append((socket.SOL_TCP, socket.TCP_KEEPIDLE, 30))
-if hasattr(socket, "TCP_KEEPINTVL"):
- DEFAULT_SOCKET_OPTION.append((socket.SOL_TCP, socket.TCP_KEEPINTVL, 10))
-if hasattr(socket, "TCP_KEEPCNT"):
- DEFAULT_SOCKET_OPTION.append((socket.SOL_TCP, socket.TCP_KEEPCNT, 3))
-
-_default_timeout = None
-
-__all__ = ["DEFAULT_SOCKET_OPTION", "sock_opt", "setdefaulttimeout", "getdefaulttimeout",
- "recv", "recv_line", "send"]
-
-
-class sock_opt(object):
-
- def __init__(self, sockopt, sslopt):
- if sockopt is None:
- sockopt = []
- if sslopt is None:
- sslopt = {}
- self.sockopt = sockopt
- self.sslopt = sslopt
- self.timeout = None
-
-
-def setdefaulttimeout(timeout):
- """
- Set the global timeout setting to connect.
-
- timeout: default socket timeout time. This value is second.
- """
- global _default_timeout
- _default_timeout = timeout
-
-
-def getdefaulttimeout():
- """
- Return the global timeout setting(second) to connect.
- """
- return _default_timeout
-
-
-def recv(sock, bufsize):
- if not sock:
- raise WebSocketConnectionClosedException("socket is already closed.")
-
- def _recv():
- try:
- return sock.recv(bufsize)
- except SSLWantReadError:
- pass
- except socket.error as exc:
- error_code = extract_error_code(exc)
- if error_code is None:
- raise
- if error_code != errno.EAGAIN or error_code != errno.EWOULDBLOCK:
- raise
-
- r, w, e = select.select((sock, ), (), (), sock.gettimeout())
- if r:
- return sock.recv(bufsize)
-
- try:
- if sock.gettimeout() == 0:
- bytes_ = sock.recv(bufsize)
- else:
- bytes_ = _recv()
- except socket.timeout as e:
- message = extract_err_message(e)
- raise WebSocketTimeoutException(message)
- except SSLError as e:
- message = extract_err_message(e)
- if isinstance(message, str) and 'timed out' in message:
- raise WebSocketTimeoutException(message)
- else:
- raise
-
- if not bytes_:
- raise WebSocketConnectionClosedException(
- "Connection is already closed.")
-
- return bytes_
-
-
-def recv_line(sock):
- line = []
- while True:
- c = recv(sock, 1)
- line.append(c)
- if c == six.b("\n"):
- break
- return six.b("").join(line)
-
-
-def send(sock, data):
- if isinstance(data, six.text_type):
- data = data.encode('utf-8')
-
- if not sock:
- raise WebSocketConnectionClosedException("socket is already closed.")
-
- def _send():
- try:
- return sock.send(data)
- except SSLWantWriteError:
- pass
- except socket.error as exc:
- error_code = extract_error_code(exc)
- if error_code is None:
- raise
- if error_code != errno.EAGAIN or error_code != errno.EWOULDBLOCK:
- raise
-
- r, w, e = select.select((), (sock, ), (), sock.gettimeout())
- if w:
- return sock.send(data)
-
- try:
- if sock.gettimeout() == 0:
- return sock.send(data)
- else:
- return _send()
- except socket.timeout as e:
- message = extract_err_message(e)
- raise WebSocketTimeoutException(message)
- except Exception as e:
- message = extract_err_message(e)
- if isinstance(message, str) and "timed out" in message:
- raise WebSocketTimeoutException(message)
- else:
- raise
diff --git a/game/python-extra/websock/_ssl_compat.py b/game/python-extra/websock/_ssl_compat.py
deleted file mode 100644
index 96cd173..0000000
--- a/game/python-extra/websock/_ssl_compat.py
+++ /dev/null
@@ -1,54 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1335 USA
-
-"""
-__all__ = ["HAVE_SSL", "ssl", "SSLError", "SSLWantReadError", "SSLWantWriteError"]
-
-try:
- import ssl
- from ssl import SSLError
- from ssl import SSLWantReadError
- from ssl import SSLWantWriteError
- if hasattr(ssl, 'SSLContext') and hasattr(ssl.SSLContext, 'check_hostname'):
- HAVE_CONTEXT_CHECK_HOSTNAME = True
- else:
- HAVE_CONTEXT_CHECK_HOSTNAME = False
- if hasattr(ssl, "match_hostname"):
- from ssl import match_hostname
- else:
- from backports.ssl_match_hostname import match_hostname
- __all__.append("match_hostname")
- __all__.append("HAVE_CONTEXT_CHECK_HOSTNAME")
-
- HAVE_SSL = True
-except ImportError:
- # dummy class of SSLError for ssl none-support environment.
- class SSLError(Exception):
- pass
-
- class SSLWantReadError(Exception):
- pass
-
- class SSLWantWriteError(Exception):
- pass
-
- ssl = lambda: None
-
- HAVE_SSL = False
diff --git a/game/python-extra/websock/_url.py b/game/python-extra/websock/_url.py
deleted file mode 100644
index a394fc3..0000000
--- a/game/python-extra/websock/_url.py
+++ /dev/null
@@ -1,164 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1335 USA
-
-"""
-
-import os
-import socket
-import struct
-
-from six.moves.urllib.parse import urlparse
-
-
-__all__ = ["parse_url", "get_proxy_info"]
-
-
-def parse_url(url):
- """
- parse url and the result is tuple of
- (hostname, port, resource path and the flag of secure mode)
-
- url: url string.
- """
- if ":" not in url:
- raise ValueError("url is invalid")
-
- scheme, url = url.split(":", 1)
-
- parsed = urlparse(url, scheme="ws")
- if parsed.hostname:
- hostname = parsed.hostname
- else:
- raise ValueError("hostname is invalid")
- port = 0
- if parsed.port:
- port = parsed.port
-
- is_secure = False
- if scheme == "ws":
- if not port:
- port = 80
- elif scheme == "wss":
- is_secure = True
- if not port:
- port = 443
- else:
- raise ValueError("scheme %s is invalid" % scheme)
-
- if parsed.path:
- resource = parsed.path
- else:
- resource = "/"
-
- if parsed.query:
- resource += "?" + parsed.query
-
- return hostname, port, resource, is_secure
-
-
-DEFAULT_NO_PROXY_HOST = ["localhost", "127.0.0.1"]
-
-
-def _is_ip_address(addr):
- try:
- socket.inet_aton(addr)
- except socket.error:
- return False
- else:
- return True
-
-
-def _is_subnet_address(hostname):
- try:
- addr, netmask = hostname.split("/")
- return _is_ip_address(addr) and 0 <= int(netmask) < 32
- except ValueError:
- return False
-
-
-def _is_address_in_network(ip, net):
- ipaddr = struct.unpack('I', socket.inet_aton(ip))[0]
- netaddr, bits = net.split('/')
- netmask = struct.unpack('I', socket.inet_aton(netaddr))[0] & ((2 << int(bits) - 1) - 1)
- return ipaddr & netmask == netmask
-
-
-def _is_no_proxy_host(hostname, no_proxy):
- if not no_proxy:
- v = os.environ.get("no_proxy", "").replace(" ", "")
- if v:
- no_proxy = v.split(",")
- if not no_proxy:
- no_proxy = DEFAULT_NO_PROXY_HOST
-
- if hostname in no_proxy:
- return True
- elif _is_ip_address(hostname):
- return any([_is_address_in_network(hostname, subnet) for subnet in no_proxy if _is_subnet_address(subnet)])
-
- return False
-
-
-def get_proxy_info(
- hostname, is_secure, proxy_host=None, proxy_port=0, proxy_auth=None,
- no_proxy=None, proxy_type='http'):
- """
- try to retrieve proxy host and port from environment
- if not provided in options.
- result is (proxy_host, proxy_port, proxy_auth).
- proxy_auth is tuple of username and password
- of proxy authentication information.
-
- hostname: websocket server name.
-
- is_secure: is the connection secure? (wss)
- looks for "https_proxy" in env
- before falling back to "http_proxy"
-
- options: "http_proxy_host" - http proxy host name.
- "http_proxy_port" - http proxy port.
- "http_no_proxy" - host names, which doesn't use proxy.
- "http_proxy_auth" - http proxy auth information.
- tuple of username and password.
- default is None
- "proxy_type" - if set to "socks5" PySocks wrapper
- will be used in place of a http proxy.
- default is "http"
- """
- if _is_no_proxy_host(hostname, no_proxy):
- return None, 0, None
-
- if proxy_host:
- port = proxy_port
- auth = proxy_auth
- return proxy_host, port, auth
-
- env_keys = ["http_proxy"]
- if is_secure:
- env_keys.insert(0, "https_proxy")
-
- for key in env_keys:
- value = os.environ.get(key, None)
- if value:
- proxy = urlparse(value)
- auth = (proxy.username, proxy.password) if proxy.username else None
- return proxy.hostname, proxy.port, auth
-
- return None, 0, None
diff --git a/game/python-extra/websock/_utils.py b/game/python-extra/websock/_utils.py
deleted file mode 100644
index 32ee12e..0000000
--- a/game/python-extra/websock/_utils.py
+++ /dev/null
@@ -1,111 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1335 USA
-
-"""
-import six
-
-__all__ = ["NoLock", "validate_utf8", "extract_err_message", "extract_error_code"]
-
-
-class NoLock(object):
-
- def __enter__(self):
- pass
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
-
-try:
- # If wsaccel is available we use compiled routines to validate UTF-8
- # strings.
- from wsaccel.utf8validator import Utf8Validator
-
- def _validate_utf8(utfbytes):
- return Utf8Validator().validate(utfbytes)[0]
-
-except ImportError:
- # UTF-8 validator
- # python implementation of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
-
- _UTF8_ACCEPT = 0
- _UTF8_REJECT = 12
-
- _UTF8D = [
- # The first part of the table maps bytes to character classes that
- # to reduce the size of the transition table and create bitmasks.
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
- 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
- 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
- 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
-
- # The second part is a transition table that maps a combination
- # of a state of the automaton and a character class to a state.
- 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
- 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
- 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
- 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
- 12,36,12,12,12,12,12,12,12,12,12,12, ]
-
- def _decode(state, codep, ch):
- tp = _UTF8D[ch]
-
- codep = (ch & 0x3f) | (codep << 6) if (
- state != _UTF8_ACCEPT) else (0xff >> tp) & ch
- state = _UTF8D[256 + state + tp]
-
- return state, codep
-
- def _validate_utf8(utfbytes):
- state = _UTF8_ACCEPT
- codep = 0
- for i in utfbytes:
- if six.PY2:
- i = ord(i)
- state, codep = _decode(state, codep, i)
- if state == _UTF8_REJECT:
- return False
-
- return True
-
-
-def validate_utf8(utfbytes):
- """
- validate utf8 byte string.
- utfbytes: utf byte string to check.
- return value: if valid utf8 string, return true. Otherwise, return false.
- """
- return _validate_utf8(utfbytes)
-
-
-def extract_err_message(exception):
- if exception.args:
- return exception.args[0]
- else:
- return None
-
-
-def extract_error_code(exception):
- if exception.args and len(exception.args) > 1:
- return exception.args[0] if isinstance(exception.args[0], int) else None
diff --git a/game/python-extra/websock/tests/data/header01.txt b/game/python-extra/websock/tests/data/header01.txt
deleted file mode 100644
index 3142b43..0000000
--- a/game/python-extra/websock/tests/data/header01.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-HTTP/1.1 101 WebSocket Protocol Handshake
-Connection: Upgrade
-Upgrade: WebSocket
-Sec-WebSocket-Accept: Kxep+hNu9n51529fGidYu7a3wO0=
-some_header: something
-
diff --git a/game/python-extra/websock/tests/data/header02.txt b/game/python-extra/websock/tests/data/header02.txt
deleted file mode 100644
index a9dd2ce..0000000
--- a/game/python-extra/websock/tests/data/header02.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-HTTP/1.1 101 WebSocket Protocol Handshake
-Connection: Upgrade
-Upgrade WebSocket
-Sec-WebSocket-Accept: Kxep+hNu9n51529fGidYu7a3wO0=
-some_header: something
-
diff --git a/game/python-extra/websock/tests/test_cookiejar.py b/game/python-extra/websock/tests/test_cookiejar.py
deleted file mode 100644
index c40a00b..0000000
--- a/game/python-extra/websock/tests/test_cookiejar.py
+++ /dev/null
@@ -1,98 +0,0 @@
-import unittest
-
-from websocket._cookiejar import SimpleCookieJar
-
-try:
- import Cookie
-except:
- import http.cookies as Cookie
-
-
-class CookieJarTest(unittest.TestCase):
- def testAdd(self):
- cookie_jar = SimpleCookieJar()
- cookie_jar.add("")
- self.assertFalse(cookie_jar.jar, "Cookie with no domain should not be added to the jar")
-
- cookie_jar = SimpleCookieJar()
- cookie_jar.add("a=b")
- self.assertFalse(cookie_jar.jar, "Cookie with no domain should not be added to the jar")
-
- cookie_jar = SimpleCookieJar()
- cookie_jar.add("a=b; domain=.abc")
- self.assertTrue(".abc" in cookie_jar.jar)
-
- cookie_jar = SimpleCookieJar()
- cookie_jar.add("a=b; domain=abc")
- self.assertTrue(".abc" in cookie_jar.jar)
- self.assertTrue("abc" not in cookie_jar.jar)
-
- cookie_jar = SimpleCookieJar()
- cookie_jar.add("a=b; c=d; domain=abc")
- self.assertEquals(cookie_jar.get("abc"), "a=b; c=d")
-
- cookie_jar = SimpleCookieJar()
- cookie_jar.add("a=b; c=d; domain=abc")
- cookie_jar.add("e=f; domain=abc")
- self.assertEquals(cookie_jar.get("abc"), "a=b; c=d; e=f")
-
- cookie_jar = SimpleCookieJar()
- cookie_jar.add("a=b; c=d; domain=abc")
- cookie_jar.add("e=f; domain=.abc")
- self.assertEquals(cookie_jar.get("abc"), "a=b; c=d; e=f")
-
- cookie_jar = SimpleCookieJar()
- cookie_jar.add("a=b; c=d; domain=abc")
- cookie_jar.add("e=f; domain=xyz")
- self.assertEquals(cookie_jar.get("abc"), "a=b; c=d")
- self.assertEquals(cookie_jar.get("xyz"), "e=f")
- self.assertEquals(cookie_jar.get("something"), "")
-
- def testSet(self):
- cookie_jar = SimpleCookieJar()
- cookie_jar.set("a=b")
- self.assertFalse(cookie_jar.jar, "Cookie with no domain should not be added to the jar")
-
- cookie_jar = SimpleCookieJar()
- cookie_jar.set("a=b; domain=.abc")
- self.assertTrue(".abc" in cookie_jar.jar)
-
- cookie_jar = SimpleCookieJar()
- cookie_jar.set("a=b; domain=abc")
- self.assertTrue(".abc" in cookie_jar.jar)
- self.assertTrue("abc" not in cookie_jar.jar)
-
- cookie_jar = SimpleCookieJar()
- cookie_jar.set("a=b; c=d; domain=abc")
- self.assertEquals(cookie_jar.get("abc"), "a=b; c=d")
-
- cookie_jar = SimpleCookieJar()
- cookie_jar.set("a=b; c=d; domain=abc")
- cookie_jar.set("e=f; domain=abc")
- self.assertEquals(cookie_jar.get("abc"), "e=f")
-
- cookie_jar = SimpleCookieJar()
- cookie_jar.set("a=b; c=d; domain=abc")
- cookie_jar.set("e=f; domain=.abc")
- self.assertEquals(cookie_jar.get("abc"), "e=f")
-
- cookie_jar = SimpleCookieJar()
- cookie_jar.set("a=b; c=d; domain=abc")
- cookie_jar.set("e=f; domain=xyz")
- self.assertEquals(cookie_jar.get("abc"), "a=b; c=d")
- self.assertEquals(cookie_jar.get("xyz"), "e=f")
- self.assertEquals(cookie_jar.get("something"), "")
-
- def testGet(self):
- cookie_jar = SimpleCookieJar()
- cookie_jar.set("a=b; c=d; domain=abc.com")
- self.assertEquals(cookie_jar.get("abc.com"), "a=b; c=d")
- self.assertEquals(cookie_jar.get("x.abc.com"), "a=b; c=d")
- self.assertEquals(cookie_jar.get("abc.com.es"), "")
- self.assertEquals(cookie_jar.get("xabc.com"), "")
-
- cookie_jar.set("a=b; c=d; domain=.abc.com")
- self.assertEquals(cookie_jar.get("abc.com"), "a=b; c=d")
- self.assertEquals(cookie_jar.get("x.abc.com"), "a=b; c=d")
- self.assertEquals(cookie_jar.get("abc.com.es"), "")
- self.assertEquals(cookie_jar.get("xabc.com"), "")
diff --git a/game/python-extra/websock/tests/test_websocket.py b/game/python-extra/websock/tests/test_websocket.py
deleted file mode 100644
index 8b131bb..0000000
--- a/game/python-extra/websock/tests/test_websocket.py
+++ /dev/null
@@ -1,665 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-
-import sys
-sys.path[0:0] = [""]
-
-import os
-import os.path
-import socket
-
-import six
-
-# websocket-client
-import websocket as ws
-from websocket._handshake import _create_sec_websocket_key, \
- _validate as _validate_header
-from websocket._http import read_headers
-from websocket._url import get_proxy_info, parse_url
-from websocket._utils import validate_utf8
-
-if six.PY3:
- from base64 import decodebytes as base64decode
-else:
- from base64 import decodestring as base64decode
-
-if sys.version_info[0] == 2 and sys.version_info[1] < 7:
- import unittest2 as unittest
-else:
- import unittest
-
-try:
- from ssl import SSLError
-except ImportError:
- # dummy class of SSLError for ssl none-support environment.
- class SSLError(Exception):
- pass
-
-# Skip test to access the internet.
-TEST_WITH_INTERNET = os.environ.get('TEST_WITH_INTERNET', '0') == '1'
-
-# Skip Secure WebSocket test.
-TEST_SECURE_WS = True
-TRACEABLE = True
-
-
-def create_mask_key(_):
- return "abcd"
-
-
-class SockMock(object):
- def __init__(self):
- self.data = []
- self.sent = []
-
- def add_packet(self, data):
- self.data.append(data)
-
- def gettimeout(self):
- return None
-
- def recv(self, bufsize):
- if self.data:
- e = self.data.pop(0)
- if isinstance(e, Exception):
- raise e
- if len(e) > bufsize:
- self.data.insert(0, e[bufsize:])
- return e[:bufsize]
-
- def send(self, data):
- self.sent.append(data)
- return len(data)
-
- def close(self):
- pass
-
-
-class HeaderSockMock(SockMock):
-
- def __init__(self, fname):
- SockMock.__init__(self)
- path = os.path.join(os.path.dirname(__file__), fname)
- with open(path, "rb") as f:
- self.add_packet(f.read())
-
-
-class WebSocketTest(unittest.TestCase):
- def setUp(self):
- ws.enableTrace(TRACEABLE)
-
- def tearDown(self):
- pass
-
- def testDefaultTimeout(self):
- self.assertEqual(ws.getdefaulttimeout(), None)
- ws.setdefaulttimeout(10)
- self.assertEqual(ws.getdefaulttimeout(), 10)
- ws.setdefaulttimeout(None)
-
- def testParseUrl(self):
- p = parse_url("ws://www.example.com/r")
- self.assertEqual(p[0], "www.example.com")
- self.assertEqual(p[1], 80)
- self.assertEqual(p[2], "/r")
- self.assertEqual(p[3], False)
-
- p = parse_url("ws://www.example.com/r/")
- self.assertEqual(p[0], "www.example.com")
- self.assertEqual(p[1], 80)
- self.assertEqual(p[2], "/r/")
- self.assertEqual(p[3], False)
-
- p = parse_url("ws://www.example.com/")
- self.assertEqual(p[0], "www.example.com")
- self.assertEqual(p[1], 80)
- self.assertEqual(p[2], "/")
- self.assertEqual(p[3], False)
-
- p = parse_url("ws://www.example.com")
- self.assertEqual(p[0], "www.example.com")
- self.assertEqual(p[1], 80)
- self.assertEqual(p[2], "/")
- self.assertEqual(p[3], False)
-
- p = parse_url("ws://www.example.com:8080/r")
- self.assertEqual(p[0], "www.example.com")
- self.assertEqual(p[1], 8080)
- self.assertEqual(p[2], "/r")
- self.assertEqual(p[3], False)
-
- p = parse_url("ws://www.example.com:8080/")
- self.assertEqual(p[0], "www.example.com")
- self.assertEqual(p[1], 8080)
- self.assertEqual(p[2], "/")
- self.assertEqual(p[3], False)
-
- p = parse_url("ws://www.example.com:8080")
- self.assertEqual(p[0], "www.example.com")
- self.assertEqual(p[1], 8080)
- self.assertEqual(p[2], "/")
- self.assertEqual(p[3], False)
-
- p = parse_url("wss://www.example.com:8080/r")
- self.assertEqual(p[0], "www.example.com")
- self.assertEqual(p[1], 8080)
- self.assertEqual(p[2], "/r")
- self.assertEqual(p[3], True)
-
- p = parse_url("wss://www.example.com:8080/r?key=value")
- self.assertEqual(p[0], "www.example.com")
- self.assertEqual(p[1], 8080)
- self.assertEqual(p[2], "/r?key=value")
- self.assertEqual(p[3], True)
-
- self.assertRaises(ValueError, parse_url, "http://www.example.com/r")
-
- if sys.version_info[0] == 2 and sys.version_info[1] < 7:
- return
-
- p = parse_url("ws://[2a03:4000:123:83::3]/r")
- self.assertEqual(p[0], "2a03:4000:123:83::3")
- self.assertEqual(p[1], 80)
- self.assertEqual(p[2], "/r")
- self.assertEqual(p[3], False)
-
- p = parse_url("ws://[2a03:4000:123:83::3]:8080/r")
- self.assertEqual(p[0], "2a03:4000:123:83::3")
- self.assertEqual(p[1], 8080)
- self.assertEqual(p[2], "/r")
- self.assertEqual(p[3], False)
-
- p = parse_url("wss://[2a03:4000:123:83::3]/r")
- self.assertEqual(p[0], "2a03:4000:123:83::3")
- self.assertEqual(p[1], 443)
- self.assertEqual(p[2], "/r")
- self.assertEqual(p[3], True)
-
- p = parse_url("wss://[2a03:4000:123:83::3]:8080/r")
- self.assertEqual(p[0], "2a03:4000:123:83::3")
- self.assertEqual(p[1], 8080)
- self.assertEqual(p[2], "/r")
- self.assertEqual(p[3], True)
-
- def testWSKey(self):
- key = _create_sec_websocket_key()
- self.assertTrue(key != 24)
- self.assertTrue(six.u("¥n") not in key)
-
- def testWsUtils(self):
- key = "c6b8hTg4EeGb2gQMztV1/g=="
- required_header = {
- "upgrade": "websocket",
- "connection": "upgrade",
- "sec-websocket-accept": "Kxep+hNu9n51529fGidYu7a3wO0=",
- }
- self.assertEqual(_validate_header(required_header, key, None), (True, None))
-
- header = required_header.copy()
- header["upgrade"] = "http"
- self.assertEqual(_validate_header(header, key, None), (False, None))
- del header["upgrade"]
- self.assertEqual(_validate_header(header, key, None), (False, None))
-
- header = required_header.copy()
- header["connection"] = "something"
- self.assertEqual(_validate_header(header, key, None), (False, None))
- del header["connection"]
- self.assertEqual(_validate_header(header, key, None), (False, None))
-
- header = required_header.copy()
- header["sec-websocket-accept"] = "something"
- self.assertEqual(_validate_header(header, key, None), (False, None))
- del header["sec-websocket-accept"]
- self.assertEqual(_validate_header(header, key, None), (False, None))
-
- header = required_header.copy()
- header["sec-websocket-protocol"] = "sub1"
- self.assertEqual(_validate_header(header, key, ["sub1", "sub2"]), (True, "sub1"))
- self.assertEqual(_validate_header(header, key, ["sub2", "sub3"]), (False, None))
-
- header = required_header.copy()
- header["sec-websocket-protocol"] = "sUb1"
- self.assertEqual(_validate_header(header, key, ["Sub1", "suB2"]), (True, "sub1"))
-
-
- def testReadHeader(self):
- status, header, status_message = read_headers(HeaderSockMock("data/header01.txt"))
- self.assertEqual(status, 101)
- self.assertEqual(header["connection"], "Upgrade")
-
- HeaderSockMock("data/header02.txt")
- self.assertRaises(ws.WebSocketException, read_headers, HeaderSockMock("data/header02.txt"))
-
- def testSend(self):
- # TODO: add longer frame data
- sock = ws.WebSocket()
- sock.set_mask_key(create_mask_key)
- s = sock.sock = HeaderSockMock("data/header01.txt")
- sock.send("Hello")
- self.assertEqual(s.sent[0], six.b("\x81\x85abcd)\x07\x0f\x08\x0e"))
-
- sock.send("こんにちは")
- self.assertEqual(s.sent[1], six.b("\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc"))
-
- sock.send(u"こんにちは")
- self.assertEqual(s.sent[1], six.b("\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc"))
-
- sock.send("x" * 127)
-
- def testRecv(self):
- # TODO: add longer frame data
- sock = ws.WebSocket()
- s = sock.sock = SockMock()
- something = six.b("\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc")
- s.add_packet(something)
- data = sock.recv()
- self.assertEqual(data, "こんにちは")
-
- s.add_packet(six.b("\x81\x85abcd)\x07\x0f\x08\x0e"))
- data = sock.recv()
- self.assertEqual(data, "Hello")
-
- @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
- def testIter(self):
- count = 2
- for _ in ws.create_connection('ws://stream.meetup.com/2/rsvps'):
- count -= 1
- if count == 0:
- break
-
- @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
- def testNext(self):
- sock = ws.create_connection('ws://stream.meetup.com/2/rsvps')
- self.assertEqual(str, type(next(sock)))
-
- def testInternalRecvStrict(self):
- sock = ws.WebSocket()
- s = sock.sock = SockMock()
- s.add_packet(six.b("foo"))
- s.add_packet(socket.timeout())
- s.add_packet(six.b("bar"))
- # s.add_packet(SSLError("The read operation timed out"))
- s.add_packet(six.b("baz"))
- with self.assertRaises(ws.WebSocketTimeoutException):
- sock.frame_buffer.recv_strict(9)
- # if six.PY2:
- # with self.assertRaises(ws.WebSocketTimeoutException):
- # data = sock._recv_strict(9)
- # else:
- # with self.assertRaises(SSLError):
- # data = sock._recv_strict(9)
- data = sock.frame_buffer.recv_strict(9)
- self.assertEqual(data, six.b("foobarbaz"))
- with self.assertRaises(ws.WebSocketConnectionClosedException):
- sock.frame_buffer.recv_strict(1)
-
- def testRecvTimeout(self):
- sock = ws.WebSocket()
- s = sock.sock = SockMock()
- s.add_packet(six.b("\x81"))
- s.add_packet(socket.timeout())
- s.add_packet(six.b("\x8dabcd\x29\x07\x0f\x08\x0e"))
- s.add_packet(socket.timeout())
- s.add_packet(six.b("\x4e\x43\x33\x0e\x10\x0f\x00\x40"))
- with self.assertRaises(ws.WebSocketTimeoutException):
- sock.recv()
- with self.assertRaises(ws.WebSocketTimeoutException):
- sock.recv()
- data = sock.recv()
- self.assertEqual(data, "Hello, World!")
- with self.assertRaises(ws.WebSocketConnectionClosedException):
- sock.recv()
-
- def testRecvWithSimpleFragmentation(self):
- sock = ws.WebSocket()
- s = sock.sock = SockMock()
- # OPCODE=TEXT, FIN=0, MSG="Brevity is "
- s.add_packet(six.b("\x01\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C"))
- # OPCODE=CONT, FIN=1, MSG="the soul of wit"
- s.add_packet(six.b("\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17"))
- data = sock.recv()
- self.assertEqual(data, "Brevity is the soul of wit")
- with self.assertRaises(ws.WebSocketConnectionClosedException):
- sock.recv()
-
- def testRecvWithFireEventOfFragmentation(self):
- sock = ws.WebSocket(fire_cont_frame=True)
- s = sock.sock = SockMock()
- # OPCODE=TEXT, FIN=0, MSG="Brevity is "
- s.add_packet(six.b("\x01\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C"))
- # OPCODE=CONT, FIN=0, MSG="Brevity is "
- s.add_packet(six.b("\x00\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C"))
- # OPCODE=CONT, FIN=1, MSG="the soul of wit"
- s.add_packet(six.b("\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17"))
-
- _, data = sock.recv_data()
- self.assertEqual(data, six.b("Brevity is "))
- _, data = sock.recv_data()
- self.assertEqual(data, six.b("Brevity is "))
- _, data = sock.recv_data()
- self.assertEqual(data, six.b("the soul of wit"))
-
- # OPCODE=CONT, FIN=0, MSG="Brevity is "
- s.add_packet(six.b("\x80\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C"))
-
- with self.assertRaises(ws.WebSocketException):
- sock.recv_data()
-
- with self.assertRaises(ws.WebSocketConnectionClosedException):
- sock.recv()
-
- def testClose(self):
- sock = ws.WebSocket()
- sock.sock = SockMock()
- sock.connected = True
- sock.close()
- self.assertEqual(sock.connected, False)
-
- sock = ws.WebSocket()
- s = sock.sock = SockMock()
- sock.connected = True
- s.add_packet(six.b('\x88\x80\x17\x98p\x84'))
- sock.recv()
- self.assertEqual(sock.connected, False)
-
- def testRecvContFragmentation(self):
- sock = ws.WebSocket()
- s = sock.sock = SockMock()
- # OPCODE=CONT, FIN=1, MSG="the soul of wit"
- s.add_packet(six.b("\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17"))
- self.assertRaises(ws.WebSocketException, sock.recv)
-
- def testRecvWithProlongedFragmentation(self):
- sock = ws.WebSocket()
- s = sock.sock = SockMock()
- # OPCODE=TEXT, FIN=0, MSG="Once more unto the breach, "
- s.add_packet(six.b("\x01\x9babcd.\x0c\x00\x01A\x0f\x0c\x16\x04B\x16\n\x15"
- "\rC\x10\t\x07C\x06\x13\x07\x02\x07\tNC"))
- # OPCODE=CONT, FIN=0, MSG="dear friends, "
- s.add_packet(six.b("\x00\x8eabcd\x05\x07\x02\x16A\x04\x11\r\x04\x0c\x07"
- "\x17MB"))
- # OPCODE=CONT, FIN=1, MSG="once more"
- s.add_packet(six.b("\x80\x89abcd\x0e\x0c\x00\x01A\x0f\x0c\x16\x04"))
- data = sock.recv()
- self.assertEqual(
- data,
- "Once more unto the breach, dear friends, once more")
- with self.assertRaises(ws.WebSocketConnectionClosedException):
- sock.recv()
-
- def testRecvWithFragmentationAndControlFrame(self):
- sock = ws.WebSocket()
- sock.set_mask_key(create_mask_key)
- s = sock.sock = SockMock()
- # OPCODE=TEXT, FIN=0, MSG="Too much "
- s.add_packet(six.b("\x01\x89abcd5\r\x0cD\x0c\x17\x00\x0cA"))
- # OPCODE=PING, FIN=1, MSG="Please PONG this"
- s.add_packet(six.b("\x89\x90abcd1\x0e\x06\x05\x12\x07C4.,$D\x15\n\n\x17"))
- # OPCODE=CONT, FIN=1, MSG="of a good thing"
- s.add_packet(six.b("\x80\x8fabcd\x0e\x04C\x05A\x05\x0c\x0b\x05B\x17\x0c"
- "\x08\x0c\x04"))
- data = sock.recv()
- self.assertEqual(data, "Too much of a good thing")
- with self.assertRaises(ws.WebSocketConnectionClosedException):
- sock.recv()
- self.assertEqual(
- s.sent[0],
- six.b("\x8a\x90abcd1\x0e\x06\x05\x12\x07C4.,$D\x15\n\n\x17"))
-
- @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
- def testWebSocket(self):
- s = ws.create_connection("ws://echo.websocket.org/")
- self.assertNotEqual(s, None)
- s.send("Hello, World")
- result = s.recv()
- self.assertEqual(result, "Hello, World")
-
- s.send(u"こにゃにゃちは、世界")
- result = s.recv()
- self.assertEqual(result, "こにゃにゃちは、世界")
- s.close()
-
- @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
- def testPingPong(self):
- s = ws.create_connection("ws://echo.websocket.org/")
- self.assertNotEqual(s, None)
- s.ping("Hello")
- s.pong("Hi")
- s.close()
-
- @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
- @unittest.skipUnless(TEST_SECURE_WS, "wss://echo.websocket.org doesn't work well.")
- def testSecureWebSocket(self):
- if 1:
- import ssl
- s = ws.create_connection("wss://echo.websocket.org/")
- self.assertNotEqual(s, None)
- self.assertTrue(isinstance(s.sock, ssl.SSLSocket))
- s.send("Hello, World")
- result = s.recv()
- self.assertEqual(result, "Hello, World")
- s.send(u"こにゃにゃちは、世界")
- result = s.recv()
- self.assertEqual(result, "こにゃにゃちは、世界")
- s.close()
- #except:
- # pass
-
- @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
- def testWebSocketWihtCustomHeader(self):
- s = ws.create_connection("ws://echo.websocket.org/",
- headers={"User-Agent": "PythonWebsocketClient"})
- self.assertNotEqual(s, None)
- s.send("Hello, World")
- result = s.recv()
- self.assertEqual(result, "Hello, World")
- s.close()
-
- @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
- def testAfterClose(self):
- s = ws.create_connection("ws://echo.websocket.org/")
- self.assertNotEqual(s, None)
- s.close()
- self.assertRaises(ws.WebSocketConnectionClosedException, s.send, "Hello")
- self.assertRaises(ws.WebSocketConnectionClosedException, s.recv)
-
- def testNonce(self):
- """ WebSocket key should be a random 16-byte nonce.
- """
- key = _create_sec_websocket_key()
- nonce = base64decode(key.encode("utf-8"))
- self.assertEqual(16, len(nonce))
-
-
-class WebSocketAppTest(unittest.TestCase):
-
- class NotSetYet(object):
- """ A marker class for signalling that a value hasn't been set yet.
- """
-
- def setUp(self):
- ws.enableTrace(TRACEABLE)
-
- WebSocketAppTest.keep_running_open = WebSocketAppTest.NotSetYet()
- WebSocketAppTest.keep_running_close = WebSocketAppTest.NotSetYet()
- WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet()
-
- def tearDown(self):
- WebSocketAppTest.keep_running_open = WebSocketAppTest.NotSetYet()
- WebSocketAppTest.keep_running_close = WebSocketAppTest.NotSetYet()
- WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet()
-
- @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
- def testKeepRunning(self):
- """ A WebSocketApp should keep running as long as its self.keep_running
- is not False (in the boolean context).
- """
-
- def on_open(self, *args, **kwargs):
- """ Set the keep_running flag for later inspection and immediately
- close the connection.
- """
- WebSocketAppTest.keep_running_open = self.keep_running
-
- self.close()
-
- def on_close(self, *args, **kwargs):
- """ Set the keep_running flag for the test to use.
- """
- WebSocketAppTest.keep_running_close = self.keep_running
-
- app = ws.WebSocketApp('ws://echo.websocket.org/', on_open=on_open, on_close=on_close)
- app.run_forever()
-
- # if numpy is installed, this assertion fail
- # self.assertFalse(isinstance(WebSocketAppTest.keep_running_open,
- # WebSocketAppTest.NotSetYet))
-
- # self.assertFalse(isinstance(WebSocketAppTest.keep_running_close,
- # WebSocketAppTest.NotSetYet))
-
- # self.assertEqual(True, WebSocketAppTest.keep_running_open)
- # self.assertEqual(False, WebSocketAppTest.keep_running_close)
-
- @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
- def testSockMaskKey(self):
- """ A WebSocketApp should forward the received mask_key function down
- to the actual socket.
- """
-
- def my_mask_key_func():
- pass
-
- def on_open(self, *args, **kwargs):
- """ Set the value so the test can use it later on and immediately
- close the connection.
- """
- WebSocketAppTest.get_mask_key_id = id(self.get_mask_key)
- self.close()
-
- app = ws.WebSocketApp('ws://echo.websocket.org/', on_open=on_open, get_mask_key=my_mask_key_func)
- app.run_forever()
-
- # if numpu is installed, this assertion fail
- # Note: We can't use 'is' for comparing the functions directly, need to use 'id'.
- # self.assertEqual(WebSocketAppTest.get_mask_key_id, id(my_mask_key_func))
-
-
-class SockOptTest(unittest.TestCase):
- @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
- def testSockOpt(self):
- sockopt = ((socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),)
- s = ws.create_connection("ws://echo.websocket.org", sockopt=sockopt)
- self.assertNotEqual(s.sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY), 0)
- s.close()
-
-
-class UtilsTest(unittest.TestCase):
- def testUtf8Validator(self):
- state = validate_utf8(six.b('\xf0\x90\x80\x80'))
- self.assertEqual(state, True)
- state = validate_utf8(six.b('\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5\xed\xa0\x80edited'))
- self.assertEqual(state, False)
- state = validate_utf8(six.b(''))
- self.assertEqual(state, True)
-
-
-class ProxyInfoTest(unittest.TestCase):
- def setUp(self):
- self.http_proxy = os.environ.get("http_proxy", None)
- self.https_proxy = os.environ.get("https_proxy", None)
- if "http_proxy" in os.environ:
- del os.environ["http_proxy"]
- if "https_proxy" in os.environ:
- del os.environ["https_proxy"]
-
- def tearDown(self):
- if self.http_proxy:
- os.environ["http_proxy"] = self.http_proxy
- elif "http_proxy" in os.environ:
- del os.environ["http_proxy"]
-
- if self.https_proxy:
- os.environ["https_proxy"] = self.https_proxy
- elif "https_proxy" in os.environ:
- del os.environ["https_proxy"]
-
- def testProxyFromArgs(self):
- self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost"), ("localhost", 0, None))
- self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_port=3128), ("localhost", 3128, None))
- self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost"), ("localhost", 0, None))
- self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128), ("localhost", 3128, None))
-
- self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_auth=("a", "b")),
- ("localhost", 0, ("a", "b")))
- self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_port=3128, proxy_auth=("a", "b")),
- ("localhost", 3128, ("a", "b")))
- self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_auth=("a", "b")),
- ("localhost", 0, ("a", "b")))
- self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128, proxy_auth=("a", "b")),
- ("localhost", 3128, ("a", "b")))
-
- self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128, no_proxy=["example.com"], proxy_auth=("a", "b")),
- ("localhost", 3128, ("a", "b")))
- self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128, no_proxy=["echo.websocket.org"], proxy_auth=("a", "b")),
- (None, 0, None))
-
- def testProxyFromEnv(self):
- os.environ["http_proxy"] = "http://localhost/"
- self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, None))
- os.environ["http_proxy"] = "http://localhost:3128/"
- self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, None))
-
- os.environ["http_proxy"] = "http://localhost/"
- os.environ["https_proxy"] = "http://localhost2/"
- self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, None))
- os.environ["http_proxy"] = "http://localhost:3128/"
- os.environ["https_proxy"] = "http://localhost2:3128/"
- self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, None))
-
- os.environ["http_proxy"] = "http://localhost/"
- os.environ["https_proxy"] = "http://localhost2/"
- self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", None, None))
- os.environ["http_proxy"] = "http://localhost:3128/"
- os.environ["https_proxy"] = "http://localhost2:3128/"
- self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", 3128, None))
-
-
- os.environ["http_proxy"] = "http://a:b@localhost/"
- self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, ("a", "b")))
- os.environ["http_proxy"] = "http://a:b@localhost:3128/"
- self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, ("a", "b")))
-
- os.environ["http_proxy"] = "http://a:b@localhost/"
- os.environ["https_proxy"] = "http://a:b@localhost2/"
- self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, ("a", "b")))
- os.environ["http_proxy"] = "http://a:b@localhost:3128/"
- os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
- self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, ("a", "b")))
-
- os.environ["http_proxy"] = "http://a:b@localhost/"
- os.environ["https_proxy"] = "http://a:b@localhost2/"
- self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", None, ("a", "b")))
- os.environ["http_proxy"] = "http://a:b@localhost:3128/"
- os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
- self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", 3128, ("a", "b")))
-
- os.environ["http_proxy"] = "http://a:b@localhost/"
- os.environ["https_proxy"] = "http://a:b@localhost2/"
- os.environ["no_proxy"] = "example1.com,example2.com"
- self.assertEqual(get_proxy_info("example.1.com", True), ("localhost2", None, ("a", "b")))
- os.environ["http_proxy"] = "http://a:b@localhost:3128/"
- os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
- os.environ["no_proxy"] = "example1.com,example2.com, echo.websocket.org"
- self.assertEqual(get_proxy_info("echo.websocket.org", True), (None, 0, None))
-
- os.environ["http_proxy"] = "http://a:b@localhost:3128/"
- os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
- os.environ["no_proxy"] = "127.0.0.0/8, 192.168.0.0/16"
- self.assertEqual(get_proxy_info("127.0.0.1", False), (None, 0, None))
- self.assertEqual(get_proxy_info("192.168.1.1", False), (None, 0, None))
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/game/python-extra/ws4py/__init__.py b/game/python-extra/ws4py/__init__.py
new file mode 100644
index 0000000..1f618b0
--- /dev/null
+++ b/game/python-extra/ws4py/__init__.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of ws4py nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+import logging
+#import logging.handlers as handlers
+
+__author__ = "Sylvain Hellegouarch"
+__version__ = "0.5.1"
+__all__ = ['WS_KEY', 'WS_VERSION', 'configure_logger', 'format_addresses']
+
+WS_KEY = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+WS_VERSION = (8, 13)
+
+def configure_logger(stdout=True, filepath=None, level=logging.INFO):
+ logger = logging.getLogger('ws4py')
+ logger.setLevel(level)
+ logfmt = logging.Formatter("[%(asctime)s] %(levelname)s %(message)s")
+
+ #########
+ # @TMW2
+ #if filepath:
+ # h = handlers.RotatingFileHandler(filepath, maxBytes=10485760, backupCount=3)
+ # h.setLevel(level)
+ # h.setFormatter(logfmt)
+ # logger.addHandler(h)
+ # @TMW2
+ #########
+
+ if stdout:
+ import sys
+ h = logging.StreamHandler(sys.stdout)
+ h.setLevel(level)
+ h.setFormatter(logfmt)
+ logger.addHandler(h)
+
+ return logger
+
+def format_addresses(ws):
+ me = ws.local_address
+ peer = ws.peer_address
+ if isinstance(me, tuple) and isinstance(peer, tuple):
+ me_ip, me_port = ws.local_address
+ peer_ip, peer_port = ws.peer_address
+ return "[Local => %s:%d | Remote => %s:%d]" % (me_ip, me_port, peer_ip, peer_port)
+
+ return "[Bound to '%s']" % me
diff --git a/game/python-extra/ws4py/async_websocket.py b/game/python-extra/ws4py/async_websocket.py
new file mode 100644
index 0000000..9e2a4c7
--- /dev/null
+++ b/game/python-extra/ws4py/async_websocket.py
@@ -0,0 +1,126 @@
+# -*- coding: utf-8 -*-
+__doc__ = """
+WebSocket implementation that relies on two new Python
+features:
+
+* asyncio to provide the high-level interface above transports
+* yield from to delegate to the reading stream whenever more
+ bytes are required
+
+You can use these implementations in that context
+and benefit from those features whilst using ws4py.
+
+Strictly speaking this module probably doesn't have to
+be called async_websocket but it feels this will be its typical
+usage and is probably more readable than
+delegated_generator_websocket_on_top_of_asyncio.py
+"""
+import asyncio
+import types
+
+from ws4py.websocket import WebSocket as _WebSocket
+from ws4py.messaging import Message
+
+__all__ = ['WebSocket', 'EchoWebSocket']
+
+class WebSocket(_WebSocket):
+ def __init__(self, proto):
+ """
+ A :pep:`3156` ready websocket handler that works
+ well in a coroutine-aware loop such as the one provided
+ by the asyncio module.
+
+ The provided `proto` instance is a
+ :class:`asyncio.Protocol` subclass instance that will
+ be used internally to read and write from the
+ underlying transport.
+
+ Because the base :class:`ws4py.websocket.WebSocket`
+ class is still coupled a bit to the socket interface,
+ we have to override a little more than necessary
+ to play nice with the :pep:`3156` interface. Hopefully,
+ some day this will be cleaned out.
+ """
+ _WebSocket.__init__(self, None)
+ self.started = False
+ self.proto = proto
+
+ @property
+ def local_address(self):
+ """
+ Local endpoint address as a tuple
+ """
+ if not self._local_address:
+ self._local_address = self.proto.reader.transport.get_extra_info('sockname')
+ if len(self._local_address) == 4:
+ self._local_address = self._local_address[:2]
+ return self._local_address
+
+ @property
+ def peer_address(self):
+ """
+ Peer endpoint address as a tuple
+ """
+ if not self._peer_address:
+ self._peer_address = self.proto.reader.transport.get_extra_info('peername')
+ if len(self._peer_address) == 4:
+ self._peer_address = self._peer_address[:2]
+ return self._peer_address
+
+ def once(self):
+ """
+ The base class directly is used in conjunction with
+ the :class:`ws4py.manager.WebSocketManager` which is
+ not actually used with the asyncio implementation
+ of ws4py. So let's make it clear it shan't be used.
+ """
+ raise NotImplemented()
+
+ def close_connection(self):
+ """
+ Close the underlying transport
+ """
+ @asyncio.coroutine
+ def closeit():
+ yield from self.proto.writer.drain()
+ self.proto.writer.close()
+ asyncio.async(closeit())
+
+ def _write(self, data):
+ """
+ Write to the underlying transport
+ """
+ @asyncio.coroutine
+ def sendit(data):
+ self.proto.writer.write(data)
+ yield from self.proto.writer.drain()
+ asyncio.async(sendit(data))
+
+ @asyncio.coroutine
+ def run(self):
+ """
+ Coroutine that runs until the websocket
+ exchange is terminated. It also calls the
+ `opened()` method to indicate the exchange
+ has started.
+ """
+ self.started = True
+ try:
+ self.opened()
+ reader = self.proto.reader
+ while True:
+ data = yield from reader.read(self.reading_buffer_size)
+ if not self.process(data):
+ return False
+ finally:
+ self.terminate()
+
+ return True
+
+class EchoWebSocket(WebSocket):
+ def received_message(self, message):
+ """
+ Automatically sends back the provided ``message`` to
+ its originating endpoint.
+ """
+ self.send(message.data, message.is_binary)
diff --git a/game/python-extra/ws4py/client/__init__.py b/game/python-extra/ws4py/client/__init__.py
new file mode 100644
index 0000000..411638f
--- /dev/null
+++ b/game/python-extra/ws4py/client/__init__.py
@@ -0,0 +1,344 @@
+# -*- coding: utf-8 -*-
+from base64 import b64encode
+from hashlib import sha1
+import os
+import socket
+import ssl
+
+from ws4py import WS_KEY, WS_VERSION
+from ws4py.exc import HandshakeError
+from ws4py.websocket import WebSocket
+from ws4py.compat import urlsplit
+
+__all__ = ['WebSocketBaseClient']
+
+class WebSocketBaseClient(WebSocket):
+ def __init__(self, url, protocols=None, extensions=None,
+ heartbeat_freq=None, ssl_options=None, headers=None, exclude_headers=None):
+ """
+ A websocket client that implements :rfc:`6455` and provides a simple
+ interface to communicate with a websocket server.
+
+ This class works on its own but will block if not run in
+ its own thread.
+
+ When an instance of this class is created, a :py:mod:`socket`
+ is created. If the connection is a TCP socket,
+ the nagle's algorithm is disabled.
+
+ The address of the server will be extracted from the given
+ websocket url.
+
+ The websocket key is randomly generated, reset the
+ `key` attribute if you want to provide yours.
+
+ For instance to create a TCP client:
+
+ .. code-block:: python
+
+ >>> from ws4py.client import WebSocketBaseClient
+ >>> ws = WebSocketBaseClient('ws://localhost/ws')
+
+
+ Here is an example for a TCP client over SSL:
+
+ .. code-block:: python
+
+ >>> from ws4py.client import WebSocketBaseClient
+ >>> ws = WebSocketBaseClient('wss://localhost/ws')
+
+
+ Finally an example of a Unix-domain connection:
+
+ .. code-block:: python
+
+ >>> from ws4py.client import WebSocketBaseClient
+ >>> ws = WebSocketBaseClient('ws+unix:///tmp/my.sock')
+
+ Note that in this case, the initial Upgrade request
+ will be sent to ``/``. You may need to change this
+ by setting the resource explicitely before connecting:
+
+ .. code-block:: python
+
+ >>> from ws4py.client import WebSocketBaseClient
+ >>> ws = WebSocketBaseClient('ws+unix:///tmp/my.sock')
+ >>> ws.resource = '/ws'
+ >>> ws.connect()
+
+ You may provide extra headers by passing a list of tuples
+ which must be unicode objects.
+
+ """
+ self.url = url
+ self.host = None
+ self.scheme = None
+ self.port = None
+ self.unix_socket_path = None
+ self.resource = None
+ self.ssl_options = ssl_options or {}
+ self.extra_headers = headers or []
+ self.exclude_headers = exclude_headers or []
+ self.exclude_headers = [x.lower() for x in self.exclude_headers]
+
+ if self.scheme == "wss":
+ # Prevent check_hostname requires server_hostname (ref #187)
+ if "cert_reqs" not in self.ssl_options:
+ self.ssl_options["cert_reqs"] = ssl.CERT_NONE
+
+ self._parse_url()
+
+ if self.unix_socket_path:
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
+ else:
+ # Let's handle IPv4 and IPv6 addresses
+ # Simplified from CherryPy's code
+ try:
+ family, socktype, proto, canonname, sa = socket.getaddrinfo(self.host, self.port,
+ socket.AF_UNSPEC,
+ socket.SOCK_STREAM,
+ 0, socket.AI_PASSIVE)[0]
+ except socket.gaierror:
+ family = socket.AF_INET
+ if self.host.startswith('::'):
+ family = socket.AF_INET6
+
+ socktype = socket.SOCK_STREAM
+ proto = 0
+ canonname = ""
+ sa = (self.host, self.port, 0, 0)
+
+ sock = socket.socket(family, socktype, proto)
+ sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ if hasattr(socket, 'AF_INET6') and family == socket.AF_INET6 and \
+ self.host.startswith('::'):
+ try:
+ sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
+ except (AttributeError, socket.error):
+ pass
+
+ WebSocket.__init__(self, sock, protocols=protocols,
+ extensions=extensions,
+ heartbeat_freq=heartbeat_freq)
+
+ self.stream.always_mask = True
+ self.stream.expect_masking = False
+ self.key = b64encode(os.urandom(16))
+
+ # Adpated from: https://github.com/liris/websocket-client/blob/master/websocket.py#L105
+ def _parse_url(self):
+ """
+ Parses a URL which must have one of the following forms:
+
+ - ws://host[:port][path]
+ - wss://host[:port][path]
+ - ws+unix:///path/to/my.socket
+
+ In the first two cases, the ``host`` and ``port``
+ attributes will be set to the parsed values. If no port
+ is explicitely provided, it will be either 80 or 443
+ based on the scheme. Also, the ``resource`` attribute is
+ set to the path segment of the URL (alongside any querystring).
+
+ In addition, if the scheme is ``ws+unix``, the
+ ``unix_socket_path`` attribute is set to the path to
+ the Unix socket while the ``resource`` attribute is
+ set to ``/``.
+ """
+ # Python 2.6.1 and below don't parse ws or wss urls properly. netloc is empty.
+ # See: https://github.com/Lawouach/WebSocket-for-Python/issues/59
+ scheme, url = self.url.split(":", 1)
+
+ parsed = urlsplit(url, scheme="http")
+ if parsed.hostname:
+ self.host = parsed.hostname
+ elif '+unix' in scheme:
+ self.host = 'localhost'
+ else:
+ raise ValueError("Invalid hostname from: %s", self.url)
+
+ if parsed.port:
+ self.port = parsed.port
+
+ if scheme == "ws":
+ if not self.port:
+ self.port = 80
+ elif scheme == "wss":
+ if not self.port:
+ self.port = 443
+ elif scheme in ('ws+unix', 'wss+unix'):
+ pass
+ else:
+ raise ValueError("Invalid scheme: %s" % scheme)
+
+ if parsed.path:
+ resource = parsed.path
+ else:
+ resource = "/"
+
+ if '+unix' in scheme:
+ self.unix_socket_path = resource
+ resource = '/'
+
+ if parsed.query:
+ resource += "?" + parsed.query
+
+ self.scheme = scheme
+ self.resource = resource
+
+ @property
+ def bind_addr(self):
+ """
+ Returns the Unix socket path if or a tuple
+ ``(host, port)`` depending on the initial
+ URL's scheme.
+ """
+ return self.unix_socket_path or (self.host, self.port)
+
+ def close(self, code=1000, reason=''):
+ """
+ Initiate the closing handshake with the server.
+ """
+ if not self.client_terminated:
+ self.client_terminated = True
+ self._write(self.stream.close(code=code, reason=reason).single(mask=True))
+
+ def connect(self):
+ """
+ Connects this websocket and starts the upgrade handshake
+ with the remote endpoint.
+ """
+ if self.scheme == "wss":
+ # default port is now 443; upgrade self.sender to send ssl
+ self.sock = ssl.wrap_socket(self.sock, **self.ssl_options)
+ self._is_secure = True
+
+ self.sock.connect(self.bind_addr)
+
+ self._write(self.handshake_request)
+
+ response = b''
+ doubleCLRF = b'\r\n\r\n'
+ while True:
+ bytes = self.sock.recv(128)
+ if not bytes:
+ break
+ response += bytes
+ if doubleCLRF in response:
+ break
+
+ if not response:
+ self.close_connection()
+ raise HandshakeError("Invalid response")
+
+ headers, _, body = response.partition(doubleCLRF)
+ response_line, _, headers = headers.partition(b'\r\n')
+
+ try:
+ self.process_response_line(response_line)
+ self.protocols, self.extensions = self.process_handshake_header(headers)
+ except HandshakeError:
+ self.close_connection()
+ raise
+
+ self.handshake_ok()
+ if body:
+ self.process(body)
+
+ @property
+ def handshake_headers(self):
+ """
+ List of headers appropriate for the upgrade
+ handshake.
+ """
+ headers = [
+ ('Host', '%s:%s' % (self.host, self.port)),
+ ('Connection', 'Upgrade'),
+ ('Upgrade', 'websocket'),
+ ('Sec-WebSocket-Key', self.key.decode('utf-8')),
+ ('Sec-WebSocket-Version', str(max(WS_VERSION)))
+ ]
+
+ if self.protocols:
+ headers.append(('Sec-WebSocket-Protocol', ','.join(self.protocols)))
+
+ if self.extra_headers:
+ headers.extend(self.extra_headers)
+
+ if not any(x for x in headers if x[0].lower() == 'origin') and \
+ 'origin' not in self.exclude_headers:
+
+ scheme, url = self.url.split(":", 1)
+ parsed = urlsplit(url, scheme="http")
+ if parsed.hostname:
+ self.host = parsed.hostname
+ else:
+ self.host = 'localhost'
+ origin = scheme + '://' + self.host
+ if parsed.port:
+ origin = origin + ':' + str(parsed.port)
+ headers.append(('Origin', origin))
+
+ headers = [x for x in headers if x[0].lower() not in self.exclude_headers]
+
+ return headers
+
+ @property
+ def handshake_request(self):
+ """
+ Prepare the request to be sent for the upgrade handshake.
+ """
+ headers = self.handshake_headers
+ request = [("GET %s HTTP/1.1" % self.resource).encode('utf-8')]
+ for header, value in headers:
+ request.append(("%s: %s" % (header, value)).encode('utf-8'))
+ request.append(b'\r\n')
+
+ return b'\r\n'.join(request)
+
+ def process_response_line(self, response_line):
+ """
+ Ensure that we received a HTTP `101` status code in
+ response to our request and if not raises :exc:`HandshakeError`.
+ """
+ protocol, code, status = response_line.split(b' ', 2)
+ if code != b'101':
+ raise HandshakeError("Invalid response status: %s %s" % (code, status))
+
+ def process_handshake_header(self, headers):
+ """
+ Read the upgrade handshake's response headers and
+ validate them against :rfc:`6455`.
+ """
+ protocols = []
+ extensions = []
+
+ headers = headers.strip()
+
+ for header_line in headers.split(b'\r\n'):
+ header, value = header_line.split(b':', 1)
+ header = header.strip().lower()
+ value = value.strip().lower()
+
+ if header == b'upgrade' and value != b'websocket':
+ raise HandshakeError("Invalid Upgrade header: %s" % value)
+
+ elif header == b'connection' and value != b'upgrade':
+ raise HandshakeError("Invalid Connection header: %s" % value)
+
+ elif header == b'sec-websocket-accept':
+ match = b64encode(sha1(self.key + WS_KEY).digest())
+ if value != match.lower():
+ raise HandshakeError("Invalid challenge response: %s" % value)
+
+ elif header == b'sec-websocket-protocol':
+ protocols.extend([x.strip() for x in value.split(b',')])
+
+ elif header == b'sec-websocket-extensions':
+ extensions.extend([x.strip() for x in value.split(b',')])
+
+ return protocols, extensions
+
+ def handshake_ok(self):
+ self.opened()
diff --git a/game/python-extra/ws4py/client/geventclient.py b/game/python-extra/ws4py/client/geventclient.py
new file mode 100644
index 0000000..b64a17e
--- /dev/null
+++ b/game/python-extra/ws4py/client/geventclient.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+import copy
+
+import gevent
+from gevent import Greenlet
+from gevent.queue import Queue
+
+from ws4py.client import WebSocketBaseClient
+
+__all__ = ['WebSocketClient']
+
+class WebSocketClient(WebSocketBaseClient):
+ def __init__(self, url, protocols=None, extensions=None, heartbeat_freq=None, ssl_options=None, headers=None, exclude_headers=None):
+ """
+ WebSocket client that executes the
+ :meth:`run() <ws4py.websocket.WebSocket.run>` into a gevent greenlet.
+
+ .. code-block:: python
+
+ ws = WebSocketClient('ws://localhost:9000/echo', protocols=['http-only', 'chat'])
+ ws.connect()
+
+ ws.send("Hello world")
+
+ def incoming():
+ while True:
+ m = ws.receive()
+ if m is not None:
+ print str(m)
+ else:
+ break
+
+ def outgoing():
+ for i in range(0, 40, 5):
+ ws.send("*" * i)
+
+ greenlets = [
+ gevent.spawn(incoming),
+ gevent.spawn(outgoing),
+ ]
+ gevent.joinall(greenlets)
+ """
+ WebSocketBaseClient.__init__(self, url, protocols, extensions, heartbeat_freq,
+ ssl_options=ssl_options, headers=headers, exclude_headers=exclude_headers)
+ self._th = Greenlet(self.run)
+
+ self.messages = Queue()
+ """
+ Queue that will hold received messages.
+ """
+
+ def handshake_ok(self):
+ """
+ Called when the upgrade handshake has completed
+ successfully.
+
+ Starts the client's thread.
+ """
+ self._th.start()
+
+ def received_message(self, message):
+ """
+ Override the base class to store the incoming message
+ in the `messages` queue.
+ """
+ self.messages.put(copy.deepcopy(message))
+
+ def closed(self, code, reason=None):
+ """
+ Puts a :exc:`StopIteration` as a message into the
+ `messages` queue.
+ """
+ # When the connection is closed, put a StopIteration
+ # on the message queue to signal there's nothing left
+ # to wait for
+ self.messages.put(StopIteration)
+
+ def receive(self, block=True):
+ """
+ Returns messages that were stored into the
+ `messages` queue and returns `None` when the
+ websocket is terminated or closed.
+ `block` is passed though the gevent queue `.get()` method, which if
+ True will block until an item in the queue is available. Set this to
+ False if you just want to check the queue, which will raise an
+ Empty exception you need to handle if there is no message to return.
+ """
+ # If the websocket was terminated and there are no messages
+ # left in the queue, return None immediately otherwise the client
+ # will block forever
+ if self.terminated and self.messages.empty():
+ return None
+ message = self.messages.get(block=block)
+ if message is StopIteration:
+ return None
+ return message
diff --git a/game/python-extra/ws4py/client/threadedclient.py b/game/python-extra/ws4py/client/threadedclient.py
new file mode 100644
index 0000000..b033f32
--- /dev/null
+++ b/game/python-extra/ws4py/client/threadedclient.py
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+import threading
+
+from ws4py.client import WebSocketBaseClient
+
+__all__ = ['WebSocketClient']
+
+class WebSocketClient(WebSocketBaseClient):
+ def __init__(self, url, protocols=None, extensions=None, heartbeat_freq=None,
+ ssl_options=None, headers=None, exclude_headers=None):
+ """
+ .. code-block:: python
+
+ from ws4py.client.threadedclient import WebSocketClient
+
+ class EchoClient(WebSocketClient):
+ def opened(self):
+ for i in range(0, 200, 25):
+ self.send("*" * i)
+
+ def closed(self, code, reason):
+ print(("Closed down", code, reason))
+
+ def received_message(self, m):
+ print("=> %d %s" % (len(m), str(m)))
+
+ try:
+ ws = EchoClient('ws://localhost:9000/echo', protocols=['http-only', 'chat'])
+ ws.connect()
+ except KeyboardInterrupt:
+ ws.close()
+
+ """
+ WebSocketBaseClient.__init__(self, url, protocols, extensions, heartbeat_freq,
+ ssl_options, headers=headers, exclude_headers=exclude_headers)
+ self._th = threading.Thread(target=self.run, name='WebSocketClient')
+ self._th.daemon = True
+
+ @property
+ def daemon(self):
+ """
+ `True` if the client's thread is set to be a daemon thread.
+ """
+ return self._th.daemon
+
+ @daemon.setter
+ def daemon(self, flag):
+ """
+ Set to `True` if the client's thread should be a daemon.
+ """
+ self._th.daemon = flag
+
+ def run_forever(self):
+ """
+ Simply blocks the thread until the
+ websocket has terminated.
+ """
+ while not self.terminated:
+ self._th.join(timeout=0.1)
+
+ def handshake_ok(self):
+ """
+ Called when the upgrade handshake has completed
+ successfully.
+
+ Starts the client's thread.
+ """
+ self._th.start()
+
+if __name__ == '__main__':
+ from ws4py.client.threadedclient import WebSocketClient
+
+ class EchoClient(WebSocketClient):
+ def opened(self):
+ def data_provider():
+ for i in range(0, 200, 25):
+ yield "#" * i
+
+ self.send(data_provider())
+
+ for i in range(0, 200, 25):
+ self.send("*" * i)
+
+ def closed(self, code, reason):
+ print(("Closed down", code, reason))
+
+ def received_message(self, m):
+ print("#%d" % len(m))
+ if len(m) == 175:
+ self.close(reason='bye bye')
+
+ try:
+ ws = EchoClient('ws://localhost:9000/ws', protocols=['http-only', 'chat'],
+ headers=[('X-Test', 'hello there')])
+ ws.connect()
+ ws.run_forever()
+ except KeyboardInterrupt:
+ ws.close()
diff --git a/game/python-extra/ws4py/client/tornadoclient.py b/game/python-extra/ws4py/client/tornadoclient.py
new file mode 100644
index 0000000..22478e6
--- /dev/null
+++ b/game/python-extra/ws4py/client/tornadoclient.py
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+import ssl
+
+from tornado import iostream, escape
+from ws4py.client import WebSocketBaseClient
+from ws4py.exc import HandshakeError
+
+__all__ = ['TornadoWebSocketClient']
+
+class TornadoWebSocketClient(WebSocketBaseClient):
+ def __init__(self, url, protocols=None, extensions=None,
+ io_loop=None, ssl_options=None, headers=None, exclude_headers=None):
+ """
+ .. code-block:: python
+
+ from tornado import ioloop
+
+ class MyClient(TornadoWebSocketClient):
+ def opened(self):
+ for i in range(0, 200, 25):
+ self.send("*" * i)
+
+ def received_message(self, m):
+ print((m, len(str(m))))
+
+ def closed(self, code, reason=None):
+ ioloop.IOLoop.instance().stop()
+
+ ws = MyClient('ws://localhost:9000/echo', protocols=['http-only', 'chat'])
+ ws.connect()
+
+ ioloop.IOLoop.instance().start()
+ """
+ WebSocketBaseClient.__init__(self, url, protocols, extensions,
+ ssl_options=ssl_options, headers=headers, exclude_headers=exclude_headers)
+ if self.scheme == "wss":
+ self.sock = ssl.wrap_socket(self.sock, do_handshake_on_connect=False, **self.ssl_options)
+ self._is_secure = True
+ self.io = iostream.SSLIOStream(self.sock, io_loop, ssl_options=self.ssl_options)
+ else:
+ self.io = iostream.IOStream(self.sock, io_loop)
+ self.io_loop = io_loop
+
+ def connect(self):
+ """
+ Connects the websocket and initiate the upgrade handshake.
+ """
+ self.io.set_close_callback(self.__connection_refused)
+ self.io.connect((self.host, int(self.port)), self.__send_handshake)
+
+ def _write(self, b):
+ """
+ Trying to prevent a write operation
+ on an already closed websocket stream.
+
+ This cannot be bullet proof but hopefully
+ will catch almost all use cases.
+ """
+ if self.terminated:
+ raise RuntimeError("Cannot send on a terminated websocket")
+
+ self.io.write(b)
+
+ def __connection_refused(self, *args, **kwargs):
+ self.server_terminated = True
+ self.closed(1005, 'Connection refused')
+
+ def __send_handshake(self):
+ self.io.set_close_callback(self.__connection_closed)
+ self.io.write(escape.utf8(self.handshake_request),
+ self.__handshake_sent)
+
+ def __connection_closed(self, *args, **kwargs):
+ self.server_terminated = True
+ self.closed(1006, 'Connection closed during handshake')
+
+ def __handshake_sent(self):
+ self.io.read_until(b"\r\n\r\n", self.__handshake_completed)
+
+ def __handshake_completed(self, data):
+ self.io.set_close_callback(None)
+ try:
+ response_line, _, headers = data.partition(b'\r\n')
+ self.process_response_line(response_line)
+ protocols, extensions = self.process_handshake_header(headers)
+ except HandshakeError:
+ self.close_connection()
+ raise
+
+ self.opened()
+ self.io.set_close_callback(self.__stream_closed)
+ self.io.read_bytes(self.reading_buffer_size, self.__fetch_more)
+
+ def __fetch_more(self, bytes):
+ try:
+ should_continue = self.process(bytes)
+ except:
+ should_continue = False
+
+ if should_continue:
+ self.io.read_bytes(self.reading_buffer_size, self.__fetch_more)
+ else:
+ self.__gracefully_terminate()
+
+ def __gracefully_terminate(self):
+ self.client_terminated = self.server_terminated = True
+
+ try:
+ if not self.stream.closing:
+ self.closed(1006)
+ finally:
+ self.close_connection()
+
+ def __stream_closed(self, *args, **kwargs):
+ self.io.set_close_callback(None)
+ code = 1006
+ reason = None
+ if self.stream.closing:
+ code, reason = self.stream.closing.code, self.stream.closing.reason
+ self.closed(code, reason)
+ self.stream._cleanup()
+
+ def close_connection(self):
+ """
+ Close the underlying connection
+ """
+ self.io.close()
+
+if __name__ == '__main__':
+ from tornado import ioloop
+
+ class MyClient(TornadoWebSocketClient):
+ def opened(self):
+ def data_provider():
+ for i in range(0, 200, 25):
+ yield "#" * i
+
+ self.send(data_provider())
+
+ for i in range(0, 200, 25):
+ self.send("*" * i)
+
+ def received_message(self, m):
+ print("#%d" % len(m))
+ if len(m) == 175:
+ self.close()
+
+ def closed(self, code, reason=None):
+ ioloop.IOLoop.instance().stop()
+ print(("Closed down", code, reason))
+
+ ws = MyClient('ws://localhost:9000/ws', protocols=['http-only', 'chat'])
+ ws.connect()
+
+ ioloop.IOLoop.instance().start()
diff --git a/game/python-extra/ws4py/compat.py b/game/python-extra/ws4py/compat.py
new file mode 100644
index 0000000..e986e33
--- /dev/null
+++ b/game/python-extra/ws4py/compat.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+__doc__ = """
+This compatibility module is inspired by the one found
+in CherryPy. It provides a common entry point for the various
+functions and types that are used with ws4py but which
+differ from Python 2.x to Python 3.x
+
+There are likely better ways for some of them so feel
+free to provide patches.
+
+Note this has been tested against 2.7 and 3.3 only but
+should hopefully work fine with other versions too.
+"""
+import sys
+
+if sys.version_info >= (3, 0):
+ py3k = True
+ from urllib.parse import urlsplit
+ range = range
+ unicode = str
+ basestring = (bytes, str)
+ _ord = ord
+
+ def get_connection(fileobj):
+ return fileobj.raw._sock
+
+ def detach_connection(fileobj):
+ fileobj.detach()
+
+ def ord(c):
+ if isinstance(c, int):
+ return c
+ return _ord(c)
+else:
+ py3k = False
+ from urlparse import urlsplit
+ range = xrange
+ unicode = unicode
+ basestring = basestring
+ ord = ord
+
+ def get_connection(fileobj):
+ return fileobj._sock
+
+ def detach_connection(fileobj):
+ fileobj._sock = None
diff --git a/game/python-extra/ws4py/exc.py b/game/python-extra/ws4py/exc.py
new file mode 100644
index 0000000..bfefea4
--- /dev/null
+++ b/game/python-extra/ws4py/exc.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+
+__all__ = ['WebSocketException', 'FrameTooLargeException', 'ProtocolException',
+ 'UnsupportedFrameTypeException', 'TextFrameEncodingException',
+ 'UnsupportedFrameTypeException', 'TextFrameEncodingException',
+ 'StreamClosed', 'HandshakeError', 'InvalidBytesError']
+
+class WebSocketException(Exception): pass
+
+class ProtocolException(WebSocketException): pass
+
+class FrameTooLargeException(WebSocketException): pass
+
+class UnsupportedFrameTypeException(WebSocketException): pass
+
+class TextFrameEncodingException(WebSocketException): pass
+
+class InvalidBytesError(WebSocketException): pass
+
+class StreamClosed(Exception): pass
+
+class HandshakeError(WebSocketException):
+ def __init__(self, msg):
+ self.msg = msg
+
+ def __str__(self):
+ return self.msg
diff --git a/game/python-extra/ws4py/framing.py b/game/python-extra/ws4py/framing.py
new file mode 100644
index 0000000..5046167
--- /dev/null
+++ b/game/python-extra/ws4py/framing.py
@@ -0,0 +1,273 @@
+# -*- coding: utf-8 -*-
+from struct import pack, unpack
+
+from ws4py.exc import FrameTooLargeException, ProtocolException
+from ws4py.compat import py3k, ord, range
+
+# Frame opcodes defined in the spec.
+OPCODE_CONTINUATION = 0x0
+OPCODE_TEXT = 0x1
+OPCODE_BINARY = 0x2
+OPCODE_CLOSE = 0x8
+OPCODE_PING = 0x9
+OPCODE_PONG = 0xa
+
+__all__ = ['Frame']
+
+class Frame(object):
+ def __init__(self, opcode=None, body=b'', masking_key=None, fin=0, rsv1=0, rsv2=0, rsv3=0):
+ """
+ Implements the framing protocol as defined by RFC 6455.
+
+ .. code-block:: python
+ :linenos:
+
+ >>> test_mask = 'XXXXXX' # perhaps from os.urandom(4)
+ >>> f = Frame(OPCODE_TEXT, 'hello world', masking_key=test_mask, fin=1)
+ >>> bytes = f.build()
+ >>> bytes.encode('hex')
+ '818bbe04e66ad6618a06d1249105cc6882'
+ >>> f = Frame()
+ >>> f.parser.send(bytes[0])
+ 1
+ >>> f.parser.send(bytes[1])
+ 4
+
+ .. seealso:: Data Framing http://tools.ietf.org/html/rfc6455#section-5.2
+ """
+ if not isinstance(body, bytes):
+ raise TypeError("The body must be properly encoded")
+
+ self.opcode = opcode
+ self.body = body
+ self.masking_key = masking_key
+ self.fin = fin
+ self.rsv1 = rsv1
+ self.rsv2 = rsv2
+ self.rsv3 = rsv3
+ self.payload_length = len(body)
+
+ self._parser = None
+
+ @property
+ def parser(self):
+ if self._parser is None:
+ self._parser = self._parsing()
+ # Python generators must be initialized once.
+ next(self.parser)
+ return self._parser
+
+ def _cleanup(self):
+ if self._parser:
+ self._parser.close()
+ self._parser = None
+
+ def build(self):
+ """
+ Builds a frame from the instance's attributes and returns
+ its bytes representation.
+ """
+ header = b''
+
+ if self.fin > 0x1:
+ raise ValueError('FIN bit parameter must be 0 or 1')
+
+ if 0x3 <= self.opcode <= 0x7 or 0xB <= self.opcode:
+ raise ValueError('Opcode cannot be a reserved opcode')
+
+ ## +-+-+-+-+-------+
+ ## |F|R|R|R| opcode|
+ ## |I|S|S|S| (4) |
+ ## |N|V|V|V| |
+ ## | |1|2|3| |
+ ## +-+-+-+-+-------+
+ header = pack('!B', ((self.fin << 7)
+ | (self.rsv1 << 6)
+ | (self.rsv2 << 5)
+ | (self.rsv3 << 4)
+ | self.opcode))
+
+ ## +-+-------------+-------------------------------+
+ ## |M| Payload len | Extended payload length |
+ ## |A| (7) | (16/63) |
+ ## |S| | (if payload len==126/127) |
+ ## |K| | |
+ ## +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
+ ## | Extended payload length continued, if payload len == 127 |
+ ## + - - - - - - - - - - - - - - - +-------------------------------+
+ if self.masking_key: mask_bit = 1 << 7
+ else: mask_bit = 0
+
+ length = self.payload_length
+ if length < 126:
+ header += pack('!B', (mask_bit | length))
+ elif length < (1 << 16):
+ header += pack('!B', (mask_bit | 126)) + pack('!H', length)
+ elif length < (1 << 63):
+ header += pack('!B', (mask_bit | 127)) + pack('!Q', length)
+ else:
+ raise FrameTooLargeException()
+
+ ## + - - - - - - - - - - - - - - - +-------------------------------+
+ ## | |Masking-key, if MASK set to 1 |
+ ## +-------------------------------+-------------------------------+
+ ## | Masking-key (continued) | Payload Data |
+ ## +-------------------------------- - - - - - - - - - - - - - - - +
+ ## : Payload Data continued ... :
+ ## + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ ## | Payload Data continued ... |
+ ## +---------------------------------------------------------------+
+ body = self.body
+ if not self.masking_key:
+ return bytes(header + body)
+
+ return bytes(header + self.masking_key + self.mask(body))
+
+ def _parsing(self):
+ """
+ Generator to parse bytes into a frame. Yields until
+ enough bytes have been read or an error is met.
+ """
+ buf = b''
+ some_bytes = b''
+
+ # yield until we get the first header's byte
+ while not some_bytes:
+ some_bytes = (yield 1)
+
+ first_byte = some_bytes[0] if isinstance(some_bytes, bytearray) else ord(some_bytes[0])
+ # frame-fin = %x0 ; more frames of this message follow
+ # / %x1 ; final frame of this message
+ self.fin = (first_byte >> 7) & 1
+ self.rsv1 = (first_byte >> 6) & 1
+ self.rsv2 = (first_byte >> 5) & 1
+ self.rsv3 = (first_byte >> 4) & 1
+ self.opcode = first_byte & 0xf
+
+ # frame-rsv1 = %x0 ; 1 bit, MUST be 0 unless negotiated otherwise
+ # frame-rsv2 = %x0 ; 1 bit, MUST be 0 unless negotiated otherwise
+ # frame-rsv3 = %x0 ; 1 bit, MUST be 0 unless negotiated otherwise
+ if self.rsv1 or self.rsv2 or self.rsv3:
+ raise ProtocolException()
+
+ # control frames between 3 and 7 as well as above 0xA are currently reserved
+ if 2 < self.opcode < 8 or self.opcode > 0xA:
+ raise ProtocolException()
+
+ # control frames cannot be fragmented
+ if self.opcode > 0x7 and self.fin == 0:
+ raise ProtocolException()
+
+ # do we already have enough some_bytes to continue?
+ some_bytes = some_bytes[1:] if some_bytes and len(some_bytes) > 1 else b''
+
+ # Yield until we get the second header's byte
+ while not some_bytes:
+ some_bytes = (yield 1)
+
+ second_byte = some_bytes[0] if isinstance(some_bytes, bytearray) else ord(some_bytes[0])
+ mask = (second_byte >> 7) & 1
+ self.payload_length = second_byte & 0x7f
+
+ # All control frames MUST have a payload length of 125 some_bytes or less
+ if self.opcode > 0x7 and self.payload_length > 125:
+ raise FrameTooLargeException()
+
+ if some_bytes and len(some_bytes) > 1:
+ buf = some_bytes[1:]
+ some_bytes = buf
+ else:
+ buf = b''
+ some_bytes = b''
+
+ if self.payload_length == 127:
+ # This will compute the actual application data size
+ if len(buf) < 8:
+ nxt_buf_size = 8 - len(buf)
+ some_bytes = (yield nxt_buf_size)
+ some_bytes = buf + (some_bytes or b'')
+ while len(some_bytes) < 8:
+ b = (yield 8 - len(some_bytes))
+ if b is not None:
+ some_bytes = some_bytes + b
+ if len(some_bytes) > 8:
+ buf = some_bytes[8:]
+ some_bytes = some_bytes[:8]
+ else:
+ some_bytes = buf[:8]
+ buf = buf[8:]
+ extended_payload_length = some_bytes
+ self.payload_length = unpack(
+ '!Q', extended_payload_length)[0]
+ if self.payload_length > 0x7FFFFFFFFFFFFFFF:
+ raise FrameTooLargeException()
+ elif self.payload_length == 126:
+ if len(buf) < 2:
+ nxt_buf_size = 2 - len(buf)
+ some_bytes = (yield nxt_buf_size)
+ some_bytes = buf + (some_bytes or b'')
+ while len(some_bytes) < 2:
+ b = (yield 2 - len(some_bytes))
+ if b is not None:
+ some_bytes = some_bytes + b
+ if len(some_bytes) > 2:
+ buf = some_bytes[2:]
+ some_bytes = some_bytes[:2]
+ else:
+ some_bytes = buf[:2]
+ buf = buf[2:]
+ extended_payload_length = some_bytes
+ self.payload_length = unpack(
+ '!H', extended_payload_length)[0]
+
+ if mask:
+ if len(buf) < 4:
+ nxt_buf_size = 4 - len(buf)
+ some_bytes = (yield nxt_buf_size)
+ some_bytes = buf + (some_bytes or b'')
+ while not some_bytes or len(some_bytes) < 4:
+ b = (yield 4 - len(some_bytes))
+ if b is not None:
+ some_bytes = some_bytes + b
+ if len(some_bytes) > 4:
+ buf = some_bytes[4:]
+ else:
+ some_bytes = buf[:4]
+ buf = buf[4:]
+ self.masking_key = some_bytes
+
+ if len(buf) < self.payload_length:
+ nxt_buf_size = self.payload_length - len(buf)
+ some_bytes = (yield nxt_buf_size)
+ some_bytes = buf + (some_bytes or b'')
+ while len(some_bytes) < self.payload_length:
+ l = self.payload_length - len(some_bytes)
+ b = (yield l)
+ if b is not None:
+ some_bytes = some_bytes + b
+ else:
+ if self.payload_length == len(buf):
+ some_bytes = buf
+ else:
+ some_bytes = buf[:self.payload_length]
+
+ self.body = some_bytes
+ yield
+
+ def mask(self, data):
+ """
+ Performs the masking or unmasking operation on data
+ using the simple masking algorithm:
+
+ ..
+ j = i MOD 4
+ transformed-octet-i = original-octet-i XOR masking-key-octet-j
+
+ """
+ masked = bytearray(data)
+ if py3k: key = self.masking_key
+ else: key = map(ord, self.masking_key)
+ for i in range(len(data)):
+ masked[i] = masked[i] ^ key[i%4]
+ return masked
+ unmask = mask
diff --git a/game/python-extra/ws4py/manager.py b/game/python-extra/ws4py/manager.py
new file mode 100644
index 0000000..20c215f
--- /dev/null
+++ b/game/python-extra/ws4py/manager.py
@@ -0,0 +1,368 @@
+# -*- coding: utf-8 -*-
+__doc__ = """
+The manager module provides a selected classes to
+handle websocket's execution.
+
+Initially the rationale was to:
+
+- Externalize the way the CherryPy server had been setup
+ as its websocket management was too tightly coupled with
+ the plugin implementation.
+- Offer a management that could be used by other
+ server or client implementations.
+- Move away from the threaded model to the event-based
+ model by relying on `select` or `epoll` (when available).
+
+
+A simple usage for handling websocket clients:
+
+.. code-block:: python
+
+ from ws4py.client import WebSocketBaseClient
+ from ws4py.manager import WebSocketManager
+
+ m = WebSocketManager()
+
+ class EchoClient(WebSocketBaseClient):
+ def handshake_ok(self):
+ m.add(self) # register the client once the handshake is done
+
+ def received_message(self, msg):
+ print str(msg)
+
+ m.start()
+
+ client = EchoClient('ws://localhost:9000/ws')
+ client.connect()
+
+ m.join() # blocks forever
+
+Managers are not compulsory but hopefully will help your
+workflow. For clients, you can still rely on threaded, gevent or
+tornado based implementations of course.
+"""
+import logging
+import select
+import threading
+import time
+
+from ws4py import format_addresses
+from ws4py.compat import py3k
+
+logger = logging.getLogger('ws4py')
+
+class SelectPoller(object):
+ def __init__(self, timeout=0.1):
+ """
+ A socket poller that uses the `select`
+ implementation to determines which
+ file descriptors have data available to read.
+
+ It is available on all platforms.
+ """
+ self._fds = []
+ self.timeout = timeout
+
+ def release(self):
+ """
+ Cleanup resources.
+ """
+ self._fds = []
+
+ def register(self, fd):
+ """
+ Register a new file descriptor to be
+ part of the select polling next time around.
+ """
+ if fd not in self._fds:
+ self._fds.append(fd)
+
+ def unregister(self, fd):
+ """
+ Unregister the given file descriptor.
+ """
+ if fd in self._fds:
+ self._fds.remove(fd)
+
+ def poll(self):
+ """
+ Polls once and returns a list of
+ ready-to-be-read file descriptors.
+ """
+ if not self._fds:
+ time.sleep(self.timeout)
+ return []
+ try:
+ r, w, x = select.select(self._fds, [], [], self.timeout)
+ except IOError as e:
+ return []
+ return r
+
+class EPollPoller(object):
+ def __init__(self, timeout=0.1):
+ """
+ An epoll poller that uses the ``epoll``
+ implementation to determines which
+ file descriptors have data available to read.
+
+ Available on Unix flavors mostly.
+ """
+ self.poller = select.epoll()
+ self.timeout = timeout
+
+ def release(self):
+ """
+ Cleanup resources.
+ """
+ self.poller.close()
+
+ def register(self, fd):
+ """
+ Register a new file descriptor to be
+ part of the select polling next time around.
+ """
+ try:
+ self.poller.register(fd, select.EPOLLIN | select.EPOLLPRI)
+ except IOError:
+ pass
+
+ def unregister(self, fd):
+ """
+ Unregister the given file descriptor.
+ """
+ self.poller.unregister(fd)
+
+ def poll(self):
+ """
+ Polls once and yields each ready-to-be-read
+ file-descriptor
+ """
+ try:
+ events = self.poller.poll(timeout=self.timeout)
+ except IOError:
+ events = []
+
+ for fd, event in events:
+ if event | select.EPOLLIN | select.EPOLLPRI:
+ yield fd
+
+class KQueuePoller(object):
+ def __init__(self, timeout=0.1):
+ """
+ An epoll poller that uses the ``epoll``
+ implementation to determines which
+ file descriptors have data available to read.
+
+ Available on Unix flavors mostly.
+ """
+ self.poller = select.epoll()
+ self.timeout = timeout
+
+ def release(self):
+ """
+ Cleanup resources.
+ """
+ self.poller.close()
+
+ def register(self, fd):
+ """
+ Register a new file descriptor to be
+ part of the select polling next time around.
+ """
+ try:
+ self.poller.register(fd, select.EPOLLIN | select.EPOLLPRI)
+ except IOError:
+ pass
+
+ def unregister(self, fd):
+ """
+ Unregister the given file descriptor.
+ """
+ self.poller.unregister(fd)
+
+ def poll(self):
+ """
+ Polls once and yields each ready-to-be-read
+ file-descriptor
+ """
+ try:
+ events = self.poller.poll(timeout=self.timeout)
+ except IOError:
+ events = []
+ for fd, event in events:
+ if event | select.EPOLLIN | select.EPOLLPRI:
+ yield fd
+
+class WebSocketManager(threading.Thread):
+ def __init__(self, poller=None):
+ """
+ An event-based websocket manager. By event-based, we mean
+ that the websockets will be called when their
+ sockets have data to be read from.
+
+ The manager itself runs in its own thread as not to
+ be the blocking mainloop of your application.
+
+ The poller's implementation is automatically chosen
+ with ``epoll`` if available else ``select`` unless you
+ provide your own ``poller``.
+ """
+ threading.Thread.__init__(self)
+ self.name = "WebSocketManager"
+ self.lock = threading.Lock()
+ self.websockets = {}
+ self.running = False
+
+ if poller:
+ self.poller = poller
+ else:
+ if hasattr(select, "epoll"):
+ self.poller = EPollPoller()
+ logger.info("Using epoll")
+ else:
+ self.poller = SelectPoller()
+ logger.info("Using select as epoll is not available")
+
+ def __len__(self):
+ return len(self.websockets)
+
+ def __iter__(self):
+ if py3k:
+ return iter(self.websockets.values())
+ else:
+ return self.websockets.itervalues()
+
+ def __contains__(self, ws):
+ fd = ws.sock.fileno()
+ # just in case the file descriptor was reused
+ # we actually check the instance (well, this might
+ # also have been reused...)
+ return self.websockets.get(fd) is ws
+
+ def add(self, websocket):
+ """
+ Manage a new websocket.
+
+ First calls its :meth:`opened() <ws4py.websocket.WebSocket.opened>`
+ method and register its socket against the poller
+ for reading events.
+ """
+ if websocket in self:
+ return
+
+ logger.info("Managing websocket %s" % format_addresses(websocket))
+ websocket.opened()
+ with self.lock:
+ fd = websocket.sock.fileno()
+ self.websockets[fd] = websocket
+ self.poller.register(fd)
+
+ def remove(self, websocket):
+ """
+ Remove the given ``websocket`` from the manager.
+
+ This does not call its :meth:`closed() <ws4py.websocket.WebSocket.closed>`
+ method as it's out-of-band by your application
+ or from within the manager's run loop.
+ """
+ if websocket not in self:
+ return
+
+ logger.info("Removing websocket %s" % format_addresses(websocket))
+ with self.lock:
+ fd = websocket.sock.fileno()
+ self.websockets.pop(fd, None)
+ self.poller.unregister(fd)
+
+ def stop(self):
+ """
+ Mark the manager as terminated and
+ releases its resources.
+ """
+ self.running = False
+ with self.lock:
+ self.websockets.clear()
+ self.poller.release()
+
+ def run(self):
+ """
+ Manager's mainloop executed from within a thread.
+
+ Constantly poll for read events and, when available,
+ call related websockets' `once` method to
+ read and process the incoming data.
+
+ If the :meth:`once() <ws4py.websocket.WebSocket.once>`
+ method returns a `False` value, its :meth:`terminate() <ws4py.websocket.WebSocket.terminate>`
+ method is also applied to properly close
+ the websocket and its socket is unregistered from the poller.
+
+ Note that websocket shouldn't take long to process
+ their data or they will block the remaining
+ websockets with data to be handled. As for what long means,
+ it's up to your requirements.
+ """
+ self.running = True
+ while self.running:
+ with self.lock:
+ polled = self.poller.poll()
+ if not self.running:
+ break
+
+ for fd in polled:
+ if not self.running:
+ break
+
+ ws = self.websockets.get(fd)
+ if ws and not ws.terminated:
+ # I don't know what kind of errors might spew out of here
+ # but they probably shouldn't crash the entire server.
+ try:
+ x = ws.once()
+ # Treat the error as if once() had returned None
+ except Exception as e:
+ x = None
+ logger.error("Terminating websocket %s due to exception: %s in once method" % (format_addresses(ws), repr(e)) )
+ if not x:
+ with self.lock:
+ self.websockets.pop(fd, None)
+ self.poller.unregister(fd)
+
+ if not ws.terminated:
+ logger.info("Terminating websocket %s" % format_addresses(ws))
+ ws.terminate()
+
+
+ def close_all(self, code=1001, message='Server is shutting down'):
+ """
+ Execute the :meth:`close() <ws4py.websocket.WebSocket.close>`
+ method of each registered websockets to initiate the closing handshake.
+ It doesn't wait for the handshake to complete properly.
+ """
+ with self.lock:
+ logger.info("Closing all websockets with [%d] '%s'" % (code, message))
+ for ws in iter(self):
+ ws.close(code=code, reason=message)
+
+ def broadcast(self, message, binary=False):
+ """
+ Broadcasts the given message to all registered
+ websockets, at the time of the call.
+
+ Broadcast may fail on a given registered peer
+ but this is silent as it's not the method's
+ purpose to handle websocket's failures.
+ """
+ with self.lock:
+ websockets = self.websockets.copy()
+ if py3k:
+ ws_iter = iter(websockets.values())
+ else:
+ ws_iter = websockets.itervalues()
+
+ for ws in ws_iter:
+ if not ws.terminated:
+ try:
+ ws.send(message, binary)
+ except:
+ pass
diff --git a/game/python-extra/ws4py/messaging.py b/game/python-extra/ws4py/messaging.py
new file mode 100644
index 0000000..f9b0e77
--- /dev/null
+++ b/game/python-extra/ws4py/messaging.py
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+import os
+import struct
+
+from ws4py.framing import Frame, OPCODE_CONTINUATION, OPCODE_TEXT, \
+ OPCODE_BINARY, OPCODE_CLOSE, OPCODE_PING, OPCODE_PONG
+from ws4py.compat import unicode, py3k
+
+__all__ = ['Message', 'TextMessage', 'BinaryMessage', 'CloseControlMessage',
+ 'PingControlMessage', 'PongControlMessage']
+
+class Message(object):
+ def __init__(self, opcode, data=b'', encoding='utf-8'):
+ """
+ A message is a application level entity. It's usually built
+ from one or many frames. The protocol defines several kind
+ of messages which are grouped into two sets:
+
+ * data messages which can be text or binary typed
+ * control messages which provide a mechanism to perform
+ in-band control communication between peers
+
+ The ``opcode`` indicates the message type and ``data`` is
+ the possible message payload.
+
+ The payload is held internally as a a :func:`bytearray` as they are
+ faster than pure strings for append operations.
+
+ Unicode data will be encoded using the provided ``encoding``.
+ """
+ self.opcode = opcode
+ self._completed = False
+ self.encoding = encoding
+
+ if isinstance(data, unicode):
+ if not encoding:
+ raise TypeError("unicode data without an encoding")
+ data = data.encode(encoding)
+ elif isinstance(data, bytearray):
+ data = bytes(data)
+ elif not isinstance(data, bytes):
+ raise TypeError("%s is not a supported data type" % type(data))
+
+ self.data = data
+
+ def single(self, mask=False):
+ """
+ Returns a frame bytes with the fin bit set and a random mask.
+
+ If ``mask`` is set, automatically mask the frame
+ using a generated 4-byte token.
+ """
+ mask = os.urandom(4) if mask else None
+ return Frame(body=self.data, opcode=self.opcode,
+ masking_key=mask, fin=1).build()
+
+ def fragment(self, first=False, last=False, mask=False):
+ """
+ Returns a :class:`ws4py.framing.Frame` bytes.
+
+ The behavior depends on the given flags:
+
+ * ``first``: the frame uses ``self.opcode`` else a continuation opcode
+ * ``last``: the frame has its ``fin`` bit set
+ * ``mask``: the frame is masked using a automatically generated 4-byte token
+ """
+ fin = 1 if last is True else 0
+ opcode = self.opcode if first is True else OPCODE_CONTINUATION
+ mask = os.urandom(4) if mask else None
+ return Frame(body=self.data,
+ opcode=opcode, masking_key=mask,
+ fin=fin).build()
+
+ @property
+ def completed(self):
+ """
+ Indicates the the message is complete, meaning
+ the frame's ``fin`` bit was set.
+ """
+ return self._completed
+
+ @completed.setter
+ def completed(self, state):
+ """
+ Sets the state for this message. Usually
+ set by the stream's parser.
+ """
+ self._completed = state
+
+ def extend(self, data):
+ """
+ Add more ``data`` to the message.
+ """
+ if isinstance(data, bytes):
+ self.data += data
+ elif isinstance(data, bytearray):
+ self.data += bytes(data)
+ elif isinstance(data, unicode):
+ self.data += data.encode(self.encoding)
+ else:
+ raise TypeError("%s is not a supported data type" % type(data))
+
+ def __len__(self):
+ return len(self.__unicode__())
+
+ def __str__(self):
+ if py3k:
+ return self.data.decode(self.encoding)
+ return self.data
+
+ def __unicode__(self):
+ return self.data.decode(self.encoding)
+
+class TextMessage(Message):
+ def __init__(self, text=None):
+ Message.__init__(self, OPCODE_TEXT, text)
+
+ @property
+ def is_binary(self):
+ return False
+
+ @property
+ def is_text(self):
+ return True
+
+class BinaryMessage(Message):
+ def __init__(self, bytes=None):
+ Message.__init__(self, OPCODE_BINARY, bytes, encoding=None)
+
+ @property
+ def is_binary(self):
+ return True
+
+ @property
+ def is_text(self):
+ return False
+
+ def __len__(self):
+ return len(self.data)
+
+class CloseControlMessage(Message):
+ def __init__(self, code=1000, reason=''):
+ data = b""
+ if code:
+ data += struct.pack("!H", code)
+ if reason is not None:
+ if isinstance(reason, unicode):
+ reason = reason.encode('utf-8')
+ data += reason
+
+ Message.__init__(self, OPCODE_CLOSE, data, 'utf-8')
+ self.code = code
+ self.reason = reason
+
+ def __str__(self):
+ if py3k:
+ return self.reason.decode('utf-8')
+ return self.reason
+
+ def __unicode__(self):
+ return self.reason.decode(self.encoding)
+
+class PingControlMessage(Message):
+ def __init__(self, data=None):
+ Message.__init__(self, OPCODE_PING, data)
+
+class PongControlMessage(Message):
+ def __init__(self, data):
+ Message.__init__(self, OPCODE_PONG, data)
diff --git a/game/python-extra/websock/tests/__init__.py b/game/python-extra/ws4py/server/__init__.py
index e69de29..e69de29 100644
--- a/game/python-extra/websock/tests/__init__.py
+++ b/game/python-extra/ws4py/server/__init__.py
diff --git a/game/python-extra/ws4py/server/cherrypyserver.py b/game/python-extra/ws4py/server/cherrypyserver.py
new file mode 100644
index 0000000..5b93465
--- /dev/null
+++ b/game/python-extra/ws4py/server/cherrypyserver.py
@@ -0,0 +1,382 @@
+# -*- coding: utf-8 -*-
+__doc__ = """
+WebSocket within CherryPy is a tricky bit since CherryPy is
+a threaded server which would choke quickly if each thread
+of the server were kept attached to a long living connection
+that WebSocket expects.
+
+In order to work around this constraint, we take some advantage
+of some internals of CherryPy as well as the introspection
+Python provides.
+
+Basically, when the WebSocket handshake is complete, we take over
+the socket and let CherryPy take back the thread that was
+associated with the upgrade request.
+
+These operations require a bit of work at various levels of
+the CherryPy framework but this module takes care of them
+and from your application's perspective, this is abstracted.
+
+Here are the various utilities provided by this module:
+
+ * WebSocketTool: The tool is in charge to perform the
+ HTTP upgrade and detach the socket from
+ CherryPy. It runs at various hook points of the
+ request's processing. Enable that tool at
+ any path you wish to handle as a WebSocket
+ handler.
+
+ * WebSocketPlugin: The plugin tracks the instanciated web socket handlers.
+ It also cleans out websocket handler which connection
+ have been closed down. The websocket connection then
+ runs in its own thread that this plugin manages.
+
+Simple usage example:
+
+.. code-block:: python
+ :linenos:
+
+ import cherrypy
+ from ws4py.server.cherrypyserver import WebSocketPlugin, WebSocketTool
+ from ws4py.websocket import EchoWebSocket
+
+ cherrypy.config.update({'server.socket_port': 9000})
+ WebSocketPlugin(cherrypy.engine).subscribe()
+ cherrypy.tools.websocket = WebSocketTool()
+
+ class Root(object):
+ @cherrypy.expose
+ def index(self):
+ return 'some HTML with a websocket javascript connection'
+
+ @cherrypy.expose
+ def ws(self):
+ pass
+
+ cherrypy.quickstart(Root(), '/', config={'/ws': {'tools.websocket.on': True,
+ 'tools.websocket.handler_cls': EchoWebSocket}})
+
+
+Note that you can set the handler class on per-path basis,
+meaning you could also dynamically change the class based
+on other envrionmental settings (is the user authenticated for ex).
+"""
+import base64
+from hashlib import sha1
+import inspect
+import threading
+
+import cherrypy
+from cherrypy import Tool
+from cherrypy.process import plugins
+try:
+ from cheroot.server import HTTPConnection, HTTPRequest, KnownLengthRFile
+except ImportError:
+ from cherrypy.wsgiserver import HTTPConnection, HTTPRequest, KnownLengthRFile
+
+from ws4py import WS_KEY, WS_VERSION
+from ws4py.exc import HandshakeError
+from ws4py.websocket import WebSocket
+from ws4py.compat import py3k, get_connection, detach_connection
+from ws4py.manager import WebSocketManager
+
+__all__ = ['WebSocketTool', 'WebSocketPlugin']
+
+class WebSocketTool(Tool):
+ def __init__(self):
+ Tool.__init__(self, 'before_request_body', self.upgrade)
+
+ def _setup(self):
+ conf = self._merged_args()
+ hooks = cherrypy.serving.request.hooks
+ p = conf.pop("priority", getattr(self.callable, "priority",
+ self._priority))
+ hooks.attach(self._point, self.callable, priority=p, **conf)
+ hooks.attach('before_finalize', self.complete,
+ priority=p)
+ hooks.attach('on_end_resource', self.cleanup_headers,
+ priority=70)
+ hooks.attach('on_end_request', self.start_handler,
+ priority=70)
+
+ def upgrade(self, protocols=None, extensions=None, version=WS_VERSION,
+ handler_cls=WebSocket, heartbeat_freq=None):
+ """
+ Performs the upgrade of the connection to the WebSocket
+ protocol.
+
+ The provided protocols may be a list of WebSocket
+ protocols supported by the instance of the tool.
+
+ When no list is provided and no protocol is either
+ during the upgrade, then the protocol parameter is
+ not taken into account. On the other hand,
+ if the protocol from the handshake isn't part
+ of the provided list, the upgrade fails immediatly.
+ """
+ request = cherrypy.serving.request
+ request.process_request_body = False
+
+ ws_protocols = None
+ ws_location = None
+ ws_version = version
+ ws_key = None
+ ws_extensions = []
+
+ if request.method != 'GET':
+ raise HandshakeError('HTTP method must be a GET')
+
+ for key, expected_value in [('Upgrade', 'websocket'),
+ ('Connection', 'upgrade')]:
+ actual_value = request.headers.get(key, '').lower()
+ if not actual_value:
+ raise HandshakeError('Header %s is not defined' % key)
+ if expected_value not in actual_value:
+ raise HandshakeError('Illegal value for header %s: %s' %
+ (key, actual_value))
+
+ version = request.headers.get('Sec-WebSocket-Version')
+ supported_versions = ', '.join([str(v) for v in ws_version])
+ version_is_valid = False
+ if version:
+ try: version = int(version)
+ except: pass
+ else: version_is_valid = version in ws_version
+
+ if not version_is_valid:
+ cherrypy.response.headers['Sec-WebSocket-Version'] = supported_versions
+ raise HandshakeError('Unhandled or missing WebSocket version')
+
+ key = request.headers.get('Sec-WebSocket-Key')
+ if key:
+ ws_key = base64.b64decode(key.encode('utf-8'))
+ if len(ws_key) != 16:
+ raise HandshakeError("WebSocket key's length is invalid")
+
+ protocols = protocols or []
+ subprotocols = request.headers.get('Sec-WebSocket-Protocol')
+ if subprotocols:
+ ws_protocols = []
+ for s in subprotocols.split(','):
+ s = s.strip()
+ if s in protocols:
+ ws_protocols.append(s)
+
+ exts = extensions or []
+ extensions = request.headers.get('Sec-WebSocket-Extensions')
+ if extensions:
+ for ext in extensions.split(','):
+ ext = ext.strip()
+ if ext in exts:
+ ws_extensions.append(ext)
+
+ location = []
+ include_port = False
+ if request.scheme == "https":
+ location.append("wss://")
+ include_port = request.local.port != 443
+ else:
+ location.append("ws://")
+ include_port = request.local.port != 80
+ location.append('localhost')
+ if include_port:
+ location.append(":%d" % request.local.port)
+ location.append(request.path_info)
+ if request.query_string != "":
+ location.append("?%s" % request.query_string)
+ ws_location = ''.join(location)
+
+ response = cherrypy.serving.response
+ response.stream = True
+ response.status = '101 Switching Protocols'
+ response.headers['Content-Type'] = 'text/plain'
+ response.headers['Upgrade'] = 'websocket'
+ response.headers['Connection'] = 'Upgrade'
+ response.headers['Sec-WebSocket-Version'] = str(version)
+ response.headers['Sec-WebSocket-Accept'] = base64.b64encode(sha1(key.encode('utf-8') + WS_KEY).digest())
+ if ws_protocols:
+ response.headers['Sec-WebSocket-Protocol'] = ', '.join(ws_protocols)
+ if ws_extensions:
+ response.headers['Sec-WebSocket-Extensions'] = ','.join(ws_extensions)
+
+ addr = (request.remote.ip, request.remote.port)
+ rfile = request.rfile.rfile
+ if isinstance(rfile, KnownLengthRFile):
+ rfile = rfile.rfile
+
+ ws_conn = get_connection(rfile)
+ request.ws_handler = handler_cls(ws_conn, ws_protocols, ws_extensions,
+ request.wsgi_environ.copy(),
+ heartbeat_freq=heartbeat_freq)
+
+ def complete(self):
+ """
+ Sets some internal flags of CherryPy so that it
+ doesn't close the socket down.
+ """
+ self._set_internal_flags()
+
+ def cleanup_headers(self):
+ """
+ Some clients aren't that smart when it comes to
+ headers lookup.
+ """
+ response = cherrypy.response
+ if not response.header_list:
+ return
+
+ headers = response.header_list[:]
+ for (k, v) in headers:
+ if k[:7] == 'Sec-Web':
+ response.header_list.remove((k, v))
+ response.header_list.append((k.replace('Sec-Websocket', 'Sec-WebSocket'), v))
+
+ def start_handler(self):
+ """
+ Runs at the end of the request processing by calling
+ the opened method of the handler.
+ """
+ request = cherrypy.request
+ if not hasattr(request, 'ws_handler'):
+ return
+
+ addr = (request.remote.ip, request.remote.port)
+ ws_handler = request.ws_handler
+ request.ws_handler = None
+ delattr(request, 'ws_handler')
+
+ # By doing this we detach the socket from
+ # the CherryPy stack avoiding memory leaks
+ detach_connection(request.rfile.rfile)
+
+ cherrypy.engine.publish('handle-websocket', ws_handler, addr)
+
+ def _set_internal_flags(self):
+ """
+ CherryPy has two internal flags that we are interested in
+ to enable WebSocket within the server. They can't be set via
+ a public API and considering I'd want to make this extension
+ as compatible as possible whilst refraining in exposing more
+ than should be within CherryPy, I prefer performing a bit
+ of introspection to set those flags. Even by Python standards
+ such introspection isn't the cleanest but it works well
+ enough in this case.
+
+ This also means that we do that only on WebSocket
+ connections rather than globally and therefore we do not
+ harm the rest of the HTTP server.
+ """
+ current = inspect.currentframe()
+ while True:
+ if not current:
+ break
+ _locals = current.f_locals
+ if 'self' in _locals:
+ if isinstance(_locals['self'], HTTPRequest):
+ _locals['self'].close_connection = True
+ if isinstance(_locals['self'], HTTPConnection):
+ _locals['self'].linger = True
+ # HTTPConnection is more inner than
+ # HTTPRequest so we can leave once
+ # we're done here
+ return
+ _locals = None
+ current = current.f_back
+
+class WebSocketPlugin(plugins.SimplePlugin):
+ def __init__(self, bus):
+ plugins.SimplePlugin.__init__(self, bus)
+ self.manager = WebSocketManager()
+
+ def start(self):
+ self.bus.log("Starting WebSocket processing")
+ self.bus.subscribe('stop', self.cleanup)
+ self.bus.subscribe('handle-websocket', self.handle)
+ self.bus.subscribe('websocket-broadcast', self.broadcast)
+ self.manager.start()
+
+ def stop(self):
+ self.bus.log("Terminating WebSocket processing")
+ self.bus.unsubscribe('stop', self.cleanup)
+ self.bus.unsubscribe('handle-websocket', self.handle)
+ self.bus.unsubscribe('websocket-broadcast', self.broadcast)
+
+ def handle(self, ws_handler, peer_addr):
+ """
+ Tracks the provided handler.
+
+ :param ws_handler: websocket handler instance
+ :param peer_addr: remote peer address for tracing purpose
+ """
+ self.manager.add(ws_handler)
+
+ def cleanup(self):
+ """
+ Terminate all connections and clear the pool. Executed when the engine stops.
+ """
+ self.manager.close_all()
+ self.manager.stop()
+ self.manager.join()
+
+ def broadcast(self, message, binary=False):
+ """
+ Broadcasts a message to all connected clients known to
+ the server.
+
+ :param message: a message suitable to pass to the send() method
+ of the connected handler.
+ :param binary: whether or not the message is a binary one
+ """
+ self.manager.broadcast(message, binary)
+
+if __name__ == '__main__':
+ import random
+ cherrypy.config.update({'server.socket_host': '127.0.0.1',
+ 'server.socket_port': 9000})
+ WebSocketPlugin(cherrypy.engine).subscribe()
+ cherrypy.tools.websocket = WebSocketTool()
+
+ class Root(object):
+ @cherrypy.expose
+ @cherrypy.tools.websocket(on=False)
+ def ws(self):
+ return """<html>
+ <head>
+ <script type='application/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js'> </script>
+ <script type='application/javascript'>
+ $(document).ready(function() {
+ var ws = new WebSocket('ws://192.168.0.10:9000/');
+ ws.onmessage = function (evt) {
+ $('#chat').val($('#chat').val() + evt.data + '\\n');
+ };
+ ws.onopen = function() {
+ ws.send("Hello there");
+ };
+ ws.onclose = function(evt) {
+ $('#chat').val($('#chat').val() + 'Connection closed by server: ' + evt.code + ' \"' + evt.reason + '\"\\n');
+ };
+ $('#chatform').submit(function() {
+ ws.send('%(username)s: ' + $('#message').val());
+ $('#message').val("");
+ return false;
+ });
+ });
+ </script>
+ </head>
+ <body>
+ <form action='/echo' id='chatform' method='get'>
+ <textarea id='chat' cols='35' rows='10'></textarea>
+ <br />
+ <label for='message'>%(username)s: </label><input type='text' id='message' />
+ <input type='submit' value='Send' />
+ </form>
+ </body>
+ </html>
+ """ % {'username': "User%d" % random.randint(0, 100)}
+
+ @cherrypy.expose
+ def index(self):
+ cherrypy.log("Handler created: %s" % repr(cherrypy.request.ws_handler))
+
+ cherrypy.quickstart(Root(), '/', config={'/': {'tools.websocket.on': True,
+ 'tools.websocket.handler_cls': EchoWebSocketHandler}})
diff --git a/game/python-extra/ws4py/server/geventserver.py b/game/python-extra/ws4py/server/geventserver.py
new file mode 100644
index 0000000..13b5554
--- /dev/null
+++ b/game/python-extra/ws4py/server/geventserver.py
@@ -0,0 +1,121 @@
+# -*- coding: utf-8 -*-
+__doc__ = """
+WSGI entities to support WebSocket from within gevent.
+
+Its usage is rather simple:
+
+.. code-block: python
+
+ from gevent import monkey; monkey.patch_all()
+ from ws4py.websocket import EchoWebSocket
+ from ws4py.server.geventserver import WSGIServer
+ from ws4py.server.wsgiutils import WebSocketWSGIApplication
+
+ server = WSGIServer(('localhost', 9000), WebSocketWSGIApplication(handler_cls=EchoWebSocket))
+ server.serve_forever()
+
+"""
+import logging
+
+import gevent
+from gevent.pywsgi import WSGIHandler, WSGIServer as _WSGIServer
+from gevent.pool import Pool
+
+from ws4py import format_addresses
+from ws4py.server.wsgiutils import WebSocketWSGIApplication
+
+
+logger = logging.getLogger('ws4py')
+
+__all__ = ['WebSocketWSGIHandler', 'WSGIServer',
+ 'GEventWebSocketPool']
+
+
+class WebSocketWSGIHandler(WSGIHandler):
+ """
+ A WSGI handler that will perform the :rfc:`6455`
+ upgrade and handshake before calling the WSGI application.
+
+ If the incoming request doesn't have a `'Upgrade'` header,
+ the handler will simply fallback to the gevent builtin's handler
+ and process it as per usual.
+ """
+
+ def run_application(self):
+ upgrade_header = self.environ.get('HTTP_UPGRADE', '').lower()
+ if upgrade_header:
+ # Build and start the HTTP response
+ self.environ['ws4py.socket'] = self.socket or self.environ['wsgi.input'].rfile._sock
+ self.result = self.application(self.environ, self.start_response) or []
+ self.process_result()
+ del self.environ['ws4py.socket']
+ self.socket = None
+ self.rfile.close()
+
+ ws = self.environ.pop('ws4py.websocket', None)
+ if ws:
+ ws_greenlet = self.server.pool.track(ws)
+ # issue #170
+ # in gevent 1.1 socket will be closed once application returns
+ # so let's wait for websocket handler to finish
+ ws_greenlet.join()
+ else:
+ gevent.pywsgi.WSGIHandler.run_application(self)
+
+
+class GEventWebSocketPool(Pool):
+ """
+ Simple pool of bound websockets.
+ Internally it uses a gevent group to track
+ the websockets. The server should call the ``clear``
+ method to initiate the closing handshake when the
+ server is shutdown.
+ """
+
+ def track(self, websocket):
+ logger.info("Managing websocket %s" % format_addresses(websocket))
+ return self.spawn(websocket.run)
+
+ def clear(self):
+ logger.info("Terminating server and all connected websockets")
+ for greenlet in list(self):
+ try:
+ websocket = greenlet._run.im_self
+ if websocket:
+ websocket.close(1001, 'Server is shutting down')
+ except:
+ pass
+ finally:
+ self.discard(greenlet)
+
+
+class WSGIServer(_WSGIServer):
+ handler_class = WebSocketWSGIHandler
+
+ def __init__(self, *args, **kwargs):
+ """
+ WSGI server that simply tracks websockets
+ and send them a proper closing handshake
+ when the server terminates.
+
+ Other than that, the server is the same
+ as its :class:`gevent.pywsgi.WSGIServer`
+ base.
+ """
+ _WSGIServer.__init__(self, *args, **kwargs)
+ self.pool = GEventWebSocketPool()
+
+ def stop(self, *args, **kwargs):
+ self.pool.clear()
+ _WSGIServer.stop(self, *args, **kwargs)
+
+
+if __name__ == '__main__':
+
+ from ws4py import configure_logger
+ configure_logger()
+
+ from ws4py.websocket import EchoWebSocket
+ server = WSGIServer(('127.0.0.1', 9000),
+ WebSocketWSGIApplication(handler_cls=EchoWebSocket))
+ server.serve_forever()
diff --git a/game/python-extra/ws4py/server/tulipserver.py b/game/python-extra/ws4py/server/tulipserver.py
new file mode 100644
index 0000000..2786c16
--- /dev/null
+++ b/game/python-extra/ws4py/server/tulipserver.py
@@ -0,0 +1,224 @@
+# -*- coding: utf-8 -*-
+import base64
+from hashlib import sha1
+from email.parser import BytesHeaderParser
+import io
+
+import asyncio
+
+from ws4py import WS_KEY, WS_VERSION
+from ws4py.exc import HandshakeError
+from ws4py.websocket import WebSocket
+
+LF = b'\n'
+CRLF = b'\r\n'
+SPACE = b' '
+EMPTY = b''
+
+__all__ = ['WebSocketProtocol']
+
+class WebSocketProtocol(asyncio.StreamReaderProtocol):
+ def __init__(self, handler_cls):
+ asyncio.StreamReaderProtocol.__init__(self, asyncio.StreamReader(),
+ self._pseudo_connected)
+ self.ws = handler_cls(self)
+
+ def _pseudo_connected(self, reader, writer):
+ pass
+
+ def connection_made(self, transport):
+ """
+ A peer is now connected and we receive an instance
+ of the underlying :class:`asyncio.Transport`.
+
+ We :class:`asyncio.StreamReader` is created
+ and the transport is associated before the
+ initial HTTP handshake is undertaken.
+ """
+ #self.transport = transport
+ #self.stream = asyncio.StreamReader()
+ #self.stream.set_transport(transport)
+ asyncio.StreamReaderProtocol.connection_made(self, transport)
+ # Let make it concurrent for others to tag along
+ f = asyncio.async(self.handle_initial_handshake())
+ f.add_done_callback(self.terminated)
+
+ @property
+ def writer(self):
+ return self._stream_writer
+
+ @property
+ def reader(self):
+ return self._stream_reader
+
+ def terminated(self, f):
+ if f.done() and not f.cancelled():
+ ex = f.exception()
+ if ex:
+ response = [b'HTTP/1.0 400 Bad Request']
+ response.append(b'Content-Length: 0')
+ response.append(b'Connection: close')
+ response.append(b'')
+ response.append(b'')
+ self.writer.write(CRLF.join(response))
+ self.ws.close_connection()
+
+ def close(self):
+ """
+ Initiate the websocket closing handshake
+ which will eventuall lead to the underlying
+ transport.
+ """
+ self.ws.close()
+
+ def timeout(self):
+ self.ws.close_connection()
+ if self.ws.started:
+ self.ws.closed(1002, "Peer connection timed-out")
+
+ def connection_lost(self, exc):
+ """
+ The peer connection is now, the closing
+ handshake won't work so let's not even try.
+ However let's make the websocket handler
+ be aware of it by calling its `closed`
+ method.
+ """
+ if exc is not None:
+ self.ws.close_connection()
+ if self.ws.started:
+ self.ws.closed(1002, "Peer connection was lost")
+
+ @asyncio.coroutine
+ def handle_initial_handshake(self):
+ """
+ Performs the HTTP handshake described in :rfc:`6455`. Note that
+ this implementation is really basic and it is strongly advised
+ against using it in production. It would probably break for
+ most clients. If you want a better support for HTTP, please
+ use a more reliable HTTP server implemented using asyncio.
+ """
+ request_line = yield from self.next_line()
+ method, uri, req_protocol = request_line.strip().split(SPACE, 2)
+
+ # GET required
+ if method.upper() != b'GET':
+ raise HandshakeError('HTTP method must be a GET')
+
+ headers = yield from self.read_headers()
+ if req_protocol == b'HTTP/1.1' and 'Host' not in headers:
+ raise ValueError("Missing host header")
+
+ for key, expected_value in [('Upgrade', 'websocket'),
+ ('Connection', 'upgrade')]:
+ actual_value = headers.get(key, '').lower()
+ if not actual_value:
+ raise HandshakeError('Header %s is not defined' % str(key))
+ if expected_value not in actual_value:
+ raise HandshakeError('Illegal value for header %s: %s' %
+ (key, actual_value))
+
+ response_headers = {}
+
+ ws_version = WS_VERSION
+ version = headers.get('Sec-WebSocket-Version')
+ supported_versions = ', '.join([str(v) for v in ws_version])
+ version_is_valid = False
+ if version:
+ try: version = int(version)
+ except: pass
+ else: version_is_valid = version in ws_version
+
+ if not version_is_valid:
+ response_headers['Sec-WebSocket-Version'] = supported_versions
+ raise HandshakeError('Unhandled or missing WebSocket version')
+
+ key = headers.get('Sec-WebSocket-Key')
+ if key:
+ ws_key = base64.b64decode(key.encode('utf-8'))
+ if len(ws_key) != 16:
+ raise HandshakeError("WebSocket key's length is invalid")
+
+ protocols = []
+ ws_protocols = []
+ subprotocols = headers.get('Sec-WebSocket-Protocol')
+ if subprotocols:
+ for s in subprotocols.split(','):
+ s = s.strip()
+ if s in protocols:
+ ws_protocols.append(s)
+
+ exts = []
+ ws_extensions = []
+ extensions = headers.get('Sec-WebSocket-Extensions')
+ if extensions:
+ for ext in extensions.split(','):
+ ext = ext.strip()
+ if ext in exts:
+ ws_extensions.append(ext)
+
+ self.ws.protocols = ws_protocols
+ self.ws.extensions = ws_extensions
+ self.ws.headers = headers
+
+ response = [req_protocol + b' 101 Switching Protocols']
+ response.append(b'Upgrade: websocket')
+ response.append(b'Content-Type: text/plain')
+ response.append(b'Content-Length: 0')
+ response.append(b'Connection: Upgrade')
+ response.append(b'Sec-WebSocket-Version:' + bytes(str(version), 'utf-8'))
+ response.append(b'Sec-WebSocket-Accept:' + base64.b64encode(sha1(key.encode('utf-8') + WS_KEY).digest()))
+ if ws_protocols:
+ response.append(b'Sec-WebSocket-Protocol:' + b', '.join(ws_protocols))
+ if ws_extensions:
+ response.append(b'Sec-WebSocket-Extensions:' + b','.join(ws_extensions))
+ response.append(b'')
+ response.append(b'')
+ self.writer.write(CRLF.join(response))
+ yield from self.handle_websocket()
+
+ @asyncio.coroutine
+ def handle_websocket(self):
+ """
+ Starts the websocket process until the
+ exchange is completed and terminated.
+ """
+ yield from self.ws.run()
+
+ @asyncio.coroutine
+ def read_headers(self):
+ """
+ Read all HTTP headers from the HTTP request
+ and returns a dictionary of them.
+ """
+ headers = b''
+ while True:
+ line = yield from self.next_line()
+ headers += line
+ if line == CRLF:
+ break
+ return BytesHeaderParser().parsebytes(headers)
+
+ @asyncio.coroutine
+ def next_line(self):
+ """
+ Reads data until \r\n is met and then return all read
+ bytes.
+ """
+ line = yield from self.reader.readline()
+ if not line.endswith(CRLF):
+ raise ValueError("Missing mandatory trailing CRLF")
+ return line
+
+if __name__ == '__main__':
+ from ws4py.async_websocket import EchoWebSocket
+
+ loop = asyncio.get_event_loop()
+
+ def start_server():
+ proto_factory = lambda: WebSocketProtocol(EchoWebSocket)
+ return loop.create_server(proto_factory, '', 9007)
+
+ s = loop.run_until_complete(start_server())
+ print('serving on', s.sockets[0].getsockname())
+ loop.run_forever()
diff --git a/game/python-extra/ws4py/server/wsgirefserver.py b/game/python-extra/ws4py/server/wsgirefserver.py
new file mode 100644
index 0000000..d4a9d9a
--- /dev/null
+++ b/game/python-extra/ws4py/server/wsgirefserver.py
@@ -0,0 +1,157 @@
+# -*- coding: utf-8 -*-
+__doc__ = """
+Add WebSocket support to the built-in WSGI server
+provided by the :py:mod:`wsgiref`. This is clearly not
+meant to be a production server so please consider this
+only for testing purpose.
+
+Mostly, this module overrides bits and pieces of
+the built-in classes so that it supports the WebSocket
+workflow.
+
+.. code-block:: python
+
+ from wsgiref.simple_server import make_server
+ from ws4py.websocket import EchoWebSocket
+ from ws4py.server.wsgirefserver import WSGIServer, WebSocketWSGIRequestHandler
+ from ws4py.server.wsgiutils import WebSocketWSGIApplication
+
+ server = make_server('', 9000, server_class=WSGIServer,
+ handler_class=WebSocketWSGIRequestHandler,
+ app=WebSocketWSGIApplication(handler_cls=EchoWebSocket))
+ server.initialize_websockets_manager()
+ server.serve_forever()
+
+.. note::
+ For some reason this server may fail against autobahntestsuite.
+"""
+import logging
+import sys
+import itertools
+import operator
+from wsgiref.handlers import SimpleHandler
+from wsgiref.simple_server import WSGIRequestHandler, WSGIServer as _WSGIServer
+from wsgiref import util
+
+util._hoppish = {}.__contains__
+
+from ws4py.manager import WebSocketManager
+from ws4py import format_addresses
+from ws4py.server.wsgiutils import WebSocketWSGIApplication
+from ws4py.compat import get_connection
+
+__all__ = ['WebSocketWSGIHandler', 'WebSocketWSGIRequestHandler',
+ 'WSGIServer']
+
+logger = logging.getLogger('ws4py')
+
+class WebSocketWSGIHandler(SimpleHandler):
+ def setup_environ(self):
+ """
+ Setup the environ dictionary and add the
+ `'ws4py.socket'` key. Its associated value
+ is the real socket underlying socket.
+ """
+ SimpleHandler.setup_environ(self)
+ self.environ['ws4py.socket'] = get_connection(self.environ['wsgi.input'])
+ self.http_version = self.environ['SERVER_PROTOCOL'].rsplit('/')[-1]
+
+ def finish_response(self):
+ """
+ Completes the response and performs the following tasks:
+
+ - Remove the `'ws4py.socket'` and `'ws4py.websocket'`
+ environ keys.
+ - Attach the returned websocket, if any, to the WSGI server
+ using its ``link_websocket_to_server`` method.
+ """
+ # force execution of the result iterator until first actual content
+ rest = iter(self.result)
+ first = list(itertools.islice(rest, 1))
+ self.result = itertools.chain(first, rest)
+
+ # now it's safe to look if environ was modified
+ ws = None
+ if self.environ:
+ self.environ.pop('ws4py.socket', None)
+ ws = self.environ.pop('ws4py.websocket', None)
+
+ try:
+ SimpleHandler.finish_response(self)
+ except:
+ if ws:
+ ws.close(1011, reason='Something broke')
+ raise
+ else:
+ if ws:
+ self.request_handler.server.link_websocket_to_server(ws)
+
+class WebSocketWSGIRequestHandler(WSGIRequestHandler):
+ WebSocketWSGIHandler = WebSocketWSGIHandler
+ def handle(self):
+ """
+ Unfortunately the base class forces us
+ to override the whole method to actually provide our wsgi handler.
+ """
+ self.raw_requestline = self.rfile.readline()
+ if not self.parse_request(): # An error code has been sent, just exit
+ return
+
+ # next line is where we'd have expect a configuration key somehow
+ handler = self.WebSocketWSGIHandler(
+ self.rfile, self.wfile, self.get_stderr(), self.get_environ()
+ )
+ handler.request_handler = self # backpointer for logging
+ handler.run(self.server.get_app())
+
+class WSGIServer(_WSGIServer):
+ def initialize_websockets_manager(self):
+ """
+ Call thos to start the underlying websockets
+ manager. Make sure to call it once your server
+ is created.
+ """
+ self.manager = WebSocketManager()
+ self.manager.start()
+
+ def shutdown_request(self, request):
+ """
+ The base class would close our socket
+ if we didn't override it.
+ """
+ pass
+
+ def link_websocket_to_server(self, ws):
+ """
+ Call this from your WSGI handler when a websocket
+ has been created.
+ """
+ self.manager.add(ws)
+
+ def server_close(self):
+ """
+ Properly initiate closing handshakes on
+ all websockets when the WSGI server terminates.
+ """
+ if hasattr(self, 'manager'):
+ self.manager.close_all()
+ self.manager.stop()
+ self.manager.join()
+ delattr(self, 'manager')
+ _WSGIServer.server_close(self)
+
+if __name__ == '__main__':
+ from ws4py import configure_logger
+ configure_logger()
+
+ from wsgiref.simple_server import make_server
+ from ws4py.websocket import EchoWebSocket
+
+ server = make_server('', 9000, server_class=WSGIServer,
+ handler_class=WebSocketWSGIRequestHandler,
+ app=WebSocketWSGIApplication(handler_cls=EchoWebSocket))
+ server.initialize_websockets_manager()
+ try:
+ server.serve_forever()
+ except KeyboardInterrupt:
+ server.server_close()
diff --git a/game/python-extra/ws4py/server/wsgiutils.py b/game/python-extra/ws4py/server/wsgiutils.py
new file mode 100644
index 0000000..efd3242
--- /dev/null
+++ b/game/python-extra/ws4py/server/wsgiutils.py
@@ -0,0 +1,162 @@
+# -*- coding: utf-8 -*-
+__doc__ = """
+This module provides a WSGI application suitable
+for a WSGI server such as gevent or wsgiref for instance.
+
+:pep:`333` couldn't foresee a protocol such as
+WebSockets but luckily the way the initial
+protocol upgrade was designed means that we can
+fit the handshake in a WSGI flow.
+
+The handshake validates the request against
+some internal or user-provided values and
+fails the request if the validation doesn't
+complete.
+
+On success, the provided WebSocket subclass
+is instanciated and stored into the
+`'ws4py.websocket'` environ key so that
+the WSGI server can handle it.
+
+The WSGI application returns an empty iterable
+since there is little value to return some
+content within the response to the handshake.
+
+A server wishing to support WebSocket via ws4py
+should:
+
+- Provide the real socket object to ws4py through the
+ `'ws4py.socket'` environ key. We can't use `'wsgi.input'`
+ as it may be wrapper to the socket we wouldn't know
+ how to extract the socket from.
+- Look for the `'ws4py.websocket'` key in the environ
+ when the application has returned and probably attach
+ it to a :class:`ws4py.manager.WebSocketManager` instance
+ so that the websocket runs its life.
+- Remove the `'ws4py.websocket'` and `'ws4py.socket'`
+ environ keys once the application has returned.
+ No need for these keys to persist.
+- Not close the underlying socket otherwise, well,
+ your websocket will also shutdown.
+
+.. warning::
+
+ The WSGI application sets the `'Upgrade'` header response
+ as specified by :rfc:`6455`. This is not tolerated by
+ :pep:`333` since it's a hop-by-hop header.
+ We expect most servers won't mind.
+"""
+import base64
+from hashlib import sha1
+import logging
+import sys
+
+from ws4py.websocket import WebSocket
+from ws4py.exc import HandshakeError
+from ws4py.compat import unicode, py3k
+from ws4py import WS_VERSION, WS_KEY, format_addresses
+
+logger = logging.getLogger('ws4py')
+
+__all__ = ['WebSocketWSGIApplication']
+
+class WebSocketWSGIApplication(object):
+ def __init__(self, protocols=None, extensions=None, handler_cls=WebSocket):
+ """
+ WSGI application usable to complete the upgrade handshake
+ by validating the requested protocols and extensions as
+ well as the websocket version.
+
+ If the upgrade validates, the `handler_cls` class
+ is instanciated and stored inside the WSGI `environ`
+ under the `'ws4py.websocket'` key to make it
+ available to the WSGI handler.
+ """
+ self.protocols = protocols
+ self.extensions = extensions
+ self.handler_cls = handler_cls
+
+ def make_websocket(self, sock, protocols, extensions, environ):
+ """
+ Initialize the `handler_cls` instance with the given
+ negociated sets of protocols and extensions as well as
+ the `environ` and `sock`.
+
+ Stores then the instance in the `environ` dict
+ under the `'ws4py.websocket'` key.
+ """
+ websocket = self.handler_cls(sock, protocols, extensions,
+ environ.copy())
+ environ['ws4py.websocket'] = websocket
+ return websocket
+
+ def __call__(self, environ, start_response):
+ if environ.get('REQUEST_METHOD') != 'GET':
+ raise HandshakeError('HTTP method must be a GET')
+
+ for key, expected_value in [('HTTP_UPGRADE', 'websocket'),
+ ('HTTP_CONNECTION', 'upgrade')]:
+ actual_value = environ.get(key, '').lower()
+ if not actual_value:
+ raise HandshakeError('Header %s is not defined' % key)
+ if expected_value not in actual_value:
+ raise HandshakeError('Illegal value for header %s: %s' %
+ (key, actual_value))
+
+ key = environ.get('HTTP_SEC_WEBSOCKET_KEY')
+ if key:
+ ws_key = base64.b64decode(key.encode('utf-8'))
+ if len(ws_key) != 16:
+ raise HandshakeError("WebSocket key's length is invalid")
+
+ version = environ.get('HTTP_SEC_WEBSOCKET_VERSION')
+ supported_versions = b', '.join([unicode(v).encode('utf-8') for v in WS_VERSION])
+ version_is_valid = False
+ if version:
+ try: version = int(version)
+ except: pass
+ else: version_is_valid = version in WS_VERSION
+
+ if not version_is_valid:
+ environ['websocket.version'] = unicode(version).encode('utf-8')
+ raise HandshakeError('Unhandled or missing WebSocket version')
+
+ ws_protocols = []
+ protocols = self.protocols or []
+ subprotocols = environ.get('HTTP_SEC_WEBSOCKET_PROTOCOL')
+ if subprotocols:
+ for s in subprotocols.split(','):
+ s = s.strip()
+ if s in protocols:
+ ws_protocols.append(s)
+
+ ws_extensions = []
+ exts = self.extensions or []
+ extensions = environ.get('HTTP_SEC_WEBSOCKET_EXTENSIONS')
+ if extensions:
+ for ext in extensions.split(','):
+ ext = ext.strip()
+ if ext in exts:
+ ws_extensions.append(ext)
+
+ accept_value = base64.b64encode(sha1(key.encode('utf-8') + WS_KEY).digest())
+ if py3k: accept_value = accept_value.decode('utf-8')
+ upgrade_headers = [
+ ('Upgrade', 'websocket'),
+ ('Connection', 'Upgrade'),
+ ('Sec-WebSocket-Version', '%s' % version),
+ ('Sec-WebSocket-Accept', accept_value),
+ ]
+ if ws_protocols:
+ upgrade_headers.append(('Sec-WebSocket-Protocol', ', '.join(ws_protocols)))
+ if ws_extensions:
+ upgrade_headers.append(('Sec-WebSocket-Extensions', ','.join(ws_extensions)))
+
+ start_response("101 Switching Protocols", upgrade_headers)
+
+ self.make_websocket(environ['ws4py.socket'],
+ ws_protocols,
+ ws_extensions,
+ environ)
+
+ return []
diff --git a/game/python-extra/ws4py/streaming.py b/game/python-extra/ws4py/streaming.py
new file mode 100644
index 0000000..4420ba1
--- /dev/null
+++ b/game/python-extra/ws4py/streaming.py
@@ -0,0 +1,319 @@
+# -*- coding: utf-8 -*-
+import struct
+from struct import unpack
+
+from ws4py.utf8validator import Utf8Validator
+from ws4py.messaging import TextMessage, BinaryMessage, CloseControlMessage,\
+ PingControlMessage, PongControlMessage
+from ws4py.framing import Frame, OPCODE_CONTINUATION, OPCODE_TEXT, \
+ OPCODE_BINARY, OPCODE_CLOSE, OPCODE_PING, OPCODE_PONG
+from ws4py.exc import FrameTooLargeException, ProtocolException, InvalidBytesError,\
+ TextFrameEncodingException, UnsupportedFrameTypeException, StreamClosed
+from ws4py.compat import py3k
+
+VALID_CLOSING_CODES = [1000, 1001, 1002, 1003, 1007, 1008, 1009, 1010, 1011]
+
+class Stream(object):
+ def __init__(self, always_mask=False, expect_masking=True):
+ """ Represents a websocket stream of bytes flowing in and out.
+
+ The stream doesn't know about the data provider itself and
+ doesn't even know about sockets. Instead the stream simply
+ yields for more bytes whenever it requires them. The stream owner
+ is responsible to provide the stream with those bytes until
+ a frame can be interpreted.
+
+ .. code-block:: python
+ :linenos:
+
+ >>> s = Stream()
+ >>> s.parser.send(BYTES)
+ >>> s.has_messages
+ False
+ >>> s.parser.send(MORE_BYTES)
+ >>> s.has_messages
+ True
+ >>> s.message
+ <TextMessage ... >
+
+ Set ``always_mask`` to mask all frames built.
+
+ Set ``expect_masking`` to indicate masking will be
+ checked on all parsed frames.
+ """
+
+ self.message = None
+ """
+ Parsed test or binary messages. Whenever the parser
+ reads more bytes from a fragment message, those bytes
+ are appended to the most recent message.
+ """
+
+ self.pings = []
+ """
+ Parsed ping control messages. They are instances of
+ :class:`ws4py.messaging.PingControlMessage`
+ """
+
+ self.pongs = []
+ """
+ Parsed pong control messages. They are instances of
+ :class:`ws4py.messaging.PongControlMessage`
+ """
+
+ self.closing = None
+ """
+ Parsed close control messsage. Instance of
+ :class:`ws4py.messaging.CloseControlMessage`
+ """
+
+ self.errors = []
+ """
+ Detected errors while parsing. Instances of
+ :class:`ws4py.messaging.CloseControlMessage`
+ """
+
+ self._parser = None
+ """
+ Parser in charge to process bytes it is fed with.
+ """
+
+ self.always_mask = always_mask
+ self.expect_masking = expect_masking
+
+ @property
+ def parser(self):
+ if self._parser is None:
+ self._parser = self.receiver()
+ # Python generators must be initialized once.
+ next(self.parser)
+ return self._parser
+
+ def _cleanup(self):
+ """
+ Frees the stream's resources rendering it unusable.
+ """
+ self.message = None
+ if self._parser is not None:
+ if not self._parser.gi_running:
+ self._parser.close()
+ self._parser = None
+ self.errors = None
+ self.pings = None
+ self.pongs = None
+ self.closing = None
+
+ def text_message(self, text):
+ """
+ Returns a :class:`ws4py.messaging.TextMessage` instance
+ ready to be built. Convenience method so
+ that the caller doesn't need to import the
+ :class:`ws4py.messaging.TextMessage` class itself.
+ """
+ return TextMessage(text=text)
+
+ def binary_message(self, bytes):
+ """
+ Returns a :class:`ws4py.messaging.BinaryMessage` instance
+ ready to be built. Convenience method so
+ that the caller doesn't need to import the
+ :class:`ws4py.messaging.BinaryMessage` class itself.
+ """
+ return BinaryMessage(bytes)
+
+ @property
+ def has_message(self):
+ """
+ Checks if the stream has received any message
+ which, if fragmented, is now completed.
+ """
+ if self.message is not None:
+ return self.message.completed
+
+ return False
+
+ def close(self, code=1000, reason=''):
+ """
+ Returns a close control message built from
+ a :class:`ws4py.messaging.CloseControlMessage` instance,
+ using the given status ``code`` and ``reason`` message.
+ """
+ return CloseControlMessage(code=code, reason=reason)
+
+ def ping(self, data=''):
+ """
+ Returns a ping control message built from
+ a :class:`ws4py.messaging.PingControlMessage` instance.
+ """
+ return PingControlMessage(data).single(mask=self.always_mask)
+
+ def pong(self, data=''):
+ """
+ Returns a ping control message built from
+ a :class:`ws4py.messaging.PongControlMessage` instance.
+ """
+ return PongControlMessage(data).single(mask=self.always_mask)
+
+ def receiver(self):
+ """
+ Parser that keeps trying to interpret bytes it is fed with as
+ incoming frames part of a message.
+
+ Control message are single frames only while data messages, like text
+ and binary, may be fragmented accross frames.
+
+ The way it works is by instanciating a :class:`wspy.framing.Frame` object,
+ then running its parser generator which yields how much bytes
+ it requires to performs its task. The stream parser yields this value
+ to its caller and feeds the frame parser.
+
+ When the frame parser raises :exc:`StopIteration`, the stream parser
+ tries to make sense of the parsed frame. It dispatches the frame's bytes
+ to the most appropriate message type based on the frame's opcode.
+
+ Overall this makes the stream parser totally agonstic to
+ the data provider.
+ """
+ utf8validator = Utf8Validator()
+ running = True
+ frame = None
+ while running:
+ frame = Frame()
+ while 1:
+ try:
+ some_bytes = (yield next(frame.parser))
+ frame.parser.send(some_bytes)
+ except GeneratorExit:
+ running = False
+ break
+ except StopIteration:
+ frame._cleanup()
+ some_bytes = frame.body
+
+ # Let's avoid unmasking when there is no payload
+ if some_bytes:
+ if frame.masking_key and self.expect_masking:
+ some_bytes = frame.unmask(some_bytes)
+ elif not frame.masking_key and self.expect_masking:
+ msg = CloseControlMessage(code=1002, reason='Missing masking when expected')
+ self.errors.append(msg)
+ break
+ elif frame.masking_key and not self.expect_masking:
+ msg = CloseControlMessage(code=1002, reason='Masked when not expected')
+ self.errors.append(msg)
+ break
+ else:
+ # If we reach this stage, it's because
+ # the frame wasn't masked and we didn't expect
+ # it anyway. Therefore, on py2k, the bytes
+ # are actually a str object and can't be used
+ # in the utf8 validator as we need integers
+ # when we get each byte one by one.
+ # Our only solution here is to convert our
+ # string to a bytearray.
+ some_bytes = bytearray(some_bytes)
+
+ if frame.opcode == OPCODE_TEXT:
+ if self.message and not self.message.completed:
+ # We got a text frame before we completed the previous one
+ msg = CloseControlMessage(code=1002, reason='Received a new message before completing previous')
+ self.errors.append(msg)
+ break
+
+ m = TextMessage(some_bytes)
+ m.completed = (frame.fin == 1)
+ self.message = m
+
+ if some_bytes:
+ is_valid, end_on_code_point, _, _ = utf8validator.validate(some_bytes)
+
+ if not is_valid or (m.completed and not end_on_code_point):
+ self.errors.append(CloseControlMessage(code=1007, reason='Invalid UTF-8 bytes'))
+ break
+
+ elif frame.opcode == OPCODE_BINARY:
+ if self.message and not self.message.completed:
+ # We got a text frame before we completed the previous one
+ msg = CloseControlMessage(code=1002, reason='Received a new message before completing previous')
+ self.errors.append(msg)
+ break
+
+ m = BinaryMessage(some_bytes)
+ m.completed = (frame.fin == 1)
+ self.message = m
+
+ elif frame.opcode == OPCODE_CONTINUATION:
+ m = self.message
+ if m is None:
+ self.errors.append(CloseControlMessage(code=1002, reason='Message not started yet'))
+ break
+
+ m.extend(some_bytes)
+ m.completed = (frame.fin == 1)
+ if m.opcode == OPCODE_TEXT:
+ if some_bytes:
+ is_valid, end_on_code_point, _, _ = utf8validator.validate(some_bytes)
+
+ if not is_valid or (m.completed and not end_on_code_point):
+ self.errors.append(CloseControlMessage(code=1007, reason='Invalid UTF-8 bytes'))
+ break
+
+ elif frame.opcode == OPCODE_CLOSE:
+ code = 1005
+ reason = ""
+ if frame.payload_length == 0:
+ self.closing = CloseControlMessage(code=1005)
+ elif frame.payload_length == 1:
+ self.closing = CloseControlMessage(code=1005, reason='Payload has invalid length')
+ else:
+ try:
+ # at this stage, some_bytes have been unmasked
+ # so actually are held in a bytearray
+ code = int(unpack("!H", bytes(some_bytes[0:2]))[0])
+ except struct.error:
+ reason = 'Failed at decoding closing code'
+ else:
+ # Those codes are reserved or plainly forbidden
+ if code not in VALID_CLOSING_CODES and not (2999 < code < 5000):
+ reason = 'Invalid Closing Frame Code: %d' % code
+ code = 1005
+ elif frame.payload_length > 1:
+ reason = some_bytes[2:] if frame.masking_key else frame.body[2:]
+
+ if not py3k: reason = bytearray(reason)
+ is_valid, end_on_code_point, _, _ = utf8validator.validate(reason)
+ if not is_valid or not end_on_code_point:
+ self.errors.append(CloseControlMessage(code=1007, reason='Invalid UTF-8 bytes'))
+ break
+ reason = bytes(reason)
+ self.closing = CloseControlMessage(code=code, reason=reason)
+
+ elif frame.opcode == OPCODE_PING:
+ self.pings.append(PingControlMessage(some_bytes))
+
+ elif frame.opcode == OPCODE_PONG:
+ self.pongs.append(PongControlMessage(some_bytes))
+
+ else:
+ self.errors.append(CloseControlMessage(code=1003))
+
+ break
+
+ except ProtocolException:
+ self.errors.append(CloseControlMessage(code=1002))
+ break
+ except FrameTooLargeException:
+ self.errors.append(CloseControlMessage(code=1002, reason="Frame was too large"))
+ break
+
+ frame._cleanup()
+ frame.body = None
+ frame = None
+
+ if self.message is not None and self.message.completed:
+ utf8validator.reset()
+
+ utf8validator.reset()
+ utf8validator = None
+
+ self._cleanup()
diff --git a/game/python-extra/ws4py/utf8validator.py b/game/python-extra/ws4py/utf8validator.py
new file mode 100644
index 0000000..50b19e5
--- /dev/null
+++ b/game/python-extra/ws4py/utf8validator.py
@@ -0,0 +1,117 @@
+# coding=utf-8
+
+###############################################################################
+##
+## Copyright 2011 Tavendo GmbH
+##
+## Note:
+##
+## This code is a Python implementation of the algorithm
+##
+## "Flexible and Economical UTF-8 Decoder"
+##
+## by Bjoern Hoehrmann
+##
+## bjoern@hoehrmann.de
+## http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
+##
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+##
+###############################################################################
+
+
+class Utf8Validator(object):
+ """
+ Incremental UTF-8 validator with constant memory consumption (minimal state).
+
+ Implements the algorithm "Flexible and Economical UTF-8 Decoder" by
+ Bjoern Hoehrmann (http://bjoern.hoehrmann.de/utf-8/decoder/dfa/).
+ """
+
+ ## DFA transitions
+ UTF8VALIDATOR_DFA = [
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 00..1f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 20..3f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 40..5f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 60..7f
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, # 80..9f
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, # a0..bf
+ 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, # c0..df
+ 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, # e0..ef
+ 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, # f0..ff
+ 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, # s0..s0
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, # s1..s2
+ 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, # s3..s4
+ 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, # s5..s6
+ 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, # s7..s8
+ ]
+
+ UTF8_ACCEPT = 0
+ UTF8_REJECT = 1
+
+ def __init__(self):
+ self.reset()
+
+ def decode(self, b):
+ """
+ Eat one UTF-8 octet, and validate on the fly.
+
+ Returns UTF8_ACCEPT when enough octets have been consumed, in which case
+ self.codepoint contains the decoded Unicode code point.
+
+ Returns UTF8_REJECT when invalid UTF-8 was encountered.
+
+ Returns some other positive integer when more octets need to be eaten.
+ """
+ type = Utf8Validator.UTF8VALIDATOR_DFA[b]
+ if self.state != Utf8Validator.UTF8_ACCEPT:
+ self.codepoint = (b & 0x3f) | (self.codepoint << 6)
+ else:
+ self.codepoint = (0xff >> type) & b
+ self.state = Utf8Validator.UTF8VALIDATOR_DFA[256 + self.state * 16 + type]
+ return self.state
+
+ def reset(self):
+ """
+ Reset validator to start new incremental UTF-8 decode/validation.
+ """
+ self.state = Utf8Validator.UTF8_ACCEPT
+ self.codepoint = 0
+ self.i = 0
+
+ def validate(self, ba):
+ """
+ Incrementally validate a chunk of bytes provided as bytearray.
+
+ Will return a quad (valid?, endsOnCodePoint?, currentIndex, totalIndex).
+
+ As soon as an octet is encountered which renders the octet sequence
+ invalid, a quad with valid? == False is returned. currentIndex returns
+ the index within the currently consumed chunk, and totalIndex the
+ index within the total consumed sequence that was the point of bail out.
+ When valid? == True, currentIndex will be len(ba) and totalIndex the
+ total amount of consumed bytes.
+ """
+ state = self.state
+ DFA = Utf8Validator.UTF8VALIDATOR_DFA
+ i = 0 # make sure 'i' is set if when 'ba' is empty
+ for i, b in enumerate(ba):
+ ## optimized version of decode(), since we are not interested in actual code points
+ state = DFA[256 + (state << 4) + DFA[b]]
+ if state == Utf8Validator.UTF8_REJECT:
+ self.i += i
+ self.state = state
+ return False, False, i, self.i
+ self.i += i
+ self.state = state
+ return True, state == Utf8Validator.UTF8_ACCEPT, i, self.i
diff --git a/game/python-extra/ws4py/websocket.py b/game/python-extra/ws4py/websocket.py
new file mode 100644
index 0000000..f7e9e3a
--- /dev/null
+++ b/game/python-extra/ws4py/websocket.py
@@ -0,0 +1,615 @@
+# -*- coding: utf-8 -*-
+import logging
+import socket
+import ssl
+import time
+import threading
+import types
+import errno
+
+try:
+ from OpenSSL.SSL import Error as pyOpenSSLError
+except ImportError:
+ class pyOpenSSLError(Exception):
+ pass
+
+from ws4py import WS_KEY, WS_VERSION
+from ws4py.exc import HandshakeError, StreamClosed
+from ws4py.streaming import Stream
+from ws4py.messaging import Message, PingControlMessage,\
+ PongControlMessage
+from ws4py.compat import basestring, unicode
+
+DEFAULT_READING_SIZE = 2
+
+logger = logging.getLogger('ws4py')
+
+__all__ = ['WebSocket', 'EchoWebSocket', 'Heartbeat']
+
+class Heartbeat(threading.Thread):
+ def __init__(self, websocket, frequency=2.0):
+ """
+ Runs at a periodic interval specified by
+ `frequency` by sending an unsolicitated pong
+ message to the connected peer.
+
+ If the message fails to be sent and a socket
+ error is raised, we close the websocket
+ socket automatically, triggering the `closed`
+ handler.
+ """
+ threading.Thread.__init__(self)
+ self.websocket = websocket
+ self.frequency = frequency
+
+ def __enter__(self):
+ if self.frequency:
+ self.start()
+ return self
+
+ def __exit__(self, exc_type, exc_value, exc_tb):
+ self.stop()
+
+ def stop(self):
+ self.running = False
+
+ def run(self):
+ self.running = True
+ while self.running:
+ time.sleep(self.frequency)
+ if self.websocket.terminated:
+ break
+
+ try:
+ self.websocket.send(PongControlMessage(data='beep'))
+ except socket.error:
+ logger.info("Heartbeat failed")
+ self.websocket.server_terminated = True
+ self.websocket.close_connection()
+ break
+
+class WebSocket(object):
+ """ Represents a websocket endpoint and provides a high level interface to drive the endpoint. """
+
+ def __init__(self, sock, protocols=None, extensions=None, environ=None, heartbeat_freq=None):
+ """ The ``sock`` is an opened connection
+ resulting from the websocket handshake.
+
+ If ``protocols`` is provided, it is a list of protocols
+ negotiated during the handshake as is ``extensions``.
+
+ If ``environ`` is provided, it is a copy of the WSGI environ
+ dictionnary from the underlying WSGI server.
+ """
+
+ self.stream = Stream(always_mask=False)
+ """
+ Underlying websocket stream that performs the websocket
+ parsing to high level objects. By default this stream
+ never masks its messages. Clients using this class should
+ set the ``stream.always_mask`` fields to ``True``
+ and ``stream.expect_masking`` fields to ``False``.
+ """
+
+ self.protocols = protocols
+ """
+ List of protocols supported by this endpoint.
+ Unused for now.
+ """
+
+ self.extensions = extensions
+ """
+ List of extensions supported by this endpoint.
+ Unused for now.
+ """
+
+ self.sock = sock
+ """
+ Underlying connection.
+ """
+
+ self._is_secure = hasattr(sock, '_ssl') or hasattr(sock, '_sslobj')
+ """
+ Tell us if the socket is secure or not.
+ """
+
+ self.client_terminated = False
+ """
+ Indicates if the client has been marked as terminated.
+ """
+
+ self.server_terminated = False
+ """
+ Indicates if the server has been marked as terminated.
+ """
+
+ self.reading_buffer_size = DEFAULT_READING_SIZE
+ """
+ Current connection reading buffer size.
+ """
+
+ self.environ = environ
+ """
+ WSGI environ dictionary.
+ """
+
+ self.heartbeat_freq = heartbeat_freq
+ """
+ At which interval the heartbeat will be running.
+ Set this to `0` or `None` to disable it entirely.
+ """
+ "Internal buffer to get around SSL problems"
+ self.buf = b''
+
+ self._local_address = None
+ self._peer_address = None
+
+ #########
+ # @TMW2
+ # Delays the reading if we are still busy writing
+ self.lock = False
+ # @TMW2
+ #########
+
+
+ @property
+ def local_address(self):
+ """
+ Local endpoint address as a tuple
+ """
+ if not self._local_address:
+ self._local_address = self.sock.getsockname()
+ if len(self._local_address) == 4:
+ self._local_address = self._local_address[:2]
+ return self._local_address
+
+ @property
+ def peer_address(self):
+ """
+ Peer endpoint address as a tuple
+ """
+ if not self._peer_address:
+ self._peer_address = self.sock.getpeername()
+ if len(self._peer_address) == 4:
+ self._peer_address = self._peer_address[:2]
+ return self._peer_address
+
+ def opened(self):
+ """
+ Called by the server when the upgrade handshake
+ has succeeded.
+ """
+ pass
+
+ def close(self, code=1000, reason=''):
+ """
+ Call this method to initiate the websocket connection
+ closing by sending a close frame to the connected peer.
+ The ``code`` is the status code representing the
+ termination's reason.
+
+ Once this method is called, the ``server_terminated``
+ attribute is set. Calling this method several times is
+ safe as the closing frame will be sent only the first
+ time.
+
+ .. seealso:: Defined Status Codes http://tools.ietf.org/html/rfc6455#section-7.4.1
+ """
+ if not self.server_terminated:
+ self.server_terminated = True
+ try:
+ self._write(self.stream.close(code=code, reason=reason).single(mask=self.stream.always_mask))
+ except Exception as ex:
+ logger.error("Error when terminating the connection: %s", str(ex))
+
+ def closed(self, code, reason=None):
+ """
+ Called when the websocket stream and connection are finally closed.
+ The provided ``code`` is status set by the other point and
+ ``reason`` is a human readable message.
+
+ .. seealso:: Defined Status Codes http://tools.ietf.org/html/rfc6455#section-7.4.1
+ """
+ pass
+
+ @property
+ def terminated(self):
+ """
+ Returns ``True`` if both the client and server have been
+ marked as terminated.
+ """
+ return self.client_terminated is True and self.server_terminated is True
+
+ @property
+ def connection(self):
+ return self.sock
+
+ def close_connection(self):
+ """
+ Shutdowns then closes the underlying connection.
+ """
+ if self.sock:
+ try:
+ self.sock.shutdown(socket.SHUT_RDWR)
+ self.sock.close()
+ except:
+ pass
+ finally:
+ self.sock = None
+
+ def ping(self, message):
+ """
+ Send a ping message to the remote peer.
+ The given `message` must be a unicode string.
+ """
+ self.send(PingControlMessage(message))
+
+ def ponged(self, pong):
+ """
+ Pong message, as a :class:`messaging.PongControlMessage` instance,
+ received on the stream.
+ """
+ pass
+
+ def received_message(self, message):
+ """
+ Called whenever a complete ``message``, binary or text,
+ is received and ready for application's processing.
+
+ The passed message is an instance of :class:`messaging.TextMessage`
+ or :class:`messaging.BinaryMessage`.
+
+ .. note:: You should override this method in your subclass.
+ """
+ pass
+
+ def unhandled_error(self, error):
+ """
+ Called whenever a socket, or an OS, error is trapped
+ by ws4py but not managed by it. The given error is
+ an instance of `socket.error` or `OSError`.
+
+ Note however that application exceptions will not go
+ through this handler. Instead, do make sure you
+ protect your code appropriately in `received_message`
+ or `send`.
+
+ The default behaviour of this handler is to log
+ the error with a message.
+ """
+ logger.exception("Failed to receive data")
+
+ def _write(self, b):
+ """
+ Trying to prevent a write operation
+ on an already closed websocket stream.
+
+ This cannot be bullet proof but hopefully
+ will catch almost all use cases.
+ """
+ if self.terminated or self.sock is None:
+ raise RuntimeError("Cannot send on a terminated websocket")
+
+ self.sock.sendall(b)
+
+ def send(self, payload, binary=False):
+ """
+ Sends the given ``payload`` out.
+
+ If ``payload`` is some bytes or a bytearray,
+ then it is sent as a single message not fragmented.
+
+ If ``payload`` is a generator, each chunk is sent as part of
+ fragmented message.
+
+ If ``binary`` is set, handles the payload as a binary message.
+ """
+ #########
+ # @TMW2
+ # Delays the reading if we are still busy writing
+ while self.lock:
+ time.sleep(0.02)
+ #print("Sending data")
+ self.lock = True
+ # @TMW2
+ #########
+
+ message_sender = self.stream.binary_message if binary else self.stream.text_message
+
+ if isinstance(payload, basestring) or isinstance(payload, bytearray):
+ m = message_sender(payload).single(mask=self.stream.always_mask)
+ self._write(m)
+
+ elif isinstance(payload, Message):
+ data = payload.single(mask=self.stream.always_mask)
+ self._write(data)
+
+ elif type(payload) == types.GeneratorType:
+ bytes = next(payload)
+ first = True
+ for chunk in payload:
+ self._write(message_sender(bytes).fragment(first=first, mask=self.stream.always_mask))
+ bytes = chunk
+ first = False
+
+ self._write(message_sender(bytes).fragment(first=first, last=True, mask=self.stream.always_mask))
+
+ else:
+ raise ValueError("Unsupported type '%s' passed to send()" % type(payload))
+
+ #########
+ # @TMW2
+ # Delays the reading if we are still busy writing
+ self.lock = False
+ #print("Done sending, unlocked")
+ # @TMW2
+ #########
+
+ def _get_from_pending(self):
+ """
+ The SSL socket object provides the same interface
+ as the socket interface but behaves differently.
+
+ When data is sent over a SSL connection
+ more data may be read than was requested from by
+ the ws4py websocket object.
+
+ In that case, the data may have been indeed read
+ from the underlying real socket, but not read by the
+ application which will expect another trigger from the
+ manager's polling mechanism as if more data was still on the
+ wire. This will happen only when new data is
+ sent by the other peer which means there will be
+ some delay before the initial read data is handled
+ by the application.
+
+ Due to this, we have to rely on a non-public method
+ to query the internal SSL socket buffer if it has indeed
+ more data pending in its buffer.
+
+ Now, some people in the Python community
+ `discourage <https://bugs.python.org/issue21430>`_
+ this usage of the ``pending()`` method because it's not
+ the right way of dealing with such use case. They advise
+ `this approach <https://docs.python.org/dev/library/ssl.html#notes-on-non-blocking-sockets>`_
+ instead. Unfortunately, this applies only if the
+ application can directly control the poller which is not
+ the case with the WebSocket abstraction here.
+
+ We therefore rely on this `technic <http://stackoverflow.com/questions/3187565/select-and-ssl-in-python>`_
+ which seems to be valid anyway.
+
+ This is a bit of a shame because we have to process
+ more data than what wanted initially.
+ """
+ data = b""
+ pending = self.sock.pending()
+ while pending:
+ data += self.sock.recv(pending)
+ pending = self.sock.pending()
+ return data
+
+ def once(self):
+ """
+ Performs the operation of reading from the underlying
+ connection in order to feed the stream of bytes.
+
+ Because this needs to support SSL sockets, we must always
+ read as much as might be in the socket at any given time,
+ however process expects to have itself called with only a certain
+ number of bytes at a time. That number is found in
+ self.reading_buffer_size, so we read everything into our own buffer,
+ and then from there feed self.process.
+
+ Then the stream indicates
+ whatever size must be read from the connection since
+ it knows the frame payload length.
+
+ It returns `False` if an error occurred at the
+ socket level or during the bytes processing. Otherwise,
+ it returns `True`.
+ """
+ if self.terminated:
+ logger.debug("WebSocket is already terminated")
+ return False
+ try:
+ b = b''
+ if self._is_secure:
+ b = self._get_from_pending()
+ if not b and not self.buf:
+ b = self.sock.recv(self.reading_buffer_size)
+ if not b and not self.buf:
+ return False
+ self.buf += b
+ except (socket.error, OSError, pyOpenSSLError) as e:
+ if hasattr(e, "errno") and e.errno == errno.EINTR:
+ pass
+ else:
+ self.unhandled_error(e)
+ return False
+ else:
+ # process as much as we can
+ # the process will stop either if there is no buffer left
+ # or if the stream is closed
+ # only pass the requested number of bytes, leave the rest in the buffer
+ requested = self.reading_buffer_size
+ if not self.process(self.buf[:requested]):
+ return False
+ self.buf = self.buf[requested:]
+
+ return True
+
+ def terminate(self):
+ """
+ Completes the websocket by calling the `closed`
+ method either using the received closing code
+ and reason, or when none was received, using
+ the special `1006` code.
+
+ Finally close the underlying connection for
+ good and cleanup resources by unsetting
+ the `environ` and `stream` attributes.
+ """
+ s = self.stream
+
+ try:
+ if s.closing is None:
+ self.closed(1006, "Going away")
+ else:
+ self.closed(s.closing.code, s.closing.reason)
+ finally:
+ self.client_terminated = self.server_terminated = True
+ self.close_connection()
+
+ # Cleaning up resources
+ s._cleanup()
+ self.stream = None
+ self.environ = None
+
+ def process(self, bytes):
+ """ Takes some bytes and process them through the
+ internal stream's parser. If a message of any kind is
+ found, performs one of these actions:
+
+ * A closing message will initiate the closing handshake
+ * Errors will initiate a closing handshake
+ * A message will be passed to the ``received_message`` method
+ * Pings will see pongs be sent automatically
+ * Pongs will be passed to the ``ponged`` method
+
+ The process should be terminated when this method
+ returns ``False``.
+ """
+ #########
+ # @TMW2
+ # Delays the reading if we are still busy writing
+ while self.lock:
+ time.sleep(0.02)
+ #print("processing, lock %d" % self.lock)
+ self.lock = True
+ #print("processing, lock %d" % self.lock)
+ err=1
+ # @TMW2
+ #########
+
+ s = self.stream
+
+ if not bytes and self.reading_buffer_size > 0:
+ #########
+ # @TMW2
+ self.lock = False
+ # @TMW2
+ #########
+ return False
+
+ #########
+ # @TMW2
+ while err:
+ try:
+ self.reading_buffer_size = s.parser.send(bytes) or DEFAULT_READING_SIZE
+ err=0
+ except ValueError:
+ time.sleep(0.1)
+ #print("ValueError happened, total %d" % err)
+ if not err % 20:
+ print("ValueError happened, total %d" % err)
+ err+=1
+ # @TMW2
+ #########
+
+ if s.closing is not None:
+ logger.debug("Closing message received (%d) '%s'" % (s.closing.code, s.closing.reason))
+ if not self.server_terminated:
+ self.close(s.closing.code, s.closing.reason)
+ else:
+ self.client_terminated = True
+ #########
+ # @TMW2
+ self.lock = False
+ # @TMW2
+ #########
+ return False
+
+ if s.errors:
+ for error in s.errors:
+ logger.debug("Error message received (%d) '%s'" % (error.code, error.reason))
+ self.close(error.code, error.reason)
+ s.errors = []
+ #########
+ # @TMW2
+ self.lock = False
+ # @TMW2
+ #########
+ return False
+
+ if s.has_message:
+ self.received_message(s.message)
+ if s.message is not None:
+ s.message.data = None
+ s.message = None
+ #########
+ # @TMW2
+ self.lock = False
+ # @TMW2
+ #########
+ return True
+
+ if s.pings:
+ for ping in s.pings:
+ self._write(s.pong(ping.data))
+ s.pings = []
+
+ if s.pongs:
+ for pong in s.pongs:
+ self.ponged(pong)
+ s.pongs = []
+
+ #########
+ # @TMW2
+ self.lock = False
+ # @TMW2
+ #########
+ return True
+
+ def run(self):
+ """
+ Performs the operation of reading from the underlying
+ connection in order to feed the stream of bytes.
+
+ We start with a small size of two bytes to be read
+ from the connection so that we can quickly parse an
+ incoming frame header. Then the stream indicates
+ whatever size must be read from the connection since
+ it knows the frame payload length.
+
+ Note that we perform some automatic opererations:
+
+ * On a closing message, we respond with a closing
+ message and finally close the connection
+ * We respond to pings with pong messages.
+ * Whenever an error is raised by the stream parsing,
+ we initiate the closing of the connection with the
+ appropiate error code.
+
+ This method is blocking and should likely be run
+ in a thread.
+ """
+ self.sock.setblocking(True)
+ with Heartbeat(self, frequency=self.heartbeat_freq):
+ s = self.stream
+
+ try:
+ self.opened()
+ while not self.terminated:
+ if not self.once():
+ break
+ finally:
+ self.terminate()
+
+class EchoWebSocket(WebSocket):
+ def received_message(self, message):
+ """
+ Automatically sends back the provided ``message`` to
+ its originating endpoint.
+ """
+ self.send(message.data, message.is_binary)