commit
						be5c2aef3a
					
				
				 69 changed files with 8796 additions and 0 deletions
			
			
		@ -0,0 +1,89 @@ | 
				
			|||||||
 | 
					Metadata-Version: 1.1 | 
				
			||||||
 | 
					Name: backports.ssl-match-hostname | 
				
			||||||
 | 
					Version: 3.7.0.1 | 
				
			||||||
 | 
					Summary: The ssl.match_hostname() function from Python 3.5 | 
				
			||||||
 | 
					Home-page: http://bitbucket.org/brandon/backports.ssl_match_hostname | 
				
			||||||
 | 
					Author: Toshio Kuratomi | 
				
			||||||
 | 
					Author-email: toshio@fedoraproject.org | 
				
			||||||
 | 
					License: Python Software Foundation License | 
				
			||||||
 | 
					Description:  | 
				
			||||||
 | 
					        The ssl.match_hostname() function from Python 3.7 | 
				
			||||||
 | 
					        ================================================= | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        The Secure Sockets Layer is only actually *secure* | 
				
			||||||
 | 
					        if you check the hostname in the certificate returned | 
				
			||||||
 | 
					        by the server to which you are connecting, | 
				
			||||||
 | 
					        and verify that it matches to hostname | 
				
			||||||
 | 
					        that you are trying to reach. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        But the matching logic, defined in `RFC2818`_, | 
				
			||||||
 | 
					        can be a bit tricky to implement on your own. | 
				
			||||||
 | 
					        So the ``ssl`` package in the Standard Library of Python 3.2 | 
				
			||||||
 | 
					        and greater now includes a ``match_hostname()`` function | 
				
			||||||
 | 
					        for performing this check instead of requiring every application | 
				
			||||||
 | 
					        to implement the check separately. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        This backport brings ``match_hostname()`` to users | 
				
			||||||
 | 
					        of earlier versions of Python. | 
				
			||||||
 | 
					        Simply make this distribution a dependency of your package, | 
				
			||||||
 | 
					        and then use it like this:: | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            from backports.ssl_match_hostname import match_hostname, CertificateError | 
				
			||||||
 | 
					            [...] | 
				
			||||||
 | 
					            sslsock = ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_SSLv23, | 
				
			||||||
 | 
					                                      cert_reqs=ssl.CERT_REQUIRED, ca_certs=...) | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                match_hostname(sslsock.getpeercert(), hostname) | 
				
			||||||
 | 
					            except CertificateError, ce: | 
				
			||||||
 | 
					                ... | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Brandon Craig Rhodes is merely the packager of this distribution; | 
				
			||||||
 | 
					        the actual code inside comes from Python 3.7 with small changes for | 
				
			||||||
 | 
					        portability. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Requirements | 
				
			||||||
 | 
					        ------------ | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        * If you need to use this on Python versions earlier than 2.6 you will need to | 
				
			||||||
 | 
					          install the `ssl module`_.  From Python 2.6 upwards ``ssl`` is included in | 
				
			||||||
 | 
					          the Python Standard Library so you do not need to install it separately. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. _`ssl module`:: https://pypi.python.org/pypi/ssl | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        History | 
				
			||||||
 | 
					        ------- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        * This function was introduced in python-3.2 | 
				
			||||||
 | 
					        * It was updated for python-3.4a1 for a CVE  | 
				
			||||||
 | 
					          (backports-ssl_match_hostname-3.4.0.1) | 
				
			||||||
 | 
					        * It was updated from RFC2818 to RFC 6125 compliance in order to fix another | 
				
			||||||
 | 
					          security flaw for python-3.3.3 and python-3.4a5 | 
				
			||||||
 | 
					          (backports-ssl_match_hostname-3.4.0.2) | 
				
			||||||
 | 
					        * It was updated in python-3.5 to handle IPAddresses in ServerAltName fields | 
				
			||||||
 | 
					          (something that backports.ssl_match_hostname will do if you also install the | 
				
			||||||
 | 
					          ipaddress library from pypi). | 
				
			||||||
 | 
					        * It was updated in python-3.7 to handle IPAddresses without the ipaddress library and dropped | 
				
			||||||
 | 
					          support for partial wildcards | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. _`ipaddress module`:: https://pypi.python.org/pypi/ipaddress | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. _RFC2818: http://tools.ietf.org/html/rfc2818.html | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					Platform: UNKNOWN | 
				
			||||||
 | 
					Classifier: Development Status :: 5 - Production/Stable | 
				
			||||||
 | 
					Classifier: License :: OSI Approved :: Python Software Foundation License | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2.4 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2.5 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2.6 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2.7 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.0 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.1 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.2 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.3 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.4 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.5 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.6 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.7 | 
				
			||||||
 | 
					Classifier: Topic :: Security :: Cryptography | 
				
			||||||
@ -0,0 +1,8 @@ | 
				
			|||||||
 | 
					README.txt | 
				
			||||||
 | 
					setup.cfg | 
				
			||||||
 | 
					backports/__init__.py | 
				
			||||||
 | 
					backports.ssl_match_hostname.egg-info/PKG-INFO | 
				
			||||||
 | 
					backports.ssl_match_hostname.egg-info/SOURCES.txt | 
				
			||||||
 | 
					backports.ssl_match_hostname.egg-info/dependency_links.txt | 
				
			||||||
 | 
					backports.ssl_match_hostname.egg-info/top_level.txt | 
				
			||||||
 | 
					backports/ssl_match_hostname/__init__.py | 
				
			||||||
@ -0,0 +1 @@ | 
				
			|||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,8 @@ | 
				
			|||||||
 | 
					../backports/__init__.py | 
				
			||||||
 | 
					../backports/__init__.pyc | 
				
			||||||
 | 
					../backports/ssl_match_hostname/__init__.py | 
				
			||||||
 | 
					../backports/ssl_match_hostname/__init__.pyc | 
				
			||||||
 | 
					PKG-INFO | 
				
			||||||
 | 
					SOURCES.txt | 
				
			||||||
 | 
					dependency_links.txt | 
				
			||||||
 | 
					top_level.txt | 
				
			||||||
@ -0,0 +1 @@ | 
				
			|||||||
 | 
					backports | 
				
			||||||
@ -0,0 +1,3 @@ | 
				
			|||||||
 | 
					# This is a Python "namespace package" http://www.python.org/dev/peps/pep-0382/ | 
				
			||||||
 | 
					from pkgutil import extend_path | 
				
			||||||
 | 
					__path__ = extend_path(__path__, __name__) | 
				
			||||||
@ -0,0 +1,204 @@ | 
				
			|||||||
 | 
					"""The match_hostname() function from Python 3.7.0, essential when using SSL.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import socket as _socket | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try: | 
				
			||||||
 | 
					    # Divergence: Python-3.7+'s _ssl has this exception type but older Pythons do not | 
				
			||||||
 | 
					    from _ssl import SSLCertVerificationError | 
				
			||||||
 | 
					    CertificateError = SSLCertVerificationError | 
				
			||||||
 | 
					except: | 
				
			||||||
 | 
					    class CertificateError(ValueError): | 
				
			||||||
 | 
					        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__version__ = '3.7.0.1' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Divergence: Added to deal with ipaddess as bytes on python2 | 
				
			||||||
 | 
					def _to_text(obj): | 
				
			||||||
 | 
					    if isinstance(obj, str) and sys.version_info < (3,): | 
				
			||||||
 | 
					        obj = unicode(obj, encoding='ascii', errors='strict') | 
				
			||||||
 | 
					    elif sys.version_info >= (3,) and isinstance(obj, bytes): | 
				
			||||||
 | 
					        obj = str(obj, encoding='ascii', errors='strict') | 
				
			||||||
 | 
					    return obj | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _to_bytes(obj): | 
				
			||||||
 | 
					    if isinstance(obj, str) and sys.version_info >= (3,): | 
				
			||||||
 | 
					        obj = bytes(obj, encoding='ascii', errors='strict') | 
				
			||||||
 | 
					    elif sys.version_info < (3,) and isinstance(obj, unicode): | 
				
			||||||
 | 
					        obj = obj.encode('ascii', 'strict') | 
				
			||||||
 | 
					    return obj | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _dnsname_match(dn, hostname): | 
				
			||||||
 | 
					    """Matching according to RFC 6125, section 6.4.3 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Hostnames are compared lower case. | 
				
			||||||
 | 
					    - For IDNA, both dn and hostname must be encoded as IDN A-label (ACE). | 
				
			||||||
 | 
					    - Partial wildcards like 'www*.example.org', multiple wildcards, sole | 
				
			||||||
 | 
					      wildcard or wildcards in labels other then the left-most label are not | 
				
			||||||
 | 
					      supported and a CertificateError is raised. | 
				
			||||||
 | 
					    - A wildcard must match at least one character. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    if not dn: | 
				
			||||||
 | 
					        return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    wildcards = dn.count('*') | 
				
			||||||
 | 
					    # speed up common case w/o wildcards | 
				
			||||||
 | 
					    if not wildcards: | 
				
			||||||
 | 
					        return dn.lower() == hostname.lower() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if wildcards > 1: | 
				
			||||||
 | 
					        # Divergence .format() to percent formatting for Python < 2.6 | 
				
			||||||
 | 
					        raise CertificateError( | 
				
			||||||
 | 
					            "too many wildcards in certificate DNS name: %s" % repr(dn)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dn_leftmost, sep, dn_remainder = dn.partition('.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if '*' in dn_remainder: | 
				
			||||||
 | 
					        # Only match wildcard in leftmost segment. | 
				
			||||||
 | 
					        # Divergence .format() to percent formatting for Python < 2.6 | 
				
			||||||
 | 
					        raise CertificateError( | 
				
			||||||
 | 
					            "wildcard can only be present in the leftmost label: " | 
				
			||||||
 | 
					            "%s." % repr(dn)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if not sep: | 
				
			||||||
 | 
					        # no right side | 
				
			||||||
 | 
					        # Divergence .format() to percent formatting for Python < 2.6 | 
				
			||||||
 | 
					        raise CertificateError( | 
				
			||||||
 | 
					            "sole wildcard without additional labels are not support: " | 
				
			||||||
 | 
					            "%s." % repr(dn)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if dn_leftmost != '*': | 
				
			||||||
 | 
					        # no partial wildcard matching | 
				
			||||||
 | 
					        # Divergence .format() to percent formatting for Python < 2.6 | 
				
			||||||
 | 
					        raise CertificateError( | 
				
			||||||
 | 
					            "partial wildcards in leftmost label are not supported: " | 
				
			||||||
 | 
					            "%s." % repr(dn)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    hostname_leftmost, sep, hostname_remainder = hostname.partition('.') | 
				
			||||||
 | 
					    if not hostname_leftmost or not sep: | 
				
			||||||
 | 
					        # wildcard must match at least one char | 
				
			||||||
 | 
					        return False | 
				
			||||||
 | 
					    return dn_remainder.lower() == hostname_remainder.lower() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _inet_paton(ipname): | 
				
			||||||
 | 
					    """Try to convert an IP address to packed binary form | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Supports IPv4 addresses on all platforms and IPv6 on platforms with IPv6 | 
				
			||||||
 | 
					    support. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    # inet_aton() also accepts strings like '1' | 
				
			||||||
 | 
					    # Divergence: We make sure we have native string type for all python versions | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        b_ipname = _to_bytes(ipname) | 
				
			||||||
 | 
					    except UnicodeError: | 
				
			||||||
 | 
					        raise ValueError("%s must be an all-ascii string." % repr(ipname)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Set ipname in native string format | 
				
			||||||
 | 
					    if sys.version_info < (3,): | 
				
			||||||
 | 
					        n_ipname = b_ipname | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        n_ipname = ipname | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if n_ipname.count('.') == 3: | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            return _socket.inet_aton(n_ipname) | 
				
			||||||
 | 
					        # Divergence: OSError on late python3.  socket.error earlier. | 
				
			||||||
 | 
					        # Null bytes generate ValueError on python3(we want to raise | 
				
			||||||
 | 
					        # ValueError anyway), TypeError # earlier | 
				
			||||||
 | 
					        except (OSError, _socket.error, TypeError): | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        return _socket.inet_pton(_socket.AF_INET6, n_ipname) | 
				
			||||||
 | 
					    # Divergence: OSError on late python3.  socket.error earlier. | 
				
			||||||
 | 
					    # Null bytes generate ValueError on python3(we want to raise | 
				
			||||||
 | 
					    # ValueError anyway), TypeError # earlier | 
				
			||||||
 | 
					    except (OSError, _socket.error, TypeError): | 
				
			||||||
 | 
					        # Divergence .format() to percent formatting for Python < 2.6 | 
				
			||||||
 | 
					        raise ValueError("%s is neither an IPv4 nor an IP6 " | 
				
			||||||
 | 
					                         "address." % repr(ipname)) | 
				
			||||||
 | 
					    except AttributeError: | 
				
			||||||
 | 
					        # AF_INET6 not available | 
				
			||||||
 | 
					        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Divergence .format() to percent formatting for Python < 2.6 | 
				
			||||||
 | 
					    raise ValueError("%s is not an IPv4 address." % repr(ipname)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _ipaddress_match(ipname, host_ip): | 
				
			||||||
 | 
					    """Exact matching of IP addresses. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    RFC 6125 explicitly doesn't define an algorithm for this | 
				
			||||||
 | 
					    (section 1.7.2 - "Out of Scope"). | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    # OpenSSL may add a trailing newline to a subjectAltName's IP address | 
				
			||||||
 | 
					    ip = _inet_paton(ipname.rstrip()) | 
				
			||||||
 | 
					    return ip == host_ip | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def match_hostname(cert, hostname): | 
				
			||||||
 | 
					    """Verify that *cert* (in decoded format as returned by | 
				
			||||||
 | 
					    SSLSocket.getpeercert()) matches the *hostname*.  RFC 2818 and RFC 6125 | 
				
			||||||
 | 
					    rules are followed. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The function matches IP addresses rather than dNSNames if hostname is a | 
				
			||||||
 | 
					    valid ipaddress string. IPv4 addresses are supported on all platforms. | 
				
			||||||
 | 
					    IPv6 addresses are supported on platforms with IPv6 support (AF_INET6 | 
				
			||||||
 | 
					    and inet_pton). | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CertificateError is raised on failure. On success, the function | 
				
			||||||
 | 
					    returns nothing. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    if not cert: | 
				
			||||||
 | 
					        raise ValueError("empty or no certificate, match_hostname needs a " | 
				
			||||||
 | 
					                         "SSL socket or SSL context with either " | 
				
			||||||
 | 
					                         "CERT_OPTIONAL or CERT_REQUIRED") | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        # Divergence: Deal with hostname as bytes | 
				
			||||||
 | 
					        host_ip = _inet_paton(_to_text(hostname)) | 
				
			||||||
 | 
					    except ValueError: | 
				
			||||||
 | 
					        # Not an IP address (common case) | 
				
			||||||
 | 
					        host_ip = None | 
				
			||||||
 | 
					    except UnicodeError: | 
				
			||||||
 | 
					        # Divergence: Deal with hostname as byte strings. | 
				
			||||||
 | 
					        # IP addresses should be all ascii, so we consider it not | 
				
			||||||
 | 
					        # an IP address if this fails | 
				
			||||||
 | 
					        host_ip = None | 
				
			||||||
 | 
					    dnsnames = [] | 
				
			||||||
 | 
					    san = cert.get('subjectAltName', ()) | 
				
			||||||
 | 
					    for key, value in san: | 
				
			||||||
 | 
					        if key == 'DNS': | 
				
			||||||
 | 
					            if host_ip is None and _dnsname_match(value, hostname): | 
				
			||||||
 | 
					                return | 
				
			||||||
 | 
					            dnsnames.append(value) | 
				
			||||||
 | 
					        elif key == 'IP Address': | 
				
			||||||
 | 
					            if host_ip is not None and _ipaddress_match(value, host_ip): | 
				
			||||||
 | 
					                return | 
				
			||||||
 | 
					            dnsnames.append(value) | 
				
			||||||
 | 
					    if not dnsnames: | 
				
			||||||
 | 
					        # The subject is only checked when there is no dNSName entry | 
				
			||||||
 | 
					        # in subjectAltName | 
				
			||||||
 | 
					        for sub in cert.get('subject', ()): | 
				
			||||||
 | 
					            for key, value in sub: | 
				
			||||||
 | 
					                # XXX according to RFC 2818, the most specific Common Name | 
				
			||||||
 | 
					                # must be used. | 
				
			||||||
 | 
					                if key == 'commonName': | 
				
			||||||
 | 
					                    if _dnsname_match(value, hostname): | 
				
			||||||
 | 
					                        return | 
				
			||||||
 | 
					                    dnsnames.append(value) | 
				
			||||||
 | 
					    if len(dnsnames) > 1: | 
				
			||||||
 | 
					        raise CertificateError("hostname %r " | 
				
			||||||
 | 
					            "doesn't match either of %s" | 
				
			||||||
 | 
					            % (hostname, ', '.join(map(repr, dnsnames)))) | 
				
			||||||
 | 
					    elif len(dnsnames) == 1: | 
				
			||||||
 | 
					        raise CertificateError("hostname %r " | 
				
			||||||
 | 
					            "doesn't match %r" | 
				
			||||||
 | 
					            % (hostname, dnsnames[0])) | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        raise CertificateError("no appropriate commonName or " | 
				
			||||||
 | 
					            "subjectAltName fields were found") | 
				
			||||||
@ -0,0 +1,201 @@ | 
				
			|||||||
 | 
					#!/usr/local/bin/python | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import argparse | 
				
			||||||
 | 
					import code | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import threading | 
				
			||||||
 | 
					import time | 
				
			||||||
 | 
					import ssl | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import six | 
				
			||||||
 | 
					from six.moves.urllib.parse import urlparse | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import websocket | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try: | 
				
			||||||
 | 
					    import readline | 
				
			||||||
 | 
					except ImportError: | 
				
			||||||
 | 
					    pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_encoding(): | 
				
			||||||
 | 
					    encoding = getattr(sys.stdin, "encoding", "") | 
				
			||||||
 | 
					    if not encoding: | 
				
			||||||
 | 
					        return "utf-8" | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        return encoding.lower() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OPCODE_DATA = (websocket.ABNF.OPCODE_TEXT, websocket.ABNF.OPCODE_BINARY) | 
				
			||||||
 | 
					ENCODING = get_encoding() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VAction(argparse.Action): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __call__(self, parser, args, values, option_string=None): | 
				
			||||||
 | 
					        if values is None: | 
				
			||||||
 | 
					            values = "1" | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            values = int(values) | 
				
			||||||
 | 
					        except ValueError: | 
				
			||||||
 | 
					            values = values.count("v") + 1 | 
				
			||||||
 | 
					        setattr(args, self.dest, values) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def parse_args(): | 
				
			||||||
 | 
					    parser = argparse.ArgumentParser(description="WebSocket Simple Dump Tool") | 
				
			||||||
 | 
					    parser.add_argument("url", metavar="ws_url", | 
				
			||||||
 | 
					                        help="websocket url. ex. ws://echo.websocket.org/") | 
				
			||||||
 | 
					    parser.add_argument("-p", "--proxy", | 
				
			||||||
 | 
					                        help="proxy url. ex. http://127.0.0.1:8080") | 
				
			||||||
 | 
					    parser.add_argument("-v", "--verbose", default=0, nargs='?', action=VAction, | 
				
			||||||
 | 
					                        dest="verbose", | 
				
			||||||
 | 
					                        help="set verbose mode. If set to 1, show opcode. " | 
				
			||||||
 | 
					                        "If set to 2, enable to trace  websocket module") | 
				
			||||||
 | 
					    parser.add_argument("-n", "--nocert", action='store_true', | 
				
			||||||
 | 
					                        help="Ignore invalid SSL cert") | 
				
			||||||
 | 
					    parser.add_argument("-r", "--raw", action="store_true", | 
				
			||||||
 | 
					                        help="raw output") | 
				
			||||||
 | 
					    parser.add_argument("-s", "--subprotocols", nargs='*', | 
				
			||||||
 | 
					                        help="Set subprotocols") | 
				
			||||||
 | 
					    parser.add_argument("-o", "--origin", | 
				
			||||||
 | 
					                        help="Set origin") | 
				
			||||||
 | 
					    parser.add_argument("--eof-wait", default=0, type=int, | 
				
			||||||
 | 
					                        help="wait time(second) after 'EOF' received.") | 
				
			||||||
 | 
					    parser.add_argument("-t", "--text", | 
				
			||||||
 | 
					                        help="Send initial text") | 
				
			||||||
 | 
					    parser.add_argument("--timings", action="store_true", | 
				
			||||||
 | 
					                        help="Print timings in seconds") | 
				
			||||||
 | 
					    parser.add_argument("--headers", | 
				
			||||||
 | 
					                        help="Set custom headers. Use ',' as separator") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return parser.parse_args() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RawInput: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def raw_input(self, prompt): | 
				
			||||||
 | 
					        if six.PY3: | 
				
			||||||
 | 
					            line = input(prompt) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            line = raw_input(prompt) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ENCODING and ENCODING != "utf-8" and not isinstance(line, six.text_type): | 
				
			||||||
 | 
					            line = line.decode(ENCODING).encode("utf-8") | 
				
			||||||
 | 
					        elif isinstance(line, six.text_type): | 
				
			||||||
 | 
					            line = line.encode("utf-8") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return line | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InteractiveConsole(RawInput, code.InteractiveConsole): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def write(self, data): | 
				
			||||||
 | 
					        sys.stdout.write("\033[2K\033[E") | 
				
			||||||
 | 
					        # sys.stdout.write("\n") | 
				
			||||||
 | 
					        sys.stdout.write("\033[34m< " + data + "\033[39m") | 
				
			||||||
 | 
					        sys.stdout.write("\n> ") | 
				
			||||||
 | 
					        sys.stdout.flush() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def read(self): | 
				
			||||||
 | 
					        return self.raw_input("> ") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NonInteractive(RawInput): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def write(self, data): | 
				
			||||||
 | 
					        sys.stdout.write(data) | 
				
			||||||
 | 
					        sys.stdout.write("\n") | 
				
			||||||
 | 
					        sys.stdout.flush() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def read(self): | 
				
			||||||
 | 
					        return self.raw_input("") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def main(): | 
				
			||||||
 | 
					    start_time = time.time() | 
				
			||||||
 | 
					    args = parse_args() | 
				
			||||||
 | 
					    if args.verbose > 1: | 
				
			||||||
 | 
					        websocket.enableTrace(True) | 
				
			||||||
 | 
					    options = {} | 
				
			||||||
 | 
					    if args.proxy: | 
				
			||||||
 | 
					        p = urlparse(args.proxy) | 
				
			||||||
 | 
					        options["http_proxy_host"] = p.hostname | 
				
			||||||
 | 
					        options["http_proxy_port"] = p.port | 
				
			||||||
 | 
					    if args.origin: | 
				
			||||||
 | 
					        options["origin"] = args.origin | 
				
			||||||
 | 
					    if args.subprotocols: | 
				
			||||||
 | 
					        options["subprotocols"] = args.subprotocols | 
				
			||||||
 | 
					    opts = {} | 
				
			||||||
 | 
					    if args.nocert: | 
				
			||||||
 | 
					        opts = {"cert_reqs": ssl.CERT_NONE, "check_hostname": False} | 
				
			||||||
 | 
					    if args.headers: | 
				
			||||||
 | 
					        options['header'] = map(str.strip, args.headers.split(',')) | 
				
			||||||
 | 
					    ws = websocket.create_connection(args.url, sslopt=opts, **options) | 
				
			||||||
 | 
					    if args.raw: | 
				
			||||||
 | 
					        console = NonInteractive() | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        console = InteractiveConsole() | 
				
			||||||
 | 
					        print("Press Ctrl+C to quit") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def recv(): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            frame = ws.recv_frame() | 
				
			||||||
 | 
					        except websocket.WebSocketException: | 
				
			||||||
 | 
					            return websocket.ABNF.OPCODE_CLOSE, None | 
				
			||||||
 | 
					        if not frame: | 
				
			||||||
 | 
					            raise websocket.WebSocketException("Not a valid frame %s" % frame) | 
				
			||||||
 | 
					        elif frame.opcode in OPCODE_DATA: | 
				
			||||||
 | 
					            return frame.opcode, frame.data | 
				
			||||||
 | 
					        elif frame.opcode == websocket.ABNF.OPCODE_CLOSE: | 
				
			||||||
 | 
					            ws.send_close() | 
				
			||||||
 | 
					            return frame.opcode, None | 
				
			||||||
 | 
					        elif frame.opcode == websocket.ABNF.OPCODE_PING: | 
				
			||||||
 | 
					            ws.pong(frame.data) | 
				
			||||||
 | 
					            return frame.opcode, frame.data | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return frame.opcode, frame.data | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def recv_ws(): | 
				
			||||||
 | 
					        while True: | 
				
			||||||
 | 
					            opcode, data = recv() | 
				
			||||||
 | 
					            msg = None | 
				
			||||||
 | 
					            if six.PY3 and opcode == websocket.ABNF.OPCODE_TEXT and isinstance(data, bytes): | 
				
			||||||
 | 
					                data = str(data, "utf-8") | 
				
			||||||
 | 
					            if not args.verbose and opcode in OPCODE_DATA: | 
				
			||||||
 | 
					                msg = data | 
				
			||||||
 | 
					            elif args.verbose: | 
				
			||||||
 | 
					                msg = "%s: %s" % (websocket.ABNF.OPCODE_MAP.get(opcode), data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if msg is not None: | 
				
			||||||
 | 
					                if args.timings: | 
				
			||||||
 | 
					                    console.write(str(time.time() - start_time) + ": " + msg) | 
				
			||||||
 | 
					                else: | 
				
			||||||
 | 
					                    console.write(msg) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if opcode == websocket.ABNF.OPCODE_CLOSE: | 
				
			||||||
 | 
					                break | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    thread = threading.Thread(target=recv_ws) | 
				
			||||||
 | 
					    thread.daemon = True | 
				
			||||||
 | 
					    thread.start() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if args.text: | 
				
			||||||
 | 
					        ws.send(args.text) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while True: | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            message = console.read() | 
				
			||||||
 | 
					            ws.send(message) | 
				
			||||||
 | 
					        except KeyboardInterrupt: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					        except EOFError: | 
				
			||||||
 | 
					            time.sleep(args.eof_wait) | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__": | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        main() | 
				
			||||||
 | 
					    except Exception as e: | 
				
			||||||
 | 
					        print(e) | 
				
			||||||
@ -0,0 +1,187 @@ | 
				
			|||||||
 | 
					Metadata-Version: 1.1 | 
				
			||||||
 | 
					Name: json-rpc | 
				
			||||||
 | 
					Version: 1.12.1 | 
				
			||||||
 | 
					Summary: JSON-RPC transport implementation | 
				
			||||||
 | 
					Home-page: https://github.com/pavlov99/json-rpc | 
				
			||||||
 | 
					Author: Kirill Pavlov | 
				
			||||||
 | 
					Author-email: k@p99.io | 
				
			||||||
 | 
					License: MIT | 
				
			||||||
 | 
					Description: json-rpc | 
				
			||||||
 | 
					        ======== | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. image:: https://circleci.com/gh/pavlov99/json-rpc/tree/master.svg?style=svg | 
				
			||||||
 | 
					            :target: https://circleci.com/gh/pavlov99/json-rpc/tree/master | 
				
			||||||
 | 
					            :alt: Build Status | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. image:: https://codecov.io/gh/pavlov99/json-rpc/branch/master/graph/badge.svg | 
				
			||||||
 | 
					            :target: https://codecov.io/gh/pavlov99/json-rpc | 
				
			||||||
 | 
					            :alt: Coverage Status | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. image:: https://readthedocs.org/projects/json-rpc/badge/?version=latest | 
				
			||||||
 | 
					            :target: http://json-rpc.readthedocs.io/en/latest/?badge=latest | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. image:: https://img.shields.io/pypi/v/json-rpc.svg | 
				
			||||||
 | 
					            :target: https://pypi.org/project/json-rpc/ | 
				
			||||||
 | 
					            :alt: Latest PyPI version | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. image:: https://img.shields.io/pypi/pyversions/json-rpc.svg | 
				
			||||||
 | 
					            :target: https://pypi.org/project/json-rpc/ | 
				
			||||||
 | 
					            :alt: Supported Python versions | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. image:: https://badges.gitter.im/pavlov99/json-rpc.svg | 
				
			||||||
 | 
					            :target: https://gitter.im/pavlov99/json-rpc | 
				
			||||||
 | 
					            :alt: Gitter | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. image:: https://opencollective.com/json-rpc/tiers/backer/badge.svg?label=backer&color=brightgreen | 
				
			||||||
 | 
					            :target: https://opencollective.com/json-rpc | 
				
			||||||
 | 
					            :alt: Bakers | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. image:: https://opencollective.com/json-rpc/tiers/backer/badge.svg?label=sponsor&color=brightgreen | 
				
			||||||
 | 
					            :target: https://opencollective.com/json-rpc | 
				
			||||||
 | 
					            :alt: Sponsors | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        `JSON-RPC2.0 <http://www.jsonrpc.org/specification>`_ and `JSON-RPC1.0 <http://json-rpc.org/wiki/specification>`_ transport specification implementation. | 
				
			||||||
 | 
					        Supports Python 2.6+, Python 3.3+, PyPy. Has optional Django and Flask support. 200+ tests. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Features | 
				
			||||||
 | 
					        -------- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        This implementation does not have any transport functionality realization, only protocol. | 
				
			||||||
 | 
					        Any client or server implementation is easy based on current code, but requires transport libraries, such as requests, gevent or zmq, see `examples <https://github.com/pavlov99/json-rpc/tree/master/examples>`_. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        - Vanilla Python, no dependencies. | 
				
			||||||
 | 
					        - 200+ tests for multiple edge cases. | 
				
			||||||
 | 
					        - Optional backend support for Django, Flask. | 
				
			||||||
 | 
					        - json-rpc 1.1 and 2.0 support. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Install | 
				
			||||||
 | 
					        ------- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code-block:: python | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            pip install json-rpc | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Tests | 
				
			||||||
 | 
					        ----- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Quickstart | 
				
			||||||
 | 
					        ^^^^^^^^^^ | 
				
			||||||
 | 
					        This is an essential part of the library as there are a lot of edge cases in JSON-RPC standard. To manage a variety of supported python versions as well as optional backends json-rpc uses `tox`: | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code-block:: bash | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            tox | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. TIP:: | 
				
			||||||
 | 
					           During local development use your python version with tox runner. For example, if your are using Python 3.6 run `tox -e py36`. It is easier to develop functionality for specific version first and then expands it to all of the supported versions. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Continuous integration | 
				
			||||||
 | 
					        ^^^^^^^^^^^^^^^^^^^^^^ | 
				
			||||||
 | 
					        This project uses `CircleCI <https://circleci.com/>`_ for continuous integration. All of the python supported versions are managed via `tox.ini` and `.circleci/config.yml` files. Master branch test status is displayed on the badge in the beginning of this document. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Test matrix | 
				
			||||||
 | 
					        ^^^^^^^^^^^ | 
				
			||||||
 | 
					        json-rpc supports multiple python versions: 2.6+, 3.3+, pypy. This introduces difficulties with testing libraries and optional dependencies management. For example, python before version 3.3 does not support `mock` and there is a limited support for `unittest2`. Every dependency translates into *if-then* blocks in the source code and adds complexity to it. Hence, while cross-python support is a core feature of this library, cross-Django or cross-Flask support is limited. In general, json-rpc uses latest stable release which supports current python version. For example, python 2.6 is compatible with Django 1.6 and not compatible with any future versions. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Below is a testing matrix: | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        +--------+-------+-----------+--------+--------+ | 
				
			||||||
 | 
					        | Python | mock  | unittest  | Django | Flask  | | 
				
			||||||
 | 
					        +========+=======+===========+========+========+ | 
				
			||||||
 | 
					        | 2.6    | 2.0.0 | unittest2 | 1.6    | 0.12.2 | | 
				
			||||||
 | 
					        +--------+-------+-----------+--------+--------+ | 
				
			||||||
 | 
					        | 2.7    | 2.0.0 |           | 1.11   | 0.12.2 | | 
				
			||||||
 | 
					        +--------+-------+-----------+--------+--------+ | 
				
			||||||
 | 
					        | 3.3    |       |           | 1.11   | 0.12.2 | | 
				
			||||||
 | 
					        +--------+-------+-----------+--------+--------+ | 
				
			||||||
 | 
					        | 3.4    |       |           | 1.11   | 0.12.2 | | 
				
			||||||
 | 
					        +--------+-------+-----------+--------+--------+ | 
				
			||||||
 | 
					        | 3.5    |       |           | 1.11   | 0.12.2 | | 
				
			||||||
 | 
					        +--------+-------+-----------+--------+--------+ | 
				
			||||||
 | 
					        | 3.6    |       |           | 1.11   | 0.12.2 | | 
				
			||||||
 | 
					        +--------+-------+-----------+--------+--------+ | 
				
			||||||
 | 
					        | pypy   | 2.0.0 |           | 1.11   | 0.12.2 | | 
				
			||||||
 | 
					        +--------+-------+-----------+--------+--------+ | 
				
			||||||
 | 
					        | pypy3  |       |           | 1.11   | 0.12.2 | | 
				
			||||||
 | 
					        +--------+-------+-----------+--------+--------+ | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Quickstart | 
				
			||||||
 | 
					        ---------- | 
				
			||||||
 | 
					        Server (uses `Werkzeug <http://werkzeug.pocoo.org/>`_) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code-block:: python | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            from werkzeug.wrappers import Request, Response | 
				
			||||||
 | 
					            from werkzeug.serving import run_simple | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            from jsonrpc import JSONRPCResponseManager, dispatcher | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            @dispatcher.add_method | 
				
			||||||
 | 
					            def foobar(**kwargs): | 
				
			||||||
 | 
					                return kwargs["foo"] + kwargs["bar"] | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            @Request.application | 
				
			||||||
 | 
					            def application(request): | 
				
			||||||
 | 
					                # Dispatcher is dictionary {<method_name>: callable} | 
				
			||||||
 | 
					                dispatcher["echo"] = lambda s: s | 
				
			||||||
 | 
					                dispatcher["add"] = lambda a, b: a + b | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					                response = JSONRPCResponseManager.handle( | 
				
			||||||
 | 
					                    request.data, dispatcher) | 
				
			||||||
 | 
					                return Response(response.json, mimetype='application/json') | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            if __name__ == '__main__': | 
				
			||||||
 | 
					                run_simple('localhost', 4000, application) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Client (uses `requests <http://www.python-requests.org/en/latest/>`_) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code-block:: python | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            import requests | 
				
			||||||
 | 
					            import json | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            def main(): | 
				
			||||||
 | 
					                url = "http://localhost:4000/jsonrpc" | 
				
			||||||
 | 
					                headers = {'content-type': 'application/json'} | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					                # Example echo method | 
				
			||||||
 | 
					                payload = { | 
				
			||||||
 | 
					                    "method": "echo", | 
				
			||||||
 | 
					                    "params": ["echome!"], | 
				
			||||||
 | 
					                    "jsonrpc": "2.0", | 
				
			||||||
 | 
					                    "id": 0, | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					                response = requests.post( | 
				
			||||||
 | 
					                    url, data=json.dumps(payload), headers=headers).json() | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					                assert response["result"] == "echome!" | 
				
			||||||
 | 
					                assert response["jsonrpc"] | 
				
			||||||
 | 
					                assert response["id"] == 0 | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            if __name__ == "__main__": | 
				
			||||||
 | 
					                main() | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Competitors | 
				
			||||||
 | 
					        ----------- | 
				
			||||||
 | 
					        There are `several libraries <http://en.wikipedia.org/wiki/JSON-RPC#Implementations>`_ implementing JSON-RPC protocol. List below represents python libraries, none of the supports python3. tinyrpc looks better than others. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					Platform: UNKNOWN | 
				
			||||||
 | 
					Classifier: Development Status :: 5 - Production/Stable | 
				
			||||||
 | 
					Classifier: Environment :: Console | 
				
			||||||
 | 
					Classifier: License :: OSI Approved :: MIT License | 
				
			||||||
 | 
					Classifier: Natural Language :: English | 
				
			||||||
 | 
					Classifier: Operating System :: OS Independent | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2.6 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2.7 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.3 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.4 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.5 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.6 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.7 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: Implementation :: PyPy | 
				
			||||||
 | 
					Classifier: Topic :: Software Development :: Libraries :: Python Modules | 
				
			||||||
@ -0,0 +1,42 @@ | 
				
			|||||||
 | 
					LICENSE.txt | 
				
			||||||
 | 
					MANIFEST.in | 
				
			||||||
 | 
					README.rst | 
				
			||||||
 | 
					get-pip.py | 
				
			||||||
 | 
					setup.cfg | 
				
			||||||
 | 
					setup.py | 
				
			||||||
 | 
					json_rpc.egg-info/PKG-INFO | 
				
			||||||
 | 
					json_rpc.egg-info/SOURCES.txt | 
				
			||||||
 | 
					json_rpc.egg-info/dependency_links.txt | 
				
			||||||
 | 
					json_rpc.egg-info/top_level.txt | 
				
			||||||
 | 
					jsonrpc/__init__.py | 
				
			||||||
 | 
					jsonrpc/base.py | 
				
			||||||
 | 
					jsonrpc/dispatcher.py | 
				
			||||||
 | 
					jsonrpc/exceptions.py | 
				
			||||||
 | 
					jsonrpc/jsonrpc.py | 
				
			||||||
 | 
					jsonrpc/jsonrpc1.py | 
				
			||||||
 | 
					jsonrpc/jsonrpc2.py | 
				
			||||||
 | 
					jsonrpc/manager.py | 
				
			||||||
 | 
					jsonrpc/six.py | 
				
			||||||
 | 
					jsonrpc/utils.py | 
				
			||||||
 | 
					jsonrpc/backend/__init__.py | 
				
			||||||
 | 
					jsonrpc/backend/django.py | 
				
			||||||
 | 
					jsonrpc/backend/flask.py | 
				
			||||||
 | 
					jsonrpc/tests/__init__.py | 
				
			||||||
 | 
					jsonrpc/tests/py35_utils.py | 
				
			||||||
 | 
					jsonrpc/tests/test_base.py | 
				
			||||||
 | 
					jsonrpc/tests/test_bug29.py | 
				
			||||||
 | 
					jsonrpc/tests/test_dispatcher.py | 
				
			||||||
 | 
					jsonrpc/tests/test_examples20.py | 
				
			||||||
 | 
					jsonrpc/tests/test_jsonrpc.py | 
				
			||||||
 | 
					jsonrpc/tests/test_jsonrpc1.py | 
				
			||||||
 | 
					jsonrpc/tests/test_jsonrpc2.py | 
				
			||||||
 | 
					jsonrpc/tests/test_jsonrpc_errors.py | 
				
			||||||
 | 
					jsonrpc/tests/test_manager.py | 
				
			||||||
 | 
					jsonrpc/tests/test_pep3107.py | 
				
			||||||
 | 
					jsonrpc/tests/test_utils.py | 
				
			||||||
 | 
					jsonrpc/tests/test_backend_django/__init__.py | 
				
			||||||
 | 
					jsonrpc/tests/test_backend_django/settings.py | 
				
			||||||
 | 
					jsonrpc/tests/test_backend_django/tests.py | 
				
			||||||
 | 
					jsonrpc/tests/test_backend_django/urls.py | 
				
			||||||
 | 
					jsonrpc/tests/test_backend_flask/__init__.py | 
				
			||||||
 | 
					jsonrpc/tests/test_backend_flask/tests.py | 
				
			||||||
@ -0,0 +1,68 @@ | 
				
			|||||||
 | 
					../jsonrpc/__init__.py | 
				
			||||||
 | 
					../jsonrpc/__init__.pyc | 
				
			||||||
 | 
					../jsonrpc/backend/__init__.py | 
				
			||||||
 | 
					../jsonrpc/backend/__init__.pyc | 
				
			||||||
 | 
					../jsonrpc/backend/django.py | 
				
			||||||
 | 
					../jsonrpc/backend/django.pyc | 
				
			||||||
 | 
					../jsonrpc/backend/flask.py | 
				
			||||||
 | 
					../jsonrpc/backend/flask.pyc | 
				
			||||||
 | 
					../jsonrpc/base.py | 
				
			||||||
 | 
					../jsonrpc/base.pyc | 
				
			||||||
 | 
					../jsonrpc/dispatcher.py | 
				
			||||||
 | 
					../jsonrpc/dispatcher.pyc | 
				
			||||||
 | 
					../jsonrpc/exceptions.py | 
				
			||||||
 | 
					../jsonrpc/exceptions.pyc | 
				
			||||||
 | 
					../jsonrpc/jsonrpc.py | 
				
			||||||
 | 
					../jsonrpc/jsonrpc.pyc | 
				
			||||||
 | 
					../jsonrpc/jsonrpc1.py | 
				
			||||||
 | 
					../jsonrpc/jsonrpc1.pyc | 
				
			||||||
 | 
					../jsonrpc/jsonrpc2.py | 
				
			||||||
 | 
					../jsonrpc/jsonrpc2.pyc | 
				
			||||||
 | 
					../jsonrpc/manager.py | 
				
			||||||
 | 
					../jsonrpc/manager.pyc | 
				
			||||||
 | 
					../jsonrpc/six.py | 
				
			||||||
 | 
					../jsonrpc/six.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/__init__.py | 
				
			||||||
 | 
					../jsonrpc/tests/__init__.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/py35_utils.py | 
				
			||||||
 | 
					../jsonrpc/tests/py35_utils.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/test_backend_django/__init__.py | 
				
			||||||
 | 
					../jsonrpc/tests/test_backend_django/__init__.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/test_backend_django/settings.py | 
				
			||||||
 | 
					../jsonrpc/tests/test_backend_django/settings.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/test_backend_django/tests.py | 
				
			||||||
 | 
					../jsonrpc/tests/test_backend_django/tests.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/test_backend_django/urls.py | 
				
			||||||
 | 
					../jsonrpc/tests/test_backend_django/urls.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/test_backend_flask/__init__.py | 
				
			||||||
 | 
					../jsonrpc/tests/test_backend_flask/__init__.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/test_backend_flask/tests.py | 
				
			||||||
 | 
					../jsonrpc/tests/test_backend_flask/tests.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/test_base.py | 
				
			||||||
 | 
					../jsonrpc/tests/test_base.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/test_bug29.py | 
				
			||||||
 | 
					../jsonrpc/tests/test_bug29.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/test_dispatcher.py | 
				
			||||||
 | 
					../jsonrpc/tests/test_dispatcher.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/test_examples20.py | 
				
			||||||
 | 
					../jsonrpc/tests/test_examples20.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/test_jsonrpc.py | 
				
			||||||
 | 
					../jsonrpc/tests/test_jsonrpc.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/test_jsonrpc1.py | 
				
			||||||
 | 
					../jsonrpc/tests/test_jsonrpc1.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/test_jsonrpc2.py | 
				
			||||||
 | 
					../jsonrpc/tests/test_jsonrpc2.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/test_jsonrpc_errors.py | 
				
			||||||
 | 
					../jsonrpc/tests/test_jsonrpc_errors.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/test_manager.py | 
				
			||||||
 | 
					../jsonrpc/tests/test_manager.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/test_pep3107.py | 
				
			||||||
 | 
					../jsonrpc/tests/test_pep3107.pyc | 
				
			||||||
 | 
					../jsonrpc/tests/test_utils.py | 
				
			||||||
 | 
					../jsonrpc/tests/test_utils.pyc | 
				
			||||||
 | 
					../jsonrpc/utils.py | 
				
			||||||
 | 
					../jsonrpc/utils.pyc | 
				
			||||||
 | 
					PKG-INFO | 
				
			||||||
 | 
					SOURCES.txt | 
				
			||||||
 | 
					dependency_links.txt | 
				
			||||||
 | 
					top_level.txt | 
				
			||||||
@ -0,0 +1 @@ | 
				
			|||||||
 | 
					jsonrpc | 
				
			||||||
@ -0,0 +1,11 @@ | 
				
			|||||||
 | 
					from .manager import JSONRPCResponseManager | 
				
			||||||
 | 
					from .dispatcher import Dispatcher | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__version = (1, 12, 1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__version__ = version = '.'.join(map(str, __version)) | 
				
			||||||
 | 
					__project__ = PROJECT = __name__ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dispatcher = Dispatcher() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# lint_ignore=W0611,W0401 | 
				
			||||||
@ -0,0 +1,89 @@ | 
				
			|||||||
 | 
					from __future__ import absolute_import | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.views.decorators.csrf import csrf_exempt | 
				
			||||||
 | 
					from django.conf.urls import url | 
				
			||||||
 | 
					from django.conf import settings | 
				
			||||||
 | 
					from django.http import HttpResponse, HttpResponseNotAllowed | 
				
			||||||
 | 
					import copy | 
				
			||||||
 | 
					import json | 
				
			||||||
 | 
					import logging | 
				
			||||||
 | 
					import time | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ..exceptions import JSONRPCInvalidRequestException | 
				
			||||||
 | 
					from ..jsonrpc import JSONRPCRequest | 
				
			||||||
 | 
					from ..manager import JSONRPCResponseManager | 
				
			||||||
 | 
					from ..utils import DatetimeDecimalEncoder | 
				
			||||||
 | 
					from ..dispatcher import Dispatcher | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					logger = logging.getLogger(__name__) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def response_serialize(obj): | 
				
			||||||
 | 
					    """ Serializes response's data object to JSON. """ | 
				
			||||||
 | 
					    return json.dumps(obj, cls=DatetimeDecimalEncoder) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRPCAPI(object): | 
				
			||||||
 | 
					    def __init__(self, dispatcher=None): | 
				
			||||||
 | 
					        self.dispatcher = dispatcher if dispatcher is not None \ | 
				
			||||||
 | 
					            else Dispatcher() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def urls(self): | 
				
			||||||
 | 
					        urls = [ | 
				
			||||||
 | 
					            url(r'^$', self.jsonrpc, name='endpoint'), | 
				
			||||||
 | 
					        ] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if getattr(settings, 'JSONRPC_MAP_VIEW_ENABLED', settings.DEBUG): | 
				
			||||||
 | 
					            urls.append( | 
				
			||||||
 | 
					                url(r'^map$', self.jsonrpc_map, name='map') | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return urls | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @csrf_exempt | 
				
			||||||
 | 
					    def jsonrpc(self, request): | 
				
			||||||
 | 
					        """ JSON-RPC 2.0 handler.""" | 
				
			||||||
 | 
					        if request.method != "POST": | 
				
			||||||
 | 
					            return HttpResponseNotAllowed(["POST"]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        request_str = request.body.decode('utf8') | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            jsonrpc_request = JSONRPCRequest.from_json(request_str) | 
				
			||||||
 | 
					        except (TypeError, ValueError, JSONRPCInvalidRequestException): | 
				
			||||||
 | 
					            response = JSONRPCResponseManager.handle( | 
				
			||||||
 | 
					                request_str, self.dispatcher) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            jsonrpc_request.params = jsonrpc_request.params or {} | 
				
			||||||
 | 
					            jsonrpc_request_params = copy.copy(jsonrpc_request.params) | 
				
			||||||
 | 
					            if isinstance(jsonrpc_request.params, dict): | 
				
			||||||
 | 
					                jsonrpc_request.params.update(request=request) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            t1 = time.time() | 
				
			||||||
 | 
					            response = JSONRPCResponseManager.handle_request( | 
				
			||||||
 | 
					                jsonrpc_request, self.dispatcher) | 
				
			||||||
 | 
					            t2 = time.time() | 
				
			||||||
 | 
					            logger.info('{0}({1}) {2:.2f} sec'.format( | 
				
			||||||
 | 
					                jsonrpc_request.method, jsonrpc_request_params, t2 - t1)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if response: | 
				
			||||||
 | 
					            response.serialize = response_serialize | 
				
			||||||
 | 
					            response = response.json | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return HttpResponse(response, content_type="application/json") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def jsonrpc_map(self, request): | 
				
			||||||
 | 
					        """ Map of json-rpc available calls. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :return str: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        result = "<h1>JSON-RPC map</h1><pre>{0}</pre>".format("\n\n".join([ | 
				
			||||||
 | 
					            "{0}: {1}".format(fname, f.__doc__) | 
				
			||||||
 | 
					            for fname, f in self.dispatcher.items() | 
				
			||||||
 | 
					        ])) | 
				
			||||||
 | 
					        return HttpResponse(result) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					api = JSONRPCAPI() | 
				
			||||||
@ -0,0 +1,85 @@ | 
				
			|||||||
 | 
					from __future__ import absolute_import | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import copy | 
				
			||||||
 | 
					import json | 
				
			||||||
 | 
					import logging | 
				
			||||||
 | 
					import time | 
				
			||||||
 | 
					from uuid import uuid4 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from flask import Blueprint, request, Response | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ..exceptions import JSONRPCInvalidRequestException | 
				
			||||||
 | 
					from ..jsonrpc import JSONRPCRequest | 
				
			||||||
 | 
					from ..manager import JSONRPCResponseManager | 
				
			||||||
 | 
					from ..utils import DatetimeDecimalEncoder | 
				
			||||||
 | 
					from ..dispatcher import Dispatcher | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					logger = logging.getLogger(__name__) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRPCAPI(object): | 
				
			||||||
 | 
					    def __init__(self, dispatcher=None, check_content_type=True): | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param dispatcher: methods dispatcher | 
				
			||||||
 | 
					        :param check_content_type: if True - content-type must be | 
				
			||||||
 | 
					            "application/json" | 
				
			||||||
 | 
					        :return: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        self.dispatcher = dispatcher if dispatcher is not None \ | 
				
			||||||
 | 
					            else Dispatcher() | 
				
			||||||
 | 
					        self.check_content_type = check_content_type | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def as_blueprint(self, name=None): | 
				
			||||||
 | 
					        blueprint = Blueprint(name if name else str(uuid4()), __name__) | 
				
			||||||
 | 
					        blueprint.add_url_rule( | 
				
			||||||
 | 
					            '/', view_func=self.jsonrpc, methods=['POST']) | 
				
			||||||
 | 
					        blueprint.add_url_rule( | 
				
			||||||
 | 
					            '/map', view_func=self.jsonrpc_map, methods=['GET']) | 
				
			||||||
 | 
					        return blueprint | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def as_view(self): | 
				
			||||||
 | 
					        return self.jsonrpc | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def jsonrpc(self): | 
				
			||||||
 | 
					        request_str = self._get_request_str() | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            jsonrpc_request = JSONRPCRequest.from_json(request_str) | 
				
			||||||
 | 
					        except (TypeError, ValueError, JSONRPCInvalidRequestException): | 
				
			||||||
 | 
					            response = JSONRPCResponseManager.handle( | 
				
			||||||
 | 
					                request_str, self.dispatcher) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            response = JSONRPCResponseManager.handle_request( | 
				
			||||||
 | 
					                jsonrpc_request, self.dispatcher) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if response: | 
				
			||||||
 | 
					            response.serialize = self._serialize | 
				
			||||||
 | 
					            response = response.json | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Response(response, content_type="application/json") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def jsonrpc_map(self): | 
				
			||||||
 | 
					        """ Map of json-rpc available calls. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :return str: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        result = "<h1>JSON-RPC map</h1><pre>{0}</pre>".format("\n\n".join([ | 
				
			||||||
 | 
					            "{0}: {1}".format(fname, f.__doc__) | 
				
			||||||
 | 
					            for fname, f in self.dispatcher.items() | 
				
			||||||
 | 
					        ])) | 
				
			||||||
 | 
					        return Response(result) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_request_str(self): | 
				
			||||||
 | 
					        if self.check_content_type or request.data: | 
				
			||||||
 | 
					            return request.data | 
				
			||||||
 | 
					        return list(request.form.keys())[0] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod | 
				
			||||||
 | 
					    def _serialize(s): | 
				
			||||||
 | 
					        return json.dumps(s, cls=DatetimeDecimalEncoder) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					api = JSONRPCAPI() | 
				
			||||||
@ -0,0 +1,87 @@ | 
				
			|||||||
 | 
					from .utils import JSONSerializable | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRPCBaseRequest(JSONSerializable): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Base class for JSON-RPC 1.0 and JSON-RPC 2.0 requests.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, method=None, params=None, _id=None, | 
				
			||||||
 | 
					                 is_notification=None): | 
				
			||||||
 | 
					        self.data = dict() | 
				
			||||||
 | 
					        self.method = method | 
				
			||||||
 | 
					        self.params = params | 
				
			||||||
 | 
					        self._id = _id | 
				
			||||||
 | 
					        self.is_notification = is_notification | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def data(self): | 
				
			||||||
 | 
					        return self._data | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @data.setter | 
				
			||||||
 | 
					    def data(self, value): | 
				
			||||||
 | 
					        if not isinstance(value, dict): | 
				
			||||||
 | 
					            raise ValueError("data should be dict") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._data = value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def args(self): | 
				
			||||||
 | 
					        """ Method position arguments. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :return tuple args: method position arguments. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        return tuple(self.params) if isinstance(self.params, list) else () | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def kwargs(self): | 
				
			||||||
 | 
					        """ Method named arguments. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :return dict kwargs: method named arguments. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        return self.params if isinstance(self.params, dict) else {} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def json(self): | 
				
			||||||
 | 
					        return self.serialize(self.data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRPCBaseResponse(JSONSerializable): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Base class for JSON-RPC 1.0 and JSON-RPC 2.0 responses.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, **kwargs): | 
				
			||||||
 | 
					        self.data = dict() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            self.result = kwargs['result'] | 
				
			||||||
 | 
					        except KeyError: | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            self.error = kwargs['error'] | 
				
			||||||
 | 
					        except KeyError: | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._id = kwargs.get('_id') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if 'result' not in kwargs and 'error' not in kwargs: | 
				
			||||||
 | 
					            raise ValueError("Either result or error should be used") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.request = None  # type: JSONRPCBaseRequest | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def data(self): | 
				
			||||||
 | 
					        return self._data | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @data.setter | 
				
			||||||
 | 
					    def data(self, value): | 
				
			||||||
 | 
					        if not isinstance(value, dict): | 
				
			||||||
 | 
					            raise ValueError("data should be dict") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._data = value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def json(self): | 
				
			||||||
 | 
					        return self.serialize(self.data) | 
				
			||||||
@ -0,0 +1,132 @@ | 
				
			|||||||
 | 
					""" Dispatcher is used to add methods (functions) to the server. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For usage examples see :meth:`Dispatcher.add_method` | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					import functools | 
				
			||||||
 | 
					import collections | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Dispatcher(collections.MutableMapping): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Dictionary like object which maps method_name to method.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, prototype=None): | 
				
			||||||
 | 
					        """ Build method dispatcher. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Parameters | 
				
			||||||
 | 
					        ---------- | 
				
			||||||
 | 
					        prototype : object or dict, optional | 
				
			||||||
 | 
					            Initial method mapping. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Examples | 
				
			||||||
 | 
					        -------- | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Init object with method dictionary. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        >>> Dispatcher({"sum": lambda a, b: a + b}) | 
				
			||||||
 | 
					        None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        self.method_map = dict() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if prototype is not None: | 
				
			||||||
 | 
					            self.build_method_map(prototype) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __getitem__(self, key): | 
				
			||||||
 | 
					        return self.method_map[key] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __setitem__(self, key, value): | 
				
			||||||
 | 
					        self.method_map[key] = value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __delitem__(self, key): | 
				
			||||||
 | 
					        del self.method_map[key] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __len__(self): | 
				
			||||||
 | 
					        return len(self.method_map) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __iter__(self): | 
				
			||||||
 | 
					        return iter(self.method_map) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self): | 
				
			||||||
 | 
					        return repr(self.method_map) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def add_class(self, cls): | 
				
			||||||
 | 
					        prefix = cls.__name__.lower() + '.' | 
				
			||||||
 | 
					        self.build_method_map(cls(), prefix) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def add_object(self, obj): | 
				
			||||||
 | 
					        prefix = obj.__class__.__name__.lower() + '.' | 
				
			||||||
 | 
					        self.build_method_map(obj, prefix) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def add_dict(self, dict, prefix=''): | 
				
			||||||
 | 
					        if prefix: | 
				
			||||||
 | 
					            prefix += '.' | 
				
			||||||
 | 
					        self.build_method_map(dict, prefix) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def add_method(self, f=None, name=None): | 
				
			||||||
 | 
					        """ Add a method to the dispatcher. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Parameters | 
				
			||||||
 | 
					        ---------- | 
				
			||||||
 | 
					        f : callable | 
				
			||||||
 | 
					            Callable to be added. | 
				
			||||||
 | 
					        name : str, optional | 
				
			||||||
 | 
					            Name to register (the default is function **f** name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Notes | 
				
			||||||
 | 
					        ----- | 
				
			||||||
 | 
					        When used as a decorator keeps callable object unmodified. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Examples | 
				
			||||||
 | 
					        -------- | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Use as method | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        >>> d = Dispatcher() | 
				
			||||||
 | 
					        >>> d.add_method(lambda a, b: a + b, name="sum") | 
				
			||||||
 | 
					        <function __main__.<lambda>> | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Or use as decorator | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        >>> d = Dispatcher() | 
				
			||||||
 | 
					        >>> @d.add_method | 
				
			||||||
 | 
					            def mymethod(*args, **kwargs): | 
				
			||||||
 | 
					                print(args, kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Or use as a decorator with a different function name | 
				
			||||||
 | 
					        >>> d = Dispatcher() | 
				
			||||||
 | 
					        >>> @d.add_method(name="my.method") | 
				
			||||||
 | 
					            def mymethod(*args, **kwargs): | 
				
			||||||
 | 
					                print(args, kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if name and not f: | 
				
			||||||
 | 
					            return functools.partial(self.add_method, name=name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.method_map[name or f.__name__] = f | 
				
			||||||
 | 
					        return f | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def build_method_map(self, prototype, prefix=''): | 
				
			||||||
 | 
					        """ Add prototype methods to the dispatcher. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Parameters | 
				
			||||||
 | 
					        ---------- | 
				
			||||||
 | 
					        prototype : object or dict | 
				
			||||||
 | 
					            Initial method mapping. | 
				
			||||||
 | 
					            If given prototype is a dictionary then all callable objects will | 
				
			||||||
 | 
					            be added to dispatcher. | 
				
			||||||
 | 
					            If given prototype is an object then all public methods will | 
				
			||||||
 | 
					            be used. | 
				
			||||||
 | 
					        prefix: string, optional | 
				
			||||||
 | 
					            Prefix of methods | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if not isinstance(prototype, dict): | 
				
			||||||
 | 
					            prototype = dict((method, getattr(prototype, method)) | 
				
			||||||
 | 
					                             for method in dir(prototype) | 
				
			||||||
 | 
					                             if not method.startswith('_')) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for attr, method in prototype.items(): | 
				
			||||||
 | 
					            if callable(method): | 
				
			||||||
 | 
					                self[prefix + attr] = method | 
				
			||||||
@ -0,0 +1,185 @@ | 
				
			|||||||
 | 
					""" JSON-RPC Exceptions.""" | 
				
			||||||
 | 
					from . import six | 
				
			||||||
 | 
					import json | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRPCError(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Error for JSON-RPC communication. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    When a rpc call encounters an error, the Response Object MUST contain the | 
				
			||||||
 | 
					    error member with a value that is a Object with the following members: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Parameters | 
				
			||||||
 | 
					    ---------- | 
				
			||||||
 | 
					    code: int | 
				
			||||||
 | 
					        A Number that indicates the error type that occurred. | 
				
			||||||
 | 
					        This MUST be an integer. | 
				
			||||||
 | 
					        The error codes from and including -32768 to -32000 are reserved for | 
				
			||||||
 | 
					        pre-defined errors. Any code within this range, but not defined | 
				
			||||||
 | 
					        explicitly below is reserved for future use. The error codes are nearly | 
				
			||||||
 | 
					        the same as those suggested for XML-RPC at the following | 
				
			||||||
 | 
					        url: http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    message: str | 
				
			||||||
 | 
					        A String providing a short description of the error. | 
				
			||||||
 | 
					        The message SHOULD be limited to a concise single sentence. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    data: int or str or dict or list, optional | 
				
			||||||
 | 
					        A Primitive or Structured value that contains additional | 
				
			||||||
 | 
					        information about the error. | 
				
			||||||
 | 
					        This may be omitted. | 
				
			||||||
 | 
					        The value of this member is defined by the Server (e.g. detailed error | 
				
			||||||
 | 
					        information, nested errors etc.). | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    serialize = staticmethod(json.dumps) | 
				
			||||||
 | 
					    deserialize = staticmethod(json.loads) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, code=None, message=None, data=None): | 
				
			||||||
 | 
					        self._data = dict() | 
				
			||||||
 | 
					        self.code = getattr(self.__class__, "CODE", code) | 
				
			||||||
 | 
					        self.message = getattr(self.__class__, "MESSAGE", message) | 
				
			||||||
 | 
					        self.data = data | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __get_code(self): | 
				
			||||||
 | 
					        return self._data["code"] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __set_code(self, value): | 
				
			||||||
 | 
					        if not isinstance(value, six.integer_types): | 
				
			||||||
 | 
					            raise ValueError("Error code should be integer") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._data["code"] = value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    code = property(__get_code, __set_code) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __get_message(self): | 
				
			||||||
 | 
					        return self._data["message"] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __set_message(self, value): | 
				
			||||||
 | 
					        if not isinstance(value, six.string_types): | 
				
			||||||
 | 
					            raise ValueError("Error message should be string") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._data["message"] = value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    message = property(__get_message, __set_message) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __get_data(self): | 
				
			||||||
 | 
					        return self._data.get("data") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __set_data(self, value): | 
				
			||||||
 | 
					        if value is not None: | 
				
			||||||
 | 
					            self._data["data"] = value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    data = property(__get_data, __set_data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod | 
				
			||||||
 | 
					    def from_json(cls, json_str): | 
				
			||||||
 | 
					        data = cls.deserialize(json_str) | 
				
			||||||
 | 
					        return cls( | 
				
			||||||
 | 
					            code=data["code"], message=data["message"], data=data.get("data")) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def json(self): | 
				
			||||||
 | 
					        return self.serialize(self._data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRPCParseError(JSONRPCError): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Parse Error. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Invalid JSON was received by the server. | 
				
			||||||
 | 
					    An error occurred on the server while parsing the JSON text. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CODE = -32700 | 
				
			||||||
 | 
					    MESSAGE = "Parse error" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRPCInvalidRequest(JSONRPCError): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Invalid Request. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The JSON sent is not a valid Request object. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CODE = -32600 | 
				
			||||||
 | 
					    MESSAGE = "Invalid Request" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRPCMethodNotFound(JSONRPCError): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Method not found. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The method does not exist / is not available. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CODE = -32601 | 
				
			||||||
 | 
					    MESSAGE = "Method not found" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRPCInvalidParams(JSONRPCError): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Invalid params. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Invalid method parameter(s). | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CODE = -32602 | 
				
			||||||
 | 
					    MESSAGE = "Invalid params" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRPCInternalError(JSONRPCError): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Internal error. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Internal JSON-RPC error. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CODE = -32603 | 
				
			||||||
 | 
					    MESSAGE = "Internal error" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRPCServerError(JSONRPCError): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Server error. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Reserved for implementation-defined server-errors. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CODE = -32000 | 
				
			||||||
 | 
					    MESSAGE = "Server error" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRPCException(Exception): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ JSON-RPC Exception.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRPCInvalidRequestException(JSONRPCException): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Request is not valid.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRPCDispatchException(JSONRPCException): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ JSON-RPC Dispatch Exception. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Should be thrown in dispatch methods. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, code=None, message=None, data=None, *args, **kwargs): | 
				
			||||||
 | 
					        super(JSONRPCDispatchException, self).__init__(args, kwargs) | 
				
			||||||
 | 
					        self.error = JSONRPCError(code=code, data=data, message=message) | 
				
			||||||
@ -0,0 +1,151 @@ | 
				
			|||||||
 | 
					from . import six | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .base import JSONRPCBaseRequest, JSONRPCBaseResponse | 
				
			||||||
 | 
					from .exceptions import JSONRPCInvalidRequestException, JSONRPCError | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRPC10Request(JSONRPCBaseRequest): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ JSON-RPC 1.0 Request. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    A remote method is invoked by sending a request to a remote service. | 
				
			||||||
 | 
					    The request is a single object serialized using json. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param str method: The name of the method to be invoked. | 
				
			||||||
 | 
					    :param list params: An Array of objects to pass as arguments to the method. | 
				
			||||||
 | 
					    :param _id: This can be of any type. It is used to match the response with | 
				
			||||||
 | 
					        the request that it is replying to. | 
				
			||||||
 | 
					    :param bool is_notification: whether request notification or not. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    JSONRPC_VERSION = "1.0" | 
				
			||||||
 | 
					    REQUIRED_FIELDS = set(["method", "params", "id"]) | 
				
			||||||
 | 
					    POSSIBLE_FIELDS = set(["method", "params", "id"]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def data(self): | 
				
			||||||
 | 
					        data = dict((k, v) for k, v in self._data.items()) | 
				
			||||||
 | 
					        data["id"] = None if self.is_notification else data["id"] | 
				
			||||||
 | 
					        return data | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @data.setter | 
				
			||||||
 | 
					    def data(self, value): | 
				
			||||||
 | 
					        if not isinstance(value, dict): | 
				
			||||||
 | 
					            raise ValueError("data should be dict") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._data = value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def method(self): | 
				
			||||||
 | 
					        return self._data.get("method") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @method.setter | 
				
			||||||
 | 
					    def method(self, value): | 
				
			||||||
 | 
					        if not isinstance(value, six.string_types): | 
				
			||||||
 | 
					            raise ValueError("Method should be string") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._data["method"] = str(value) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def params(self): | 
				
			||||||
 | 
					        return self._data.get("params") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @params.setter | 
				
			||||||
 | 
					    def params(self, value): | 
				
			||||||
 | 
					        if not isinstance(value, (list, tuple)): | 
				
			||||||
 | 
					            raise ValueError("Incorrect params {0}".format(value)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._data["params"] = list(value) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def _id(self): | 
				
			||||||
 | 
					        return self._data.get("id") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @_id.setter | 
				
			||||||
 | 
					    def _id(self, value): | 
				
			||||||
 | 
					        self._data["id"] = value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def is_notification(self): | 
				
			||||||
 | 
					        return self._data["id"] is None or self._is_notification | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @is_notification.setter | 
				
			||||||
 | 
					    def is_notification(self, value): | 
				
			||||||
 | 
					        if value is None: | 
				
			||||||
 | 
					            value = self._id is None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self._id is None and not value: | 
				
			||||||
 | 
					            raise ValueError("Can not set attribute is_notification. " + | 
				
			||||||
 | 
					                             "Request id should not be None") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._is_notification = value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod | 
				
			||||||
 | 
					    def from_json(cls, json_str): | 
				
			||||||
 | 
					        data = cls.deserialize(json_str) | 
				
			||||||
 | 
					        return cls.from_data(data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod | 
				
			||||||
 | 
					    def from_data(cls, data): | 
				
			||||||
 | 
					        if not isinstance(data, dict): | 
				
			||||||
 | 
					            raise ValueError("data should be dict") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if cls.REQUIRED_FIELDS <= set(data.keys()) <= cls.POSSIBLE_FIELDS: | 
				
			||||||
 | 
					            return cls( | 
				
			||||||
 | 
					                method=data["method"], params=data["params"], _id=data["id"] | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            extra = set(data.keys()) - cls.POSSIBLE_FIELDS | 
				
			||||||
 | 
					            missed = cls.REQUIRED_FIELDS - set(data.keys()) | 
				
			||||||
 | 
					            msg = "Invalid request. Extra fields: {0}, Missed fields: {1}" | 
				
			||||||
 | 
					            raise JSONRPCInvalidRequestException(msg.format(extra, missed)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRPC10Response(JSONRPCBaseResponse): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    JSONRPC_VERSION = "1.0" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def data(self): | 
				
			||||||
 | 
					        data = dict((k, v) for k, v in self._data.items()) | 
				
			||||||
 | 
					        return data | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @data.setter | 
				
			||||||
 | 
					    def data(self, value): | 
				
			||||||
 | 
					        if not isinstance(value, dict): | 
				
			||||||
 | 
					            raise ValueError("data should be dict") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._data = value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def result(self): | 
				
			||||||
 | 
					        return self._data.get("result") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @result.setter | 
				
			||||||
 | 
					    def result(self, value): | 
				
			||||||
 | 
					        if self.error: | 
				
			||||||
 | 
					            raise ValueError("Either result or error should be used") | 
				
			||||||
 | 
					        self._data["result"] = value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def error(self): | 
				
			||||||
 | 
					        return self._data.get("error") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @error.setter | 
				
			||||||
 | 
					    def error(self, value): | 
				
			||||||
 | 
					        self._data.pop('value', None) | 
				
			||||||
 | 
					        if value: | 
				
			||||||
 | 
					            self._data["error"] = value | 
				
			||||||
 | 
					            # Test error | 
				
			||||||
 | 
					            JSONRPCError(**value) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def _id(self): | 
				
			||||||
 | 
					        return self._data.get("id") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @_id.setter | 
				
			||||||
 | 
					    def _id(self, value): | 
				
			||||||
 | 
					        if value is None: | 
				
			||||||
 | 
					            raise ValueError("id could not be null for JSON-RPC1.0 Response") | 
				
			||||||
 | 
					        self._data["id"] = value | 
				
			||||||
@ -0,0 +1,136 @@ | 
				
			|||||||
 | 
					import json | 
				
			||||||
 | 
					import logging | 
				
			||||||
 | 
					from .utils import is_invalid_params | 
				
			||||||
 | 
					from .exceptions import ( | 
				
			||||||
 | 
					    JSONRPCInvalidParams, | 
				
			||||||
 | 
					    JSONRPCInvalidRequest, | 
				
			||||||
 | 
					    JSONRPCInvalidRequestException, | 
				
			||||||
 | 
					    JSONRPCMethodNotFound, | 
				
			||||||
 | 
					    JSONRPCParseError, | 
				
			||||||
 | 
					    JSONRPCServerError, | 
				
			||||||
 | 
					    JSONRPCDispatchException, | 
				
			||||||
 | 
					) | 
				
			||||||
 | 
					from .jsonrpc1 import JSONRPC10Response | 
				
			||||||
 | 
					from .jsonrpc2 import ( | 
				
			||||||
 | 
					    JSONRPC20BatchRequest, | 
				
			||||||
 | 
					    JSONRPC20BatchResponse, | 
				
			||||||
 | 
					    JSONRPC20Response, | 
				
			||||||
 | 
					) | 
				
			||||||
 | 
					from .jsonrpc import JSONRPCRequest | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					logger = logging.getLogger(__name__) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONRPCResponseManager(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ JSON-RPC response manager. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Method brings syntactic sugar into library. Given dispatcher it handles | 
				
			||||||
 | 
					    request (both single and batch) and handles errors. | 
				
			||||||
 | 
					    Request could be handled in parallel, it is server responsibility. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param str request_str: json string. Will be converted into | 
				
			||||||
 | 
					        JSONRPC20Request, JSONRPC20BatchRequest or JSONRPC10Request | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param dict dispather: dict<function_name:function>. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    RESPONSE_CLASS_MAP = { | 
				
			||||||
 | 
					        "1.0": JSONRPC10Response, | 
				
			||||||
 | 
					        "2.0": JSONRPC20Response, | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod | 
				
			||||||
 | 
					    def handle(cls, request_str, dispatcher): | 
				
			||||||
 | 
					        if isinstance(request_str, bytes): | 
				
			||||||
 | 
					            request_str = request_str.decode("utf-8") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            data = json.loads(request_str) | 
				
			||||||
 | 
					        except (TypeError, ValueError): | 
				
			||||||
 | 
					            return JSONRPC20Response(error=JSONRPCParseError()._data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            request = JSONRPCRequest.from_data(data) | 
				
			||||||
 | 
					        except JSONRPCInvalidRequestException: | 
				
			||||||
 | 
					            return JSONRPC20Response(error=JSONRPCInvalidRequest()._data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return cls.handle_request(request, dispatcher) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod | 
				
			||||||
 | 
					    def handle_request(cls, request, dispatcher): | 
				
			||||||
 | 
					        """ Handle request data. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        At this moment request has correct jsonrpc format. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param dict request: data parsed from request_str. | 
				
			||||||
 | 
					        :param jsonrpc.dispatcher.Dispatcher dispatcher: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionadded: 1.8.0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        rs = request if isinstance(request, JSONRPC20BatchRequest) \ | 
				
			||||||
 | 
					            else [request] | 
				
			||||||
 | 
					        responses = [r for r in cls._get_responses(rs, dispatcher) | 
				
			||||||
 | 
					                     if r is not None] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # notifications | 
				
			||||||
 | 
					        if not responses: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if isinstance(request, JSONRPC20BatchRequest): | 
				
			||||||
 | 
					            response = JSONRPC20BatchResponse(*responses) | 
				
			||||||
 | 
					            response.request = request | 
				
			||||||
 | 
					            return response | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            return responses[0] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod | 
				
			||||||
 | 
					    def _get_responses(cls, requests, dispatcher): | 
				
			||||||
 | 
					        """ Response to each single JSON-RPC Request. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :return iterator(JSONRPC20Response): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionadded: 1.9.0 | 
				
			||||||
 | 
					          TypeError inside the function is distinguished from Invalid Params. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        for request in requests: | 
				
			||||||
 | 
					            def make_response(**kwargs): | 
				
			||||||
 | 
					                response = cls.RESPONSE_CLASS_MAP[request.JSONRPC_VERSION]( | 
				
			||||||
 | 
					                    _id=request._id, **kwargs) | 
				
			||||||
 | 
					                response.request = request | 
				
			||||||
 | 
					                return response | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            output = None | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                method = dispatcher[request.method] | 
				
			||||||
 | 
					            except KeyError: | 
				
			||||||
 | 
					                output = make_response(error=JSONRPCMethodNotFound()._data) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    result = method(*request.args, **request.kwargs) | 
				
			||||||
 | 
					                except JSONRPCDispatchException as e: | 
				
			||||||
 | 
					                    output = make_response(error=e.error._data) | 
				
			||||||
 | 
					                except Exception as e: | 
				
			||||||
 | 
					                    data = { | 
				
			||||||
 | 
					                        "type": e.__class__.__name__, | 
				
			||||||
 | 
					                        "args": e.args, | 
				
			||||||
 | 
					                        "message": str(e), | 
				
			||||||
 | 
					                    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    logger.exception("API Exception: {0}".format(data)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if isinstance(e, TypeError) and is_invalid_params( | 
				
			||||||
 | 
					                            method, *request.args, **request.kwargs): | 
				
			||||||
 | 
					                        output = make_response( | 
				
			||||||
 | 
					                            error=JSONRPCInvalidParams(data=data)._data) | 
				
			||||||
 | 
					                    else: | 
				
			||||||
 | 
					                        output = make_response( | 
				
			||||||
 | 
					                            error=JSONRPCServerError(data=data)._data) | 
				
			||||||
 | 
					                else: | 
				
			||||||
 | 
					                    output = make_response(result=result) | 
				
			||||||
 | 
					            finally: | 
				
			||||||
 | 
					                if not request.is_notification: | 
				
			||||||
 | 
					                    yield output | 
				
			||||||
@ -0,0 +1,584 @@ | 
				
			|||||||
 | 
					"""Utilities for writing code that runs on Python 2 and 3""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copyright (c) 2010-2013 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. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import operator | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import types | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__author__ = "Benjamin Peterson <benjamin@python.org>" | 
				
			||||||
 | 
					__version__ = "1.4.1" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Useful for very coarse version differentiation. | 
				
			||||||
 | 
					PY2 = sys.version_info[0] == 2 | 
				
			||||||
 | 
					PY3 = sys.version_info[0] == 3 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if PY3: | 
				
			||||||
 | 
					    string_types = str, | 
				
			||||||
 | 
					    integer_types = int, | 
				
			||||||
 | 
					    class_types = type, | 
				
			||||||
 | 
					    text_type = str | 
				
			||||||
 | 
					    binary_type = bytes | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MAXSIZE = sys.maxsize | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    string_types = basestring, | 
				
			||||||
 | 
					    integer_types = (int, long) | 
				
			||||||
 | 
					    class_types = (type, types.ClassType) | 
				
			||||||
 | 
					    text_type = unicode | 
				
			||||||
 | 
					    binary_type = str | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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) | 
				
			||||||
 | 
					        # This is a bit ugly, but it avoids running this again. | 
				
			||||||
 | 
					        delattr(tp, self.name) | 
				
			||||||
 | 
					        return result | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MovedModule(_LazyDescr): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, name, old, new=None): | 
				
			||||||
 | 
					        super(MovedModule, self).__init__(name) | 
				
			||||||
 | 
					        if PY3: | 
				
			||||||
 | 
					            if new is None: | 
				
			||||||
 | 
					                new = name | 
				
			||||||
 | 
					            self.mod = new | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            self.mod = old | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _resolve(self): | 
				
			||||||
 | 
					        return _import_module(self.mod) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MovedAttribute(_LazyDescr): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): | 
				
			||||||
 | 
					        super(MovedAttribute, self).__init__(name) | 
				
			||||||
 | 
					        if PY3: | 
				
			||||||
 | 
					            if new_mod is None: | 
				
			||||||
 | 
					                new_mod = name | 
				
			||||||
 | 
					            self.mod = new_mod | 
				
			||||||
 | 
					            if new_attr is None: | 
				
			||||||
 | 
					                if old_attr is None: | 
				
			||||||
 | 
					                    new_attr = name | 
				
			||||||
 | 
					                else: | 
				
			||||||
 | 
					                    new_attr = old_attr | 
				
			||||||
 | 
					            self.attr = new_attr | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            self.mod = old_mod | 
				
			||||||
 | 
					            if old_attr is None: | 
				
			||||||
 | 
					                old_attr = name | 
				
			||||||
 | 
					            self.attr = old_attr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _resolve(self): | 
				
			||||||
 | 
					        module = _import_module(self.mod) | 
				
			||||||
 | 
					        return getattr(module, self.attr) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _MovedItems(types.ModuleType): | 
				
			||||||
 | 
					    """Lazy loading of moved objects""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_moved_attributes = [ | 
				
			||||||
 | 
					    MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), | 
				
			||||||
 | 
					    MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), | 
				
			||||||
 | 
					    MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), | 
				
			||||||
 | 
					    MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), | 
				
			||||||
 | 
					    MovedAttribute("map", "itertools", "builtins", "imap", "map"), | 
				
			||||||
 | 
					    MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), | 
				
			||||||
 | 
					    MovedAttribute("reload_module", "__builtin__", "imp", "reload"), | 
				
			||||||
 | 
					    MovedAttribute("reduce", "__builtin__", "functools"), | 
				
			||||||
 | 
					    MovedAttribute("StringIO", "StringIO", "io"), | 
				
			||||||
 | 
					    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("copyreg", "copy_reg"), | 
				
			||||||
 | 
					    MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), | 
				
			||||||
 | 
					    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_multipart", "email.MIMEMultipart", "email.mime.multipart"), | 
				
			||||||
 | 
					    MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), | 
				
			||||||
 | 
					    MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), | 
				
			||||||
 | 
					    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_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("winreg", "_winreg"), | 
				
			||||||
 | 
					] | 
				
			||||||
 | 
					for attr in _moved_attributes: | 
				
			||||||
 | 
					    setattr(_MovedItems, attr.name, attr) | 
				
			||||||
 | 
					del attr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					moves = sys.modules[__name__ + ".moves"] = _MovedItems(__name__ + ".moves") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Module_six_moves_urllib_parse(types.ModuleType): | 
				
			||||||
 | 
					    """Lazy loading of moved objects in six.moves.urllib_parse""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_urllib_parse_moved_attributes = [ | 
				
			||||||
 | 
					    MovedAttribute("ParseResult", "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("urlencode", "urllib", "urllib.parse"), | 
				
			||||||
 | 
					] | 
				
			||||||
 | 
					for attr in _urllib_parse_moved_attributes: | 
				
			||||||
 | 
					    setattr(Module_six_moves_urllib_parse, attr.name, attr) | 
				
			||||||
 | 
					del attr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sys.modules[__name__ + ".moves.urllib_parse"] = Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse") | 
				
			||||||
 | 
					sys.modules[__name__ + ".moves.urllib.parse"] = Module_six_moves_urllib_parse(__name__ + ".moves.urllib.parse") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Module_six_moves_urllib_error(types.ModuleType): | 
				
			||||||
 | 
					    """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 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sys.modules[__name__ + ".moves.urllib_error"] = Module_six_moves_urllib_error(__name__ + ".moves.urllib_error") | 
				
			||||||
 | 
					sys.modules[__name__ + ".moves.urllib.error"] = Module_six_moves_urllib_error(__name__ + ".moves.urllib.error") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Module_six_moves_urllib_request(types.ModuleType): | 
				
			||||||
 | 
					    """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"), | 
				
			||||||
 | 
					] | 
				
			||||||
 | 
					for attr in _urllib_request_moved_attributes: | 
				
			||||||
 | 
					    setattr(Module_six_moves_urllib_request, attr.name, attr) | 
				
			||||||
 | 
					del attr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sys.modules[__name__ + ".moves.urllib_request"] = Module_six_moves_urllib_request(__name__ + ".moves.urllib_request") | 
				
			||||||
 | 
					sys.modules[__name__ + ".moves.urllib.request"] = Module_six_moves_urllib_request(__name__ + ".moves.urllib.request") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Module_six_moves_urllib_response(types.ModuleType): | 
				
			||||||
 | 
					    """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 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sys.modules[__name__ + ".moves.urllib_response"] = Module_six_moves_urllib_response(__name__ + ".moves.urllib_response") | 
				
			||||||
 | 
					sys.modules[__name__ + ".moves.urllib.response"] = Module_six_moves_urllib_response(__name__ + ".moves.urllib.response") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Module_six_moves_urllib_robotparser(types.ModuleType): | 
				
			||||||
 | 
					    """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 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sys.modules[__name__ + ".moves.urllib_robotparser"] = Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib_robotparser") | 
				
			||||||
 | 
					sys.modules[__name__ + ".moves.urllib.robotparser"] = Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Module_six_moves_urllib(types.ModuleType): | 
				
			||||||
 | 
					    """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" | 
				
			||||||
 | 
					    parse = sys.modules[__name__ + ".moves.urllib_parse"] | 
				
			||||||
 | 
					    error = sys.modules[__name__ + ".moves.urllib_error"] | 
				
			||||||
 | 
					    request = sys.modules[__name__ + ".moves.urllib_request"] | 
				
			||||||
 | 
					    response = sys.modules[__name__ + ".moves.urllib_response"] | 
				
			||||||
 | 
					    robotparser = sys.modules[__name__ + ".moves.urllib_robotparser"] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sys.modules[__name__ + ".moves.urllib"] = Module_six_moves_urllib(__name__ + ".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__" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _iterkeys = "keys" | 
				
			||||||
 | 
					    _itervalues = "values" | 
				
			||||||
 | 
					    _iteritems = "items" | 
				
			||||||
 | 
					    _iterlists = "lists" | 
				
			||||||
 | 
					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" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _iterkeys = "iterkeys" | 
				
			||||||
 | 
					    _itervalues = "itervalues" | 
				
			||||||
 | 
					    _iteritems = "iteritems" | 
				
			||||||
 | 
					    _iterlists = "iterlists" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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__) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def iterkeys(d, **kw): | 
				
			||||||
 | 
					    """Return an iterator over the keys of a dictionary.""" | 
				
			||||||
 | 
					    return iter(getattr(d, _iterkeys)(**kw)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def itervalues(d, **kw): | 
				
			||||||
 | 
					    """Return an iterator over the values of a dictionary.""" | 
				
			||||||
 | 
					    return iter(getattr(d, _itervalues)(**kw)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def iteritems(d, **kw): | 
				
			||||||
 | 
					    """Return an iterator over the (key, value) pairs of a dictionary.""" | 
				
			||||||
 | 
					    return iter(getattr(d, _iteritems)(**kw)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def iterlists(d, **kw): | 
				
			||||||
 | 
					    """Return an iterator over the (key, [values]) pairs of a dictionary.""" | 
				
			||||||
 | 
					    return iter(getattr(d, _iterlists)(**kw)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if PY3: | 
				
			||||||
 | 
					    def b(s): | 
				
			||||||
 | 
					        return s.encode("latin-1") | 
				
			||||||
 | 
					    def u(s): | 
				
			||||||
 | 
					        return s | 
				
			||||||
 | 
					    unichr = chr | 
				
			||||||
 | 
					    if sys.version_info[1] <= 1: | 
				
			||||||
 | 
					        def int2byte(i): | 
				
			||||||
 | 
					            return bytes((i,)) | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        # This is about 2x faster than the implementation above on 3.2+ | 
				
			||||||
 | 
					        int2byte = operator.methodcaller("to_bytes", 1, "big") | 
				
			||||||
 | 
					    byte2int = operator.itemgetter(0) | 
				
			||||||
 | 
					    indexbytes = operator.getitem | 
				
			||||||
 | 
					    iterbytes = iter | 
				
			||||||
 | 
					    import io | 
				
			||||||
 | 
					    StringIO = io.StringIO | 
				
			||||||
 | 
					    BytesIO = io.BytesIO | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    def b(s): | 
				
			||||||
 | 
					        return s | 
				
			||||||
 | 
					    def u(s): | 
				
			||||||
 | 
					        return unicode(s, "unicode_escape") | 
				
			||||||
 | 
					    unichr = unichr | 
				
			||||||
 | 
					    int2byte = chr | 
				
			||||||
 | 
					    def byte2int(bs): | 
				
			||||||
 | 
					        return ord(bs[0]) | 
				
			||||||
 | 
					    def indexbytes(buf, i): | 
				
			||||||
 | 
					        return ord(buf[i]) | 
				
			||||||
 | 
					    def iterbytes(buf): | 
				
			||||||
 | 
					        return (ord(byte) for byte in buf) | 
				
			||||||
 | 
					    import StringIO | 
				
			||||||
 | 
					    StringIO = BytesIO = StringIO.StringIO | 
				
			||||||
 | 
					_add_doc(b, """Byte literal""") | 
				
			||||||
 | 
					_add_doc(u, """Text literal""") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if PY3: | 
				
			||||||
 | 
					    exec_ = getattr(moves.builtins, "exec") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def reraise(tp, value, tb=None): | 
				
			||||||
 | 
					        if value.__traceback__ is not tb: | 
				
			||||||
 | 
					            raise value.with_traceback(tb) | 
				
			||||||
 | 
					        raise value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    def exec_(_code_, _globs_=None, _locs_=None): | 
				
			||||||
 | 
					        """Execute code in a namespace.""" | 
				
			||||||
 | 
					        if _globs_ is None: | 
				
			||||||
 | 
					            frame = sys._getframe(1) | 
				
			||||||
 | 
					            _globs_ = frame.f_globals | 
				
			||||||
 | 
					            if _locs_ is None: | 
				
			||||||
 | 
					                _locs_ = frame.f_locals | 
				
			||||||
 | 
					            del frame | 
				
			||||||
 | 
					        elif _locs_ is None: | 
				
			||||||
 | 
					            _locs_ = _globs_ | 
				
			||||||
 | 
					        exec("""exec _code_ in _globs_, _locs_""") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    exec_("""def reraise(tp, value, tb=None): | 
				
			||||||
 | 
					    raise tp, value, tb | 
				
			||||||
 | 
					""") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_add_doc(reraise, """Reraise an exception.""") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def with_metaclass(meta, *bases): | 
				
			||||||
 | 
					    """Create a base class with a metaclass.""" | 
				
			||||||
 | 
					    return meta("NewBase", bases, {}) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def add_metaclass(metaclass): | 
				
			||||||
 | 
					    """Class decorator for creating a class with a metaclass.""" | 
				
			||||||
 | 
					    def wrapper(cls): | 
				
			||||||
 | 
					        orig_vars = cls.__dict__.copy() | 
				
			||||||
 | 
					        orig_vars.pop('__dict__', None) | 
				
			||||||
 | 
					        orig_vars.pop('__weakref__', None) | 
				
			||||||
 | 
					        for slots_var in orig_vars.get('__slots__', ()): | 
				
			||||||
 | 
					            orig_vars.pop(slots_var) | 
				
			||||||
 | 
					        return metaclass(cls.__name__, cls.__bases__, orig_vars) | 
				
			||||||
 | 
					    return wrapper | 
				
			||||||
@ -0,0 +1,7 @@ | 
				
			|||||||
 | 
					# Python3.5+ code. | 
				
			||||||
 | 
					# This won't even parse in earlier versions, so it's kept in a separate file | 
				
			||||||
 | 
					# and imported when needed. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def distance(a: float, b: float) -> float: | 
				
			||||||
 | 
					    return (a ** 2 + b ** 2) ** 0.5 | 
				
			||||||
@ -0,0 +1,11 @@ | 
				
			|||||||
 | 
					SECRET_KEY = 'secret' | 
				
			||||||
 | 
					ROOT_URLCONF = 'jsonrpc.tests.test_backend_django.urls' | 
				
			||||||
 | 
					ALLOWED_HOSTS = ['testserver'] | 
				
			||||||
 | 
					DATABASE_ENGINE = 'django.db.backends.sqlite3' | 
				
			||||||
 | 
					DATABASES = { | 
				
			||||||
 | 
					    'default': { | 
				
			||||||
 | 
					        'ENGINE': 'django.db.backends.sqlite3', | 
				
			||||||
 | 
					        'NAME': ':memory:', | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					JSONRPC_MAP_VIEW_ENABLED = True | 
				
			||||||
@ -0,0 +1,89 @@ | 
				
			|||||||
 | 
					""" Test Django Backend.""" | 
				
			||||||
 | 
					from __future__ import absolute_import | 
				
			||||||
 | 
					import os | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try: | 
				
			||||||
 | 
					    from django.core.urlresolvers import RegexURLPattern | 
				
			||||||
 | 
					    from django.test import TestCase | 
				
			||||||
 | 
					except ImportError: | 
				
			||||||
 | 
					    import unittest | 
				
			||||||
 | 
					    raise unittest.SkipTest('Django not found for testing') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ...backend.django import JSONRPCAPI, api | 
				
			||||||
 | 
					import json | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestDjangoBackend(TestCase): | 
				
			||||||
 | 
					    @classmethod | 
				
			||||||
 | 
					    def setUpClass(cls): | 
				
			||||||
 | 
					        os.environ['DJANGO_SETTINGS_MODULE'] = \ | 
				
			||||||
 | 
					            'jsonrpc.tests.test_backend_django.settings' | 
				
			||||||
 | 
					        super(TestDjangoBackend, cls).setUpClass() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_urls(self): | 
				
			||||||
 | 
					        self.assertTrue(isinstance(api.urls, list)) | 
				
			||||||
 | 
					        for api_url in api.urls: | 
				
			||||||
 | 
					            self.assertTrue(isinstance(api_url, RegexURLPattern)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_client(self): | 
				
			||||||
 | 
					        @api.dispatcher.add_method | 
				
			||||||
 | 
					        def dummy(request): | 
				
			||||||
 | 
					            return "" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        json_data = { | 
				
			||||||
 | 
					            "id": "0", | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "dummy", | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					        response = self.client.post( | 
				
			||||||
 | 
					            '', | 
				
			||||||
 | 
					            json.dumps(json_data), | 
				
			||||||
 | 
					            content_type='application/json', | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200) | 
				
			||||||
 | 
					        data = json.loads(response.content.decode('utf8')) | 
				
			||||||
 | 
					        self.assertEqual(data['result'], '') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_method_not_allowed(self): | 
				
			||||||
 | 
					        response = self.client.get( | 
				
			||||||
 | 
					            '', | 
				
			||||||
 | 
					            content_type='application/json', | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 405, "Should allow only POST") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_invalid_request(self): | 
				
			||||||
 | 
					        response = self.client.post( | 
				
			||||||
 | 
					            '', | 
				
			||||||
 | 
					            '{', | 
				
			||||||
 | 
					            content_type='application/json', | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200) | 
				
			||||||
 | 
					        data = json.loads(response.content.decode('utf8')) | 
				
			||||||
 | 
					        self.assertEqual(data['error']['code'], -32700) | 
				
			||||||
 | 
					        self.assertEqual(data['error']['message'], 'Parse error') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_resource_map(self): | 
				
			||||||
 | 
					        response = self.client.get('/map') | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200) | 
				
			||||||
 | 
					        data = response.content.decode('utf8') | 
				
			||||||
 | 
					        self.assertIn("JSON-RPC map", data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_method_not_allowed_prefix(self): | 
				
			||||||
 | 
					        response = self.client.get( | 
				
			||||||
 | 
					            '/prefix/', | 
				
			||||||
 | 
					            content_type='application/json', | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 405) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_resource_map_prefix(self): | 
				
			||||||
 | 
					        response = self.client.get('/prefix/map') | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_empty_initial_dispatcher(self): | 
				
			||||||
 | 
					        class SubDispatcher(type(api.dispatcher)): | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        custom_dispatcher = SubDispatcher() | 
				
			||||||
 | 
					        custom_api = JSONRPCAPI(custom_dispatcher) | 
				
			||||||
 | 
					        self.assertEqual(type(custom_api.dispatcher), SubDispatcher) | 
				
			||||||
 | 
					        self.assertEqual(id(custom_api.dispatcher), id(custom_dispatcher)) | 
				
			||||||
@ -0,0 +1,7 @@ | 
				
			|||||||
 | 
					from django.conf.urls import url, include | 
				
			||||||
 | 
					from jsonrpc.backend.django import api | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					urlpatterns = [ | 
				
			||||||
 | 
					    url(r'', include(api.urls)), | 
				
			||||||
 | 
					    url(r'^prefix/', include(api.urls)), | 
				
			||||||
 | 
					] | 
				
			||||||
@ -0,0 +1,182 @@ | 
				
			|||||||
 | 
					import json | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if sys.version_info < (3, 3): | 
				
			||||||
 | 
					    from mock import patch | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    from unittest.mock import patch | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if sys.version_info < (2, 7): | 
				
			||||||
 | 
					    import unittest2 as unittest | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    import unittest | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Flask is supported only for python2 and python3.3+ | 
				
			||||||
 | 
					if sys.version_info < (3, 0) or sys.version_info >= (3, 3): | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        from flask import Flask | 
				
			||||||
 | 
					    except ImportError: | 
				
			||||||
 | 
					        raise unittest.SkipTest('Flask not found for testing') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    from ...backend.flask import JSONRPCAPI, api | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @api.dispatcher.add_method | 
				
			||||||
 | 
					    def dummy(): | 
				
			||||||
 | 
					        return "" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@unittest.skipIf((3, 0) <= sys.version_info < (3, 3), | 
				
			||||||
 | 
					                 'Flask does not support python 3.0 - 3.2') | 
				
			||||||
 | 
					class TestFlaskBackend(unittest.TestCase): | 
				
			||||||
 | 
					    REQUEST = json.dumps({ | 
				
			||||||
 | 
					        "id": "0", | 
				
			||||||
 | 
					        "jsonrpc": "2.0", | 
				
			||||||
 | 
					        "method": "dummy", | 
				
			||||||
 | 
					    }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self): | 
				
			||||||
 | 
					        self.client = self._get_test_client(JSONRPCAPI()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_test_client(self, api): | 
				
			||||||
 | 
					        @api.dispatcher.add_method | 
				
			||||||
 | 
					        def dummy(): | 
				
			||||||
 | 
					            return "" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app = Flask(__name__) | 
				
			||||||
 | 
					        app.config["TESTING"] = True | 
				
			||||||
 | 
					        app.register_blueprint(api.as_blueprint()) | 
				
			||||||
 | 
					        return app.test_client() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_client(self): | 
				
			||||||
 | 
					        response = self.client.post( | 
				
			||||||
 | 
					            '/', | 
				
			||||||
 | 
					            data=self.REQUEST, | 
				
			||||||
 | 
					            content_type='application/json', | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200) | 
				
			||||||
 | 
					        data = json.loads(response.data.decode('utf8')) | 
				
			||||||
 | 
					        self.assertEqual(data['result'], '') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_method_not_allowed(self): | 
				
			||||||
 | 
					        response = self.client.get( | 
				
			||||||
 | 
					            '/', | 
				
			||||||
 | 
					            content_type='application/json', | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 405, "Should allow only POST") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_parse_error(self): | 
				
			||||||
 | 
					        response = self.client.post( | 
				
			||||||
 | 
					            '/', | 
				
			||||||
 | 
					            data='{', | 
				
			||||||
 | 
					            content_type='application/json', | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200) | 
				
			||||||
 | 
					        data = json.loads(response.data.decode('utf8')) | 
				
			||||||
 | 
					        self.assertEqual(data['error']['code'], -32700) | 
				
			||||||
 | 
					        self.assertEqual(data['error']['message'], 'Parse error') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_wrong_content_type(self): | 
				
			||||||
 | 
					        response = self.client.post( | 
				
			||||||
 | 
					            '/', | 
				
			||||||
 | 
					            data=self.REQUEST, | 
				
			||||||
 | 
					            content_type='application/x-www-form-urlencoded', | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200) | 
				
			||||||
 | 
					        data = json.loads(response.data.decode('utf8')) | 
				
			||||||
 | 
					        self.assertEqual(data['error']['code'], -32700) | 
				
			||||||
 | 
					        self.assertEqual(data['error']['message'], 'Parse error') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_invalid_request(self): | 
				
			||||||
 | 
					        response = self.client.post( | 
				
			||||||
 | 
					            '/', | 
				
			||||||
 | 
					            data='{"method": "dummy", "id": 1}', | 
				
			||||||
 | 
					            content_type='application/json', | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200) | 
				
			||||||
 | 
					        data = json.loads(response.data.decode('utf8')) | 
				
			||||||
 | 
					        self.assertEqual(data['error']['code'], -32600) | 
				
			||||||
 | 
					        self.assertEqual(data['error']['message'], 'Invalid Request') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_method_not_found(self): | 
				
			||||||
 | 
					        data = { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "dummy2", | 
				
			||||||
 | 
					            "id": 1 | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					        response = self.client.post( | 
				
			||||||
 | 
					            '/', | 
				
			||||||
 | 
					            data=json.dumps(data), | 
				
			||||||
 | 
					            content_type='application/json', | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200) | 
				
			||||||
 | 
					        data = json.loads(response.data.decode('utf8')) | 
				
			||||||
 | 
					        self.assertEqual(data['error']['code'], -32601) | 
				
			||||||
 | 
					        self.assertEqual(data['error']['message'], 'Method not found') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_invalid_parameters(self): | 
				
			||||||
 | 
					        data = { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "dummy", | 
				
			||||||
 | 
					            "params": [42], | 
				
			||||||
 | 
					            "id": 1 | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					        response = self.client.post( | 
				
			||||||
 | 
					            '/', | 
				
			||||||
 | 
					            data=json.dumps(data), | 
				
			||||||
 | 
					            content_type='application/json', | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200) | 
				
			||||||
 | 
					        data = json.loads(response.data.decode('utf8')) | 
				
			||||||
 | 
					        self.assertEqual(data['error']['code'], -32602) | 
				
			||||||
 | 
					        self.assertEqual(data['error']['message'], 'Invalid params') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_resource_map(self): | 
				
			||||||
 | 
					        response = self.client.get('/map') | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200) | 
				
			||||||
 | 
					        self.assertTrue("JSON-RPC map" in response.data.decode('utf8')) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_method_not_allowed_prefix(self): | 
				
			||||||
 | 
					        response = self.client.get( | 
				
			||||||
 | 
					            '/', | 
				
			||||||
 | 
					            content_type='application/json', | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 405) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_resource_map_prefix(self): | 
				
			||||||
 | 
					        response = self.client.get('/map') | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_as_view(self): | 
				
			||||||
 | 
					        api = JSONRPCAPI() | 
				
			||||||
 | 
					        with patch.object(api, 'jsonrpc') as mock_jsonrpc: | 
				
			||||||
 | 
					            self.assertIs(api.as_view(), mock_jsonrpc) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_not_check_content_type(self): | 
				
			||||||
 | 
					        client = self._get_test_client(JSONRPCAPI(check_content_type=False)) | 
				
			||||||
 | 
					        response = client.post( | 
				
			||||||
 | 
					            '/', | 
				
			||||||
 | 
					            data=self.REQUEST, | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200) | 
				
			||||||
 | 
					        data = json.loads(response.data.decode('utf8')) | 
				
			||||||
 | 
					        self.assertEqual(data['result'], '') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_check_content_type(self): | 
				
			||||||
 | 
					        client = self._get_test_client(JSONRPCAPI(check_content_type=False)) | 
				
			||||||
 | 
					        response = client.post( | 
				
			||||||
 | 
					            '/', | 
				
			||||||
 | 
					            data=self.REQUEST, | 
				
			||||||
 | 
					            content_type="application/x-www-form-urlencoded" | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200) | 
				
			||||||
 | 
					        data = json.loads(response.data.decode('utf8')) | 
				
			||||||
 | 
					        self.assertEqual(data['result'], '') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_empty_initial_dispatcher(self): | 
				
			||||||
 | 
					        class SubDispatcher(type(api.dispatcher)): | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        custom_dispatcher = SubDispatcher() | 
				
			||||||
 | 
					        custom_api = JSONRPCAPI(custom_dispatcher) | 
				
			||||||
 | 
					        self.assertEqual(type(custom_api.dispatcher), SubDispatcher) | 
				
			||||||
 | 
					        self.assertEqual(id(custom_api.dispatcher), id(custom_dispatcher)) | 
				
			||||||
@ -0,0 +1,39 @@ | 
				
			|||||||
 | 
					""" Test base JSON-RPC classes.""" | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ..base import JSONRPCBaseRequest, JSONRPCBaseResponse | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if sys.version_info < (2, 7): | 
				
			||||||
 | 
					    import unittest2 as unittest | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    import unittest | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPCBaseRequest(unittest.TestCase): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Test JSONRPCBaseRequest functionality.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data(self): | 
				
			||||||
 | 
					        request = JSONRPCBaseRequest() | 
				
			||||||
 | 
					        self.assertEqual(request.data, {}) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            request.data = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            request.data = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPCBaseResponse(unittest.TestCase): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Test JSONRPCBaseResponse functionality.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data(self): | 
				
			||||||
 | 
					        response = JSONRPCBaseResponse(result="") | 
				
			||||||
 | 
					        self.assertEqual(response.data, {}) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            response.data = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            response.data = None | 
				
			||||||
@ -0,0 +1,34 @@ | 
				
			|||||||
 | 
					""" Exmples of usage with tests. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Tests in this file represent examples taken from JSON-RPC specification. | 
				
			||||||
 | 
					http://www.jsonrpc.org/specification#examples | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import json | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ..manager import JSONRPCResponseManager | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if sys.version_info < (2, 7): | 
				
			||||||
 | 
					    import unittest2 as unittest | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    import unittest | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def isjsonequal(json1, json2): | 
				
			||||||
 | 
					    return json.loads(json1) == json.loads(json2) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPCExamples(unittest.TestCase): | 
				
			||||||
 | 
					    def setUp(self): | 
				
			||||||
 | 
					        self.dispatcher = { | 
				
			||||||
 | 
					            "return_none": lambda: None, | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_none_as_result(self): | 
				
			||||||
 | 
					        req = '{"jsonrpc": "2.0", "method": "return_none", "id": 0}' | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isjsonequal( | 
				
			||||||
 | 
					            response.json, | 
				
			||||||
 | 
					            '{"jsonrpc": "2.0", "result": null, "id": 0}' | 
				
			||||||
 | 
					        )) | 
				
			||||||
@ -0,0 +1,142 @@ | 
				
			|||||||
 | 
					from ..dispatcher import Dispatcher | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					if sys.version_info < (2, 7): | 
				
			||||||
 | 
					    import unittest2 as unittest | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    import unittest | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Math: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def sum(self, a, b): | 
				
			||||||
 | 
					        return a + b | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def diff(self, a, b): | 
				
			||||||
 | 
					        return a - b | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestDispatcher(unittest.TestCase): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Test Dispatcher functionality.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_getter(self): | 
				
			||||||
 | 
					        d = Dispatcher() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(KeyError): | 
				
			||||||
 | 
					            d["method"] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        d["add"] = lambda *args: sum(args) | 
				
			||||||
 | 
					        self.assertEqual(d["add"](1, 1), 2) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_in(self): | 
				
			||||||
 | 
					        d = Dispatcher() | 
				
			||||||
 | 
					        d["method"] = lambda: "" | 
				
			||||||
 | 
					        self.assertIn("method", d) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_add_method(self): | 
				
			||||||
 | 
					        d = Dispatcher() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @d.add_method | 
				
			||||||
 | 
					        def add(x, y): | 
				
			||||||
 | 
					            return x + y | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertIn("add", d) | 
				
			||||||
 | 
					        self.assertEqual(d["add"](1, 1), 2) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_add_method_with_name(self): | 
				
			||||||
 | 
					        d = Dispatcher() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @d.add_method(name="this.add") | 
				
			||||||
 | 
					        def add(x, y): | 
				
			||||||
 | 
					            return x + y | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertNotIn("add", d) | 
				
			||||||
 | 
					        self.assertIn("this.add", d) | 
				
			||||||
 | 
					        self.assertEqual(d["this.add"](1, 1), 2) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_add_class(self): | 
				
			||||||
 | 
					        d = Dispatcher() | 
				
			||||||
 | 
					        d.add_class(Math) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertIn("math.sum", d) | 
				
			||||||
 | 
					        self.assertIn("math.diff", d) | 
				
			||||||
 | 
					        self.assertEqual(d["math.sum"](3, 8), 11) | 
				
			||||||
 | 
					        self.assertEqual(d["math.diff"](6, 9), -3) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_add_object(self): | 
				
			||||||
 | 
					        d = Dispatcher() | 
				
			||||||
 | 
					        d.add_object(Math()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertIn("math.sum", d) | 
				
			||||||
 | 
					        self.assertIn("math.diff", d) | 
				
			||||||
 | 
					        self.assertEqual(d["math.sum"](5, 2), 7) | 
				
			||||||
 | 
					        self.assertEqual(d["math.diff"](15, 9), 6) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_add_dict(self): | 
				
			||||||
 | 
					        d = Dispatcher() | 
				
			||||||
 | 
					        d.add_dict({"sum": lambda *args: sum(args)}, "util") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertIn("util.sum", d) | 
				
			||||||
 | 
					        self.assertEqual(d["util.sum"](13, -2), 11) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_add_method_keep_function_definitions(self): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        d = Dispatcher() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @d.add_method | 
				
			||||||
 | 
					        def one(x): | 
				
			||||||
 | 
					            return x | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertIsNotNone(one) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_del_method(self): | 
				
			||||||
 | 
					        d = Dispatcher() | 
				
			||||||
 | 
					        d["method"] = lambda: "" | 
				
			||||||
 | 
					        self.assertIn("method", d) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        del d["method"] | 
				
			||||||
 | 
					        self.assertNotIn("method", d) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_to_dict(self): | 
				
			||||||
 | 
					        d = Dispatcher() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def func(): | 
				
			||||||
 | 
					            return "" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        d["method"] = func | 
				
			||||||
 | 
					        self.assertEqual(dict(d), {"method": func}) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_init_from_object_instance(self): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class Dummy(): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            def one(self): | 
				
			||||||
 | 
					                pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            def two(self): | 
				
			||||||
 | 
					                pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dummy = Dummy() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        d = Dispatcher(dummy) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertIn("one", d) | 
				
			||||||
 | 
					        self.assertIn("two", d) | 
				
			||||||
 | 
					        self.assertNotIn("__class__", d) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_init_from_dictionary(self): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dummy = { | 
				
			||||||
 | 
					            'one': lambda x: x, | 
				
			||||||
 | 
					            'two': lambda x: x, | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        d = Dispatcher(dummy) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertIn("one", d) | 
				
			||||||
 | 
					        self.assertIn("two", d) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_dispatcher_representation(self): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        d = Dispatcher() | 
				
			||||||
 | 
					        self.assertEqual('{}', repr(d)) | 
				
			||||||
@ -0,0 +1,206 @@ | 
				
			|||||||
 | 
					""" Exmples of usage with tests. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Tests in this file represent examples taken from JSON-RPC specification. | 
				
			||||||
 | 
					http://www.jsonrpc.org/specification#examples | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import json | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ..manager import JSONRPCResponseManager | 
				
			||||||
 | 
					from ..jsonrpc2 import JSONRPC20Request, JSONRPC20BatchRequest | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if sys.version_info < (2, 7): | 
				
			||||||
 | 
					    import unittest2 as unittest | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    import unittest | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def isjsonequal(json1, json2): | 
				
			||||||
 | 
					    return json.loads(json1) == json.loads(json2) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPCExamples(unittest.TestCase): | 
				
			||||||
 | 
					    def setUp(self): | 
				
			||||||
 | 
					        self.dispatcher = { | 
				
			||||||
 | 
					            "subtract": lambda a, b: a - b, | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_rpc_call_with_positional_parameters(self): | 
				
			||||||
 | 
					        req = '{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}'  # noqa | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isjsonequal( | 
				
			||||||
 | 
					            response.json, | 
				
			||||||
 | 
					            '{"jsonrpc": "2.0", "result": 19, "id": 1}' | 
				
			||||||
 | 
					        )) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        req = '{"jsonrpc": "2.0", "method": "subtract", "params": [23, 42], "id": 2}'  # noqa | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isjsonequal( | 
				
			||||||
 | 
					            response.json, | 
				
			||||||
 | 
					            '{"jsonrpc": "2.0", "result": -19, "id": 2}' | 
				
			||||||
 | 
					        )) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_rpc_call_with_named_parameters(self): | 
				
			||||||
 | 
					        def subtract(minuend=None, subtrahend=None): | 
				
			||||||
 | 
					            return minuend - subtrahend | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dispatcher = { | 
				
			||||||
 | 
					            "subtract": subtract, | 
				
			||||||
 | 
					            "sum": lambda *args: sum(args), | 
				
			||||||
 | 
					            "get_data": lambda: ["hello", 5], | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        req = '{"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3}'  # noqa | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isjsonequal( | 
				
			||||||
 | 
					            response.json, | 
				
			||||||
 | 
					            '{"jsonrpc": "2.0", "result": 19, "id": 3}' | 
				
			||||||
 | 
					        )) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        req = '{"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "id": 4}'  # noqa | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isjsonequal( | 
				
			||||||
 | 
					            response.json, | 
				
			||||||
 | 
					            '{"jsonrpc": "2.0", "result": 19, "id": 4}', | 
				
			||||||
 | 
					        )) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_notification(self): | 
				
			||||||
 | 
					        req = '{"jsonrpc": "2.0", "method": "update", "params": [1,2,3,4,5]}' | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertEqual(response, None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        req = '{"jsonrpc": "2.0", "method": "foobar"}' | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertEqual(response, None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_rpc_call_of_non_existent_method(self): | 
				
			||||||
 | 
					        req = '{"jsonrpc": "2.0", "method": "foobar", "id": "1"}' | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isjsonequal( | 
				
			||||||
 | 
					            response.json, | 
				
			||||||
 | 
					            '{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "1"}'  # noqa | 
				
			||||||
 | 
					        )) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_rpc_call_with_invalid_json(self): | 
				
			||||||
 | 
					        req = '{"jsonrpc": "2.0", "method": "foobar, "params": "bar", "baz]' | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isjsonequal( | 
				
			||||||
 | 
					            response.json, | 
				
			||||||
 | 
					            '{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}'  # noqa | 
				
			||||||
 | 
					        )) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_rpc_call_with_invalid_request_object(self): | 
				
			||||||
 | 
					        req = '{"jsonrpc": "2.0", "method": 1, "params": "bar"}' | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isjsonequal( | 
				
			||||||
 | 
					            response.json, | 
				
			||||||
 | 
					            '{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}'  # noqa | 
				
			||||||
 | 
					        )) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_rpc_call_batch_invalid_json(self): | 
				
			||||||
 | 
					        req = """[ | 
				
			||||||
 | 
					            {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"}, | 
				
			||||||
 | 
					            {"jsonrpc": "2.0", "method" | 
				
			||||||
 | 
					        ]""" | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isjsonequal( | 
				
			||||||
 | 
					            response.json, | 
				
			||||||
 | 
					            '{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}'  # noqa | 
				
			||||||
 | 
					        )) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_rpc_call_with_an_empty_array(self): | 
				
			||||||
 | 
					        req = '[]' | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isjsonequal( | 
				
			||||||
 | 
					            response.json, | 
				
			||||||
 | 
					            '{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}'  # noqa | 
				
			||||||
 | 
					        )) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_rpc_call_with_rpc_call_with_an_invalid_batch_but_not_empty(self): | 
				
			||||||
 | 
					        req = '[1]' | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isjsonequal( | 
				
			||||||
 | 
					            response.json, | 
				
			||||||
 | 
					            '{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}'  # noqa | 
				
			||||||
 | 
					        )) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_rpc_call_with_invalid_batch(self): | 
				
			||||||
 | 
					        req = '[1,2,3]' | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue( | 
				
			||||||
 | 
					            response, | 
				
			||||||
 | 
					            json.loads("""[ | 
				
			||||||
 | 
					                {"jsonrpc": "2.0", "error": {"code": -32600, | 
				
			||||||
 | 
					                "message": "Invalid Request"}, "id": null}, | 
				
			||||||
 | 
					                {"jsonrpc": "2.0", "error": {"code": -32600, | 
				
			||||||
 | 
					                "message": "Invalid Request"}, "id": null}, | 
				
			||||||
 | 
					                {"jsonrpc": "2.0", "error": {"code": -32600, | 
				
			||||||
 | 
					                "message": "Invalid Request"}, "id": null} | 
				
			||||||
 | 
					            ]""") | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_rpc_call_batch(self): | 
				
			||||||
 | 
					        req = """[ | 
				
			||||||
 | 
					            {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"}, | 
				
			||||||
 | 
					            {"jsonrpc": "2.0", "method": "notify_hello", "params": [7]}, | 
				
			||||||
 | 
					            {"jsonrpc": "2.0", "method": "subtract", | 
				
			||||||
 | 
					            "params": [42,23], "id": "2"}, | 
				
			||||||
 | 
					            {"foo": "boo"}, | 
				
			||||||
 | 
					            {"jsonrpc": "2.0", "method": "foo.get", | 
				
			||||||
 | 
					            "params": {"name": "myself"}, "id": "5"}, | 
				
			||||||
 | 
					            {"jsonrpc": "2.0", "method": "get_data", "id": "9"} | 
				
			||||||
 | 
					        ]""" | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue( | 
				
			||||||
 | 
					            response, | 
				
			||||||
 | 
					            json.loads("""[ | 
				
			||||||
 | 
					                {"jsonrpc": "2.0", "result": 7, "id": "1"}, | 
				
			||||||
 | 
					                {"jsonrpc": "2.0", "result": 19, "id": "2"}, | 
				
			||||||
 | 
					                {"jsonrpc": "2.0", "error": {"code": -32600, | 
				
			||||||
 | 
					                "message": "Invalid Request"}, "id": null}, | 
				
			||||||
 | 
					                {"jsonrpc": "2.0", "error": {"code": -32601, | 
				
			||||||
 | 
					                "message": "Method not found"}, "id": "5"}, | 
				
			||||||
 | 
					                {"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"} | 
				
			||||||
 | 
					            ]""") | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_rpc_call_batch_all_notifications(self): | 
				
			||||||
 | 
					        req = """[ | 
				
			||||||
 | 
					            {"jsonrpc": "2.0", "method": "notify_sum", "params": [1,2,4]}, | 
				
			||||||
 | 
					            {"jsonrpc": "2.0", "method": "notify_hello", "params": [7]} | 
				
			||||||
 | 
					        ]""" | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertEqual(response, None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_rpc_call_response_request(self): | 
				
			||||||
 | 
					        req = '{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}'  # noqa | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isinstance( | 
				
			||||||
 | 
					            response.request, | 
				
			||||||
 | 
					            JSONRPC20Request | 
				
			||||||
 | 
					        )) | 
				
			||||||
 | 
					        self.assertTrue(isjsonequal( | 
				
			||||||
 | 
					            response.request.json, | 
				
			||||||
 | 
					            req | 
				
			||||||
 | 
					        )) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_rpc_call_response_request_batch(self): | 
				
			||||||
 | 
					        req = """[ | 
				
			||||||
 | 
					            {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"}, | 
				
			||||||
 | 
					            {"jsonrpc": "2.0", "method": "notify_hello", "params": [7]}, | 
				
			||||||
 | 
					            {"jsonrpc": "2.0", "method": "subtract", | 
				
			||||||
 | 
					            "params": [42,23], "id": "2"}, | 
				
			||||||
 | 
					            {"jsonrpc": "2.0", "method": "foo.get", | 
				
			||||||
 | 
					            "params": {"name": "myself"}, "id": "5"}, | 
				
			||||||
 | 
					            {"jsonrpc": "2.0", "method": "get_data", "id": "9"} | 
				
			||||||
 | 
					        ]""" | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isinstance( | 
				
			||||||
 | 
					            response.request, | 
				
			||||||
 | 
					            JSONRPC20BatchRequest | 
				
			||||||
 | 
					        )) | 
				
			||||||
 | 
					        self.assertTrue(isjsonequal( | 
				
			||||||
 | 
					            response.request.json, | 
				
			||||||
 | 
					            req | 
				
			||||||
 | 
					        )) | 
				
			||||||
@ -0,0 +1 @@ | 
				
			|||||||
 | 
					""" Tets base JSON-RPC structures.""" | 
				
			||||||
@ -0,0 +1,429 @@ | 
				
			|||||||
 | 
					import json | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ..exceptions import JSONRPCInvalidRequestException | 
				
			||||||
 | 
					from ..jsonrpc1 import ( | 
				
			||||||
 | 
					    JSONRPC10Request, | 
				
			||||||
 | 
					    JSONRPC10Response, | 
				
			||||||
 | 
					) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if sys.version_info < (2, 7): | 
				
			||||||
 | 
					    import unittest2 as unittest | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    import unittest | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPC10Request(unittest.TestCase): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Test JSONRPC10Request functionality.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self): | 
				
			||||||
 | 
					        self.request_params = { | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [1, 2], | 
				
			||||||
 | 
					            "_id": 1, | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_correct_init(self): | 
				
			||||||
 | 
					        """ Test object is created.""" | 
				
			||||||
 | 
					        JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validation_incorrect_no_parameters(self): | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC10Request() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_method_validation_str(self): | 
				
			||||||
 | 
					        self.request_params.update({"method": "add"}) | 
				
			||||||
 | 
					        JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_method_validation_not_str(self): | 
				
			||||||
 | 
					        self.request_params.update({"method": []}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.request_params.update({"method": {}}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.request_params.update({"method": None}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_params_validation_list(self): | 
				
			||||||
 | 
					        self.request_params.update({"params": []}) | 
				
			||||||
 | 
					        JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.request_params.update({"params": [0]}) | 
				
			||||||
 | 
					        JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_params_validation_tuple(self): | 
				
			||||||
 | 
					        self.request_params.update({"params": ()}) | 
				
			||||||
 | 
					        JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.request_params.update({"params": tuple([0])}) | 
				
			||||||
 | 
					        JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_params_validation_dict(self): | 
				
			||||||
 | 
					        self.request_params.update({"params": {}}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.request_params.update({"params": {"a": 0}}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_params_validation_none(self): | 
				
			||||||
 | 
					        self.request_params.update({"params": None}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_params_validation_incorrect(self): | 
				
			||||||
 | 
					        self.request_params.update({"params": "str"}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_request_args(self): | 
				
			||||||
 | 
					        self.assertEqual(JSONRPC10Request("add", []).args, ()) | 
				
			||||||
 | 
					        self.assertEqual(JSONRPC10Request("add", [1, 2]).args, (1, 2)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_id_validation_string(self): | 
				
			||||||
 | 
					        self.request_params.update({"_id": "id"}) | 
				
			||||||
 | 
					        JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_id_validation_int(self): | 
				
			||||||
 | 
					        self.request_params.update({"_id": 0}) | 
				
			||||||
 | 
					        JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_id_validation_null(self): | 
				
			||||||
 | 
					        self.request_params.update({"_id": "null"}) | 
				
			||||||
 | 
					        JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_id_validation_none(self): | 
				
			||||||
 | 
					        self.request_params.update({"_id": None}) | 
				
			||||||
 | 
					        JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_id_validation_float(self): | 
				
			||||||
 | 
					        self.request_params.update({"_id": 0.1}) | 
				
			||||||
 | 
					        JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_id_validation_list_tuple(self): | 
				
			||||||
 | 
					        self.request_params.update({"_id": []}) | 
				
			||||||
 | 
					        JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.request_params.update({"_id": ()}) | 
				
			||||||
 | 
					        JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_id_validation_default_id_none(self): | 
				
			||||||
 | 
					        del self.request_params["_id"] | 
				
			||||||
 | 
					        JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_method_1(self): | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", []) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [], | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_method_2(self): | 
				
			||||||
 | 
					        r = JSONRPC10Request(method="add", params=[]) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [], | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_params_1(self): | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", params=[], _id=None) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [], | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_params_2(self): | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", ()) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [], | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_params_3(self): | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", (1, 2)) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [1, 2], | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_id_1(self): | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", [], _id="null") | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [], | 
				
			||||||
 | 
					            "id": "null", | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_id_1_notification(self): | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", [], _id="null", is_notification=True) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [], | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_id_2(self): | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", [], _id=None) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [], | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_id_2_notification(self): | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", [], _id=None, is_notification=True) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [], | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_id_3(self): | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", [], _id="id") | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [], | 
				
			||||||
 | 
					            "id": "id", | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_id_3_notification(self): | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", [], _id="id", is_notification=True) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [], | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_id_4(self): | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", [], _id=0) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [], | 
				
			||||||
 | 
					            "id": 0, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_id_4_notification(self): | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", [], _id=0, is_notification=True) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [], | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_is_notification(self): | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", []) | 
				
			||||||
 | 
					        self.assertTrue(r.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", [], _id=None) | 
				
			||||||
 | 
					        self.assertTrue(r.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", [], _id="null") | 
				
			||||||
 | 
					        self.assertFalse(r.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", [], _id=0) | 
				
			||||||
 | 
					        self.assertFalse(r.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", [], is_notification=True) | 
				
			||||||
 | 
					        self.assertTrue(r.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", [], is_notification=True, _id=None) | 
				
			||||||
 | 
					        self.assertTrue(r.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", [], is_notification=True, _id=0) | 
				
			||||||
 | 
					        self.assertTrue(r.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_set_unset_notification_keep_id(self): | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", [], is_notification=True, _id=0) | 
				
			||||||
 | 
					        self.assertTrue(r.is_notification) | 
				
			||||||
 | 
					        self.assertEqual(r.data["id"], None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        r.is_notification = False | 
				
			||||||
 | 
					        self.assertFalse(r.is_notification) | 
				
			||||||
 | 
					        self.assertEqual(r.data["id"], 0) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_error_if_notification_true_but_id_none(self): | 
				
			||||||
 | 
					        r = JSONRPC10Request("add", [], is_notification=True, _id=None) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            r.is_notification = False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json_invalid_request_method(self): | 
				
			||||||
 | 
					        str_json = json.dumps({ | 
				
			||||||
 | 
					            "params": [1, 2], | 
				
			||||||
 | 
					            "id": 0, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(JSONRPCInvalidRequestException): | 
				
			||||||
 | 
					            JSONRPC10Request.from_json(str_json) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json_invalid_request_params(self): | 
				
			||||||
 | 
					        str_json = json.dumps({ | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "id": 0, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(JSONRPCInvalidRequestException): | 
				
			||||||
 | 
					            JSONRPC10Request.from_json(str_json) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json_invalid_request_id(self): | 
				
			||||||
 | 
					        str_json = json.dumps({ | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [1, 2], | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(JSONRPCInvalidRequestException): | 
				
			||||||
 | 
					            JSONRPC10Request.from_json(str_json) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json_invalid_request_extra_data(self): | 
				
			||||||
 | 
					        str_json = json.dumps({ | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [1, 2], | 
				
			||||||
 | 
					            "id": 0, | 
				
			||||||
 | 
					            "is_notification": True, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(JSONRPCInvalidRequestException): | 
				
			||||||
 | 
					            JSONRPC10Request.from_json(str_json) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json_request(self): | 
				
			||||||
 | 
					        str_json = json.dumps({ | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [1, 2], | 
				
			||||||
 | 
					            "id": 0, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        request = JSONRPC10Request.from_json(str_json) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(request, JSONRPC10Request)) | 
				
			||||||
 | 
					        self.assertEqual(request.method, "add") | 
				
			||||||
 | 
					        self.assertEqual(request.params, [1, 2]) | 
				
			||||||
 | 
					        self.assertEqual(request._id, 0) | 
				
			||||||
 | 
					        self.assertFalse(request.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json_request_notification(self): | 
				
			||||||
 | 
					        str_json = json.dumps({ | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [1, 2], | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        request = JSONRPC10Request.from_json(str_json) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(request, JSONRPC10Request)) | 
				
			||||||
 | 
					        self.assertEqual(request.method, "add") | 
				
			||||||
 | 
					        self.assertEqual(request.params, [1, 2]) | 
				
			||||||
 | 
					        self.assertEqual(request._id, None) | 
				
			||||||
 | 
					        self.assertTrue(request.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json_string_not_dict(self): | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC10Request.from_json("[]") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC10Request.from_json("0") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_setter(self): | 
				
			||||||
 | 
					        request = JSONRPC10Request(**self.request_params) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            request.data = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            request.data = "" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            request.data = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPC10Response(unittest.TestCase): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Test JSONRPC10Response functionality.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self): | 
				
			||||||
 | 
					        self.response_success_params = { | 
				
			||||||
 | 
					            "result": "", | 
				
			||||||
 | 
					            "error": None, | 
				
			||||||
 | 
					            "_id": 1, | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					        self.response_error_params = { | 
				
			||||||
 | 
					            "result": None, | 
				
			||||||
 | 
					            "error": { | 
				
			||||||
 | 
					                "code": 1, | 
				
			||||||
 | 
					                "message": "error", | 
				
			||||||
 | 
					            }, | 
				
			||||||
 | 
					            "_id": 1, | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_correct_init(self): | 
				
			||||||
 | 
					        """ Test object is created.""" | 
				
			||||||
 | 
					        JSONRPC10Response(**self.response_success_params) | 
				
			||||||
 | 
					        JSONRPC10Response(**self.response_error_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validation_incorrect_no_parameters(self): | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC10Response() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validation_success_incorrect(self): | 
				
			||||||
 | 
					        wrong_params = self.response_success_params | 
				
			||||||
 | 
					        del wrong_params["_id"] | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC10Response(**wrong_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validation_error_incorrect(self): | 
				
			||||||
 | 
					        wrong_params = self.response_error_params | 
				
			||||||
 | 
					        del wrong_params["_id"] | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC10Response(**wrong_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _test_validation_incorrect_result_and_error(self): | 
				
			||||||
 | 
					        # @todo: remove | 
				
			||||||
 | 
					        # It is OK because result is an mepty string, it is still result | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC10Response(result="", error="", _id=0) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = JSONRPC10Response(error="", _id=0) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            response.result = "" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data(self): | 
				
			||||||
 | 
					        r = JSONRPC10Response(result="", _id=0) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "result": "", | 
				
			||||||
 | 
					            "id": 0, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_setter(self): | 
				
			||||||
 | 
					        response = JSONRPC10Response(**self.response_success_params) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            response.data = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            response.data = "" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            response.data = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validation_id(self): | 
				
			||||||
 | 
					        response = JSONRPC10Response(**self.response_success_params) | 
				
			||||||
 | 
					        self.assertEqual(response._id, self.response_success_params["_id"]) | 
				
			||||||
@ -0,0 +1,728 @@ | 
				
			|||||||
 | 
					import json | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ..exceptions import JSONRPCInvalidRequestException | 
				
			||||||
 | 
					from ..jsonrpc2 import ( | 
				
			||||||
 | 
					    JSONRPC20Request, | 
				
			||||||
 | 
					    JSONRPC20BatchRequest, | 
				
			||||||
 | 
					    JSONRPC20Response, | 
				
			||||||
 | 
					    JSONRPC20BatchResponse, | 
				
			||||||
 | 
					) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if sys.version_info < (2, 7): | 
				
			||||||
 | 
					    import unittest2 as unittest | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    import unittest | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPC20Request(unittest.TestCase): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Test JSONRPC20Request functionality.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self): | 
				
			||||||
 | 
					        self.request_params = { | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [1, 2], | 
				
			||||||
 | 
					            "_id": 1, | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_correct_init(self): | 
				
			||||||
 | 
					        """ Test object is created.""" | 
				
			||||||
 | 
					        JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validation_incorrect_no_parameters(self): | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC20Request() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_method_validation_str(self): | 
				
			||||||
 | 
					        self.request_params.update({"method": "add"}) | 
				
			||||||
 | 
					        JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_method_validation_not_str(self): | 
				
			||||||
 | 
					        self.request_params.update({"method": []}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.request_params.update({"method": {}}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_method_validation_str_rpc_prefix(self): | 
				
			||||||
 | 
					        """ Test method SHOULD NOT starts with rpc. """ | 
				
			||||||
 | 
					        self.request_params.update({"method": "rpc."}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.request_params.update({"method": "rpc.test"}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.request_params.update({"method": "rpccorrect"}) | 
				
			||||||
 | 
					        JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.request_params.update({"method": "rpc"}) | 
				
			||||||
 | 
					        JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_params_validation_list(self): | 
				
			||||||
 | 
					        self.request_params.update({"params": []}) | 
				
			||||||
 | 
					        JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.request_params.update({"params": [0]}) | 
				
			||||||
 | 
					        JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_params_validation_tuple(self): | 
				
			||||||
 | 
					        self.request_params.update({"params": ()}) | 
				
			||||||
 | 
					        JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.request_params.update({"params": tuple([0])}) | 
				
			||||||
 | 
					        JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_params_validation_dict(self): | 
				
			||||||
 | 
					        self.request_params.update({"params": {}}) | 
				
			||||||
 | 
					        JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.request_params.update({"params": {"a": 0}}) | 
				
			||||||
 | 
					        JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_params_validation_none(self): | 
				
			||||||
 | 
					        self.request_params.update({"params": None}) | 
				
			||||||
 | 
					        JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_params_validation_incorrect(self): | 
				
			||||||
 | 
					        self.request_params.update({"params": "str"}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_request_args(self): | 
				
			||||||
 | 
					        self.assertEqual(JSONRPC20Request("add").args, ()) | 
				
			||||||
 | 
					        self.assertEqual(JSONRPC20Request("add", []).args, ()) | 
				
			||||||
 | 
					        self.assertEqual(JSONRPC20Request("add", {"a": 1}).args, ()) | 
				
			||||||
 | 
					        self.assertEqual(JSONRPC20Request("add", [1, 2]).args, (1, 2)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_request_kwargs(self): | 
				
			||||||
 | 
					        self.assertEqual(JSONRPC20Request("add").kwargs, {}) | 
				
			||||||
 | 
					        self.assertEqual(JSONRPC20Request("add", [1, 2]).kwargs, {}) | 
				
			||||||
 | 
					        self.assertEqual(JSONRPC20Request("add", {}).kwargs, {}) | 
				
			||||||
 | 
					        self.assertEqual(JSONRPC20Request("add", {"a": 1}).kwargs, {"a": 1}) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_id_validation_string(self): | 
				
			||||||
 | 
					        self.request_params.update({"_id": "id"}) | 
				
			||||||
 | 
					        JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_id_validation_int(self): | 
				
			||||||
 | 
					        self.request_params.update({"_id": 0}) | 
				
			||||||
 | 
					        JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_id_validation_null(self): | 
				
			||||||
 | 
					        self.request_params.update({"_id": "null"}) | 
				
			||||||
 | 
					        JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_id_validation_none(self): | 
				
			||||||
 | 
					        self.request_params.update({"_id": None}) | 
				
			||||||
 | 
					        JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_id_validation_float(self): | 
				
			||||||
 | 
					        self.request_params.update({"_id": 0.1}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_id_validation_incorrect(self): | 
				
			||||||
 | 
					        self.request_params.update({"_id": []}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.request_params.update({"_id": ()}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_method_1(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add") | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_method_2(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request(method="add") | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_method_3(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", None) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_params_1(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", params=None, _id=None) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_params_2(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", []) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [], | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_params_3(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", ()) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [], | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_params_4(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", (1, 2)) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [1, 2], | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_params_5(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", {"a": 0}) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": {"a": 0}, | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_id_1(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", _id="null") | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "id": "null", | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_id_1_notification(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", _id="null", is_notification=True) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_id_2(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", _id=None) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_id_2_notification(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", _id=None, is_notification=True) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_id_3(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", _id="id") | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "id": "id", | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_id_3_notification(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", _id="id", is_notification=True) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_id_4(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", _id=0) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "id": 0, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_id_4_notification(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", _id=0, is_notification=True) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_is_notification(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add") | 
				
			||||||
 | 
					        self.assertFalse(r.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", _id=None) | 
				
			||||||
 | 
					        self.assertFalse(r.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", _id="null") | 
				
			||||||
 | 
					        self.assertFalse(r.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", _id=0) | 
				
			||||||
 | 
					        self.assertFalse(r.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", is_notification=True) | 
				
			||||||
 | 
					        self.assertTrue(r.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", is_notification=True, _id=None) | 
				
			||||||
 | 
					        self.assertTrue(r.is_notification) | 
				
			||||||
 | 
					        self.assertNotIn("id", r.data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", is_notification=True, _id=0) | 
				
			||||||
 | 
					        self.assertTrue(r.is_notification) | 
				
			||||||
 | 
					        self.assertNotIn("id", r.data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_set_unset_notification_keep_id(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", is_notification=True, _id=0) | 
				
			||||||
 | 
					        self.assertTrue(r.is_notification) | 
				
			||||||
 | 
					        self.assertFalse("id" in r.data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        r.is_notification = False | 
				
			||||||
 | 
					        self.assertFalse(r.is_notification) | 
				
			||||||
 | 
					        self.assertTrue("id" in r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data["id"], 0) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_serialize_method_1(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add") | 
				
			||||||
 | 
					        self.assertTrue({ | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }, json.loads(r.json)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_serialize_method_2(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request(method="add") | 
				
			||||||
 | 
					        self.assertTrue({ | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }, json.loads(r.json)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_serialize_method_3(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", None) | 
				
			||||||
 | 
					        self.assertTrue({ | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }, json.loads(r.json)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_serialize_params_1(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", params=None, _id=None) | 
				
			||||||
 | 
					        self.assertTrue({ | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }, json.loads(r.json)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_serialize_params_2(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", []) | 
				
			||||||
 | 
					        self.assertTrue({ | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [], | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }, json.loads(r.json)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_serialize_params_3(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", ()) | 
				
			||||||
 | 
					        self.assertTrue({ | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [], | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }, json.loads(r.json)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_serialize_params_4(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", (1, 2)) | 
				
			||||||
 | 
					        self.assertTrue({ | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [1, 2], | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }, json.loads(r.json)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_serialize_params_5(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", {"a": 0}) | 
				
			||||||
 | 
					        self.assertTrue({ | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": {"a": 0}, | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }, json.loads(r.json)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_serialize_id_1(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", _id="null") | 
				
			||||||
 | 
					        self.assertTrue({ | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "id": "null", | 
				
			||||||
 | 
					        }, json.loads(r.json)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_serialize_id_2(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", _id=None) | 
				
			||||||
 | 
					        self.assertTrue({ | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }, json.loads(r.json)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_serialize_id_3(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", _id="id") | 
				
			||||||
 | 
					        self.assertTrue({ | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "id": "id", | 
				
			||||||
 | 
					        }, json.loads(r.json)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_serialize_id_4(self): | 
				
			||||||
 | 
					        r = JSONRPC20Request("add", _id=0) | 
				
			||||||
 | 
					        self.assertTrue({ | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "id": 0, | 
				
			||||||
 | 
					        }, json.loads(r.json)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json_request_no_id(self): | 
				
			||||||
 | 
					        str_json = json.dumps({ | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [1, 2], | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        request = JSONRPC20Request.from_json(str_json) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(request, JSONRPC20Request)) | 
				
			||||||
 | 
					        self.assertEqual(request.method, "add") | 
				
			||||||
 | 
					        self.assertEqual(request.params, [1, 2]) | 
				
			||||||
 | 
					        self.assertEqual(request._id, None) | 
				
			||||||
 | 
					        self.assertTrue(request.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json_request_no_params(self): | 
				
			||||||
 | 
					        str_json = json.dumps({ | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        request = JSONRPC20Request.from_json(str_json) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(request, JSONRPC20Request)) | 
				
			||||||
 | 
					        self.assertEqual(request.method, "add") | 
				
			||||||
 | 
					        self.assertEqual(request.params, None) | 
				
			||||||
 | 
					        self.assertEqual(request._id, None) | 
				
			||||||
 | 
					        self.assertTrue(request.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json_request_null_id(self): | 
				
			||||||
 | 
					        str_json = json.dumps({ | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        request = JSONRPC20Request.from_json(str_json) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(request, JSONRPC20Request)) | 
				
			||||||
 | 
					        self.assertEqual(request.method, "add") | 
				
			||||||
 | 
					        self.assertEqual(request.params, None) | 
				
			||||||
 | 
					        self.assertEqual(request._id, None) | 
				
			||||||
 | 
					        self.assertFalse(request.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json_request(self): | 
				
			||||||
 | 
					        str_json = json.dumps({ | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "params": [0, 1], | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "id": "id", | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        request = JSONRPC20Request.from_json(str_json) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(request, JSONRPC20Request)) | 
				
			||||||
 | 
					        self.assertEqual(request.method, "add") | 
				
			||||||
 | 
					        self.assertEqual(request.params, [0, 1]) | 
				
			||||||
 | 
					        self.assertEqual(request._id, "id") | 
				
			||||||
 | 
					        self.assertFalse(request.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json_invalid_request_jsonrpc(self): | 
				
			||||||
 | 
					        str_json = json.dumps({ | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(JSONRPCInvalidRequestException): | 
				
			||||||
 | 
					            JSONRPC20Request.from_json(str_json) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json_invalid_request_method(self): | 
				
			||||||
 | 
					        str_json = json.dumps({ | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(JSONRPCInvalidRequestException): | 
				
			||||||
 | 
					            JSONRPC20Request.from_json(str_json) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json_invalid_request_extra_data(self): | 
				
			||||||
 | 
					        str_json = json.dumps({ | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "method": "add", | 
				
			||||||
 | 
					            "is_notification": True, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(JSONRPCInvalidRequestException): | 
				
			||||||
 | 
					            JSONRPC20Request.from_json(str_json) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_setter(self): | 
				
			||||||
 | 
					        request = JSONRPC20Request(**self.request_params) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            request.data = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            request.data = "" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            request.data = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPC20BatchRequest(unittest.TestCase): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Test JSONRPC20BatchRequest functionality.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_batch_request(self): | 
				
			||||||
 | 
					        request = JSONRPC20BatchRequest( | 
				
			||||||
 | 
					            JSONRPC20Request("devide", {"num": 1, "denom": 2}, _id=1), | 
				
			||||||
 | 
					            JSONRPC20Request("devide", {"num": 3, "denom": 2}, _id=2), | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(request.json), [ | 
				
			||||||
 | 
					            {"method": "devide", "params": {"num": 1, "denom": 2}, "id": 1, | 
				
			||||||
 | 
					             "jsonrpc": "2.0"}, | 
				
			||||||
 | 
					            {"method": "devide", "params": {"num": 3, "denom": 2}, "id": 2, | 
				
			||||||
 | 
					             "jsonrpc": "2.0"}, | 
				
			||||||
 | 
					        ]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json_batch(self): | 
				
			||||||
 | 
					        str_json = json.dumps([ | 
				
			||||||
 | 
					            {"method": "add", "params": [1, 2], "jsonrpc": "2.0"}, | 
				
			||||||
 | 
					            {"method": "mul", "params": [1, 2], "jsonrpc": "2.0"}, | 
				
			||||||
 | 
					        ]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        requests = JSONRPC20BatchRequest.from_json(str_json) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(requests, JSONRPC20BatchRequest)) | 
				
			||||||
 | 
					        for r in requests: | 
				
			||||||
 | 
					            self.assertTrue(isinstance(r, JSONRPC20Request)) | 
				
			||||||
 | 
					            self.assertTrue(r.method in ["add", "mul"]) | 
				
			||||||
 | 
					            self.assertEqual(r.params, [1, 2]) | 
				
			||||||
 | 
					            self.assertEqual(r._id, None) | 
				
			||||||
 | 
					            self.assertTrue(r.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json_batch_one(self): | 
				
			||||||
 | 
					        str_json = json.dumps([ | 
				
			||||||
 | 
					            {"method": "add", "params": [1, 2], "jsonrpc": "2.0", "id": None}, | 
				
			||||||
 | 
					        ]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        requests = JSONRPC20Request.from_json(str_json) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(requests, JSONRPC20BatchRequest)) | 
				
			||||||
 | 
					        requests = list(requests) | 
				
			||||||
 | 
					        self.assertEqual(len(requests), 1) | 
				
			||||||
 | 
					        r = requests[0] | 
				
			||||||
 | 
					        self.assertTrue(isinstance(r, JSONRPC20Request)) | 
				
			||||||
 | 
					        self.assertEqual(r.method, "add") | 
				
			||||||
 | 
					        self.assertEqual(r.params, [1, 2]) | 
				
			||||||
 | 
					        self.assertEqual(r._id, None) | 
				
			||||||
 | 
					        self.assertFalse(r.is_notification) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_response_iterator(self): | 
				
			||||||
 | 
					        requests = JSONRPC20BatchRequest( | 
				
			||||||
 | 
					            JSONRPC20Request("devide", {"num": 1, "denom": 2}, _id=1), | 
				
			||||||
 | 
					            JSONRPC20Request("devide", {"num": 3, "denom": 2}, _id=2), | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        for request in requests: | 
				
			||||||
 | 
					            self.assertTrue(isinstance(request, JSONRPC20Request)) | 
				
			||||||
 | 
					            self.assertEqual(request.method, "devide") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPC20Response(unittest.TestCase): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Test JSONRPC20Response functionality.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self): | 
				
			||||||
 | 
					        self.response_success_params = { | 
				
			||||||
 | 
					            "result": "", | 
				
			||||||
 | 
					            "_id": 1, | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					        self.response_error_params = { | 
				
			||||||
 | 
					            "error": { | 
				
			||||||
 | 
					                "code": 1, | 
				
			||||||
 | 
					                "message": "error", | 
				
			||||||
 | 
					            }, | 
				
			||||||
 | 
					            "_id": 1, | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_correct_init(self): | 
				
			||||||
 | 
					        """ Test object is created.""" | 
				
			||||||
 | 
					        JSONRPC20Response(**self.response_success_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validation_incorrect_no_parameters(self): | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC20Response() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validation_incorrect_result_and_error(self): | 
				
			||||||
 | 
					        response = JSONRPC20Response(error={"code": 1, "message": ""}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            response.result = "" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validation_error_correct(self): | 
				
			||||||
 | 
					        JSONRPC20Response(**self.response_error_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validation_error_incorrect(self): | 
				
			||||||
 | 
					        self.response_error_params["error"].update({"code": "str"}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC20Response(**self.response_error_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validation_error_incorrect_no_code(self): | 
				
			||||||
 | 
					        del self.response_error_params["error"]["code"] | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC20Response(**self.response_error_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validation_error_incorrect_no_message(self): | 
				
			||||||
 | 
					        del self.response_error_params["error"]["message"] | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC20Response(**self.response_error_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validation_error_incorrect_message_not_str(self): | 
				
			||||||
 | 
					        self.response_error_params["error"].update({"message": 0}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPC20Response(**self.response_error_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validation_id(self): | 
				
			||||||
 | 
					        response = JSONRPC20Response(**self.response_success_params) | 
				
			||||||
 | 
					        self.assertEqual(response._id, self.response_success_params["_id"]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validation_id_incorrect_type(self): | 
				
			||||||
 | 
					        response = JSONRPC20Response(**self.response_success_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            response._id = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            response._id = {} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            response._id = 0.1 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_result(self): | 
				
			||||||
 | 
					        r = JSONRPC20Response(result="") | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "result": "", | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_result_id_none(self): | 
				
			||||||
 | 
					        r = JSONRPC20Response(result="", _id=None) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "result": "", | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_result_id(self): | 
				
			||||||
 | 
					        r = JSONRPC20Response(result="", _id=0) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "result": "", | 
				
			||||||
 | 
					            "id": 0, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_error(self): | 
				
			||||||
 | 
					        r = JSONRPC20Response(error={"code": 0, "message": ""}) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "error": { | 
				
			||||||
 | 
					                "code": 0, | 
				
			||||||
 | 
					                "message": "", | 
				
			||||||
 | 
					            }, | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_error_id_none(self): | 
				
			||||||
 | 
					        r = JSONRPC20Response(error={"code": 0, "message": ""}, _id=None) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "error": { | 
				
			||||||
 | 
					                "code": 0, | 
				
			||||||
 | 
					                "message": "", | 
				
			||||||
 | 
					            }, | 
				
			||||||
 | 
					            "id": None, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_error_id(self): | 
				
			||||||
 | 
					        r = JSONRPC20Response(error={"code": 0, "message": ""}, _id=0) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(r.json), r.data) | 
				
			||||||
 | 
					        self.assertEqual(r.data, { | 
				
			||||||
 | 
					            "jsonrpc": "2.0", | 
				
			||||||
 | 
					            "error": { | 
				
			||||||
 | 
					                "code": 0, | 
				
			||||||
 | 
					                "message": "", | 
				
			||||||
 | 
					            }, | 
				
			||||||
 | 
					            "id": 0, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_setter(self): | 
				
			||||||
 | 
					        response = JSONRPC20Response(**self.response_success_params) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            response.data = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            response.data = "" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            response.data = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPC20BatchResponse(unittest.TestCase): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Test JSONRPC20BatchResponse functionality.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_batch_response(self): | 
				
			||||||
 | 
					        response = JSONRPC20BatchResponse( | 
				
			||||||
 | 
					            JSONRPC20Response(result="result", _id=1), | 
				
			||||||
 | 
					            JSONRPC20Response(error={"code": 0, "message": ""}, _id=2), | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        self.assertEqual(json.loads(response.json), [ | 
				
			||||||
 | 
					            {"result": "result", "id": 1, "jsonrpc": "2.0"}, | 
				
			||||||
 | 
					            {"error": {"code": 0, "message": ""}, "id": 2, "jsonrpc": "2.0"}, | 
				
			||||||
 | 
					        ]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_response_iterator(self): | 
				
			||||||
 | 
					        responses = JSONRPC20BatchResponse( | 
				
			||||||
 | 
					            JSONRPC20Response(result="result", _id=1), | 
				
			||||||
 | 
					            JSONRPC20Response(result="result", _id=2), | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        for response in responses: | 
				
			||||||
 | 
					            self.assertTrue(isinstance(response, JSONRPC20Response)) | 
				
			||||||
 | 
					            self.assertEqual(response.result, "result") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_batch_response_data(self): | 
				
			||||||
 | 
					        response = JSONRPC20BatchResponse( | 
				
			||||||
 | 
					            JSONRPC20Response(result="result", _id=1), | 
				
			||||||
 | 
					            JSONRPC20Response(result="result", _id=2), | 
				
			||||||
 | 
					            JSONRPC20Response(result="result"), | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        self.assertEqual(response.data, [ | 
				
			||||||
 | 
					            {"id": 1, "jsonrpc": "2.0", "result": "result"}, | 
				
			||||||
 | 
					            {"id": 2, "jsonrpc": "2.0", "result": "result"}, | 
				
			||||||
 | 
					            {"id": None, "jsonrpc": "2.0", "result": "result"}, | 
				
			||||||
 | 
					        ]) | 
				
			||||||
@ -0,0 +1,150 @@ | 
				
			|||||||
 | 
					import json | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ..exceptions import ( | 
				
			||||||
 | 
					    JSONRPCError, | 
				
			||||||
 | 
					    JSONRPCInternalError, | 
				
			||||||
 | 
					    JSONRPCInvalidParams, | 
				
			||||||
 | 
					    JSONRPCInvalidRequest, | 
				
			||||||
 | 
					    JSONRPCMethodNotFound, | 
				
			||||||
 | 
					    JSONRPCParseError, | 
				
			||||||
 | 
					    JSONRPCServerError, | 
				
			||||||
 | 
					    JSONRPCDispatchException, | 
				
			||||||
 | 
					) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if sys.version_info < (2, 7): | 
				
			||||||
 | 
					    import unittest2 as unittest | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    import unittest | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPCError(unittest.TestCase): | 
				
			||||||
 | 
					    def setUp(self): | 
				
			||||||
 | 
					        self.error_params = { | 
				
			||||||
 | 
					            "code": 0, | 
				
			||||||
 | 
					            "message": "", | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_correct_init(self): | 
				
			||||||
 | 
					        """ Test object is created.""" | 
				
			||||||
 | 
					        JSONRPCError(**self.error_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validation_incorrect_no_parameters(self): | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPCError() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_code_validation_int(self): | 
				
			||||||
 | 
					        self.error_params.update({"code": 32000}) | 
				
			||||||
 | 
					        JSONRPCError(**self.error_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_code_validation_no_code(self): | 
				
			||||||
 | 
					        del self.error_params["code"] | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPCError(**self.error_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_code_validation_str(self): | 
				
			||||||
 | 
					        self.error_params.update({"code": "0"}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPCError(**self.error_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_message_validation_str(self): | 
				
			||||||
 | 
					        self.error_params.update({"message": ""}) | 
				
			||||||
 | 
					        JSONRPCError(**self.error_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_message_validation_none(self): | 
				
			||||||
 | 
					        del self.error_params["message"] | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPCError(**self.error_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_message_validation_int(self): | 
				
			||||||
 | 
					        self.error_params.update({"message": 0}) | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            JSONRPCError(**self.error_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_validation_none(self): | 
				
			||||||
 | 
					        self.error_params.update({"data": None}) | 
				
			||||||
 | 
					        JSONRPCError(**self.error_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_data_validation(self): | 
				
			||||||
 | 
					        self.error_params.update({"data": {}}) | 
				
			||||||
 | 
					        JSONRPCError(**self.error_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.error_params.update({"data": ""}) | 
				
			||||||
 | 
					        JSONRPCError(**self.error_params) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_json(self): | 
				
			||||||
 | 
					        error = JSONRPCError(**self.error_params) | 
				
			||||||
 | 
					        self.assertEqual( | 
				
			||||||
 | 
					            json.loads(error.json), | 
				
			||||||
 | 
					            self.error_params, | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json(self): | 
				
			||||||
 | 
					        str_json = json.dumps({ | 
				
			||||||
 | 
					            "code": 0, | 
				
			||||||
 | 
					            "message": "", | 
				
			||||||
 | 
					            "data": {}, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        request = JSONRPCError.from_json(str_json) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(request, JSONRPCError)) | 
				
			||||||
 | 
					        self.assertEqual(request.code, 0) | 
				
			||||||
 | 
					        self.assertEqual(request.message, "") | 
				
			||||||
 | 
					        self.assertEqual(request.data, {}) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPCParseError(unittest.TestCase): | 
				
			||||||
 | 
					    def test_code_message(self): | 
				
			||||||
 | 
					        error = JSONRPCParseError() | 
				
			||||||
 | 
					        self.assertEqual(error.code, -32700) | 
				
			||||||
 | 
					        self.assertEqual(error.message, "Parse error") | 
				
			||||||
 | 
					        self.assertEqual(error.data, None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPCServerError(unittest.TestCase): | 
				
			||||||
 | 
					    def test_code_message(self): | 
				
			||||||
 | 
					        error = JSONRPCServerError() | 
				
			||||||
 | 
					        self.assertEqual(error.code, -32000) | 
				
			||||||
 | 
					        self.assertEqual(error.message, "Server error") | 
				
			||||||
 | 
					        self.assertEqual(error.data, None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPCInternalError(unittest.TestCase): | 
				
			||||||
 | 
					    def test_code_message(self): | 
				
			||||||
 | 
					        error = JSONRPCInternalError() | 
				
			||||||
 | 
					        self.assertEqual(error.code, -32603) | 
				
			||||||
 | 
					        self.assertEqual(error.message, "Internal error") | 
				
			||||||
 | 
					        self.assertEqual(error.data, None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPCInvalidParams(unittest.TestCase): | 
				
			||||||
 | 
					    def test_code_message(self): | 
				
			||||||
 | 
					        error = JSONRPCInvalidParams() | 
				
			||||||
 | 
					        self.assertEqual(error.code, -32602) | 
				
			||||||
 | 
					        self.assertEqual(error.message, "Invalid params") | 
				
			||||||
 | 
					        self.assertEqual(error.data, None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPCInvalidRequest(unittest.TestCase): | 
				
			||||||
 | 
					    def test_code_message(self): | 
				
			||||||
 | 
					        error = JSONRPCInvalidRequest() | 
				
			||||||
 | 
					        self.assertEqual(error.code, -32600) | 
				
			||||||
 | 
					        self.assertEqual(error.message, "Invalid Request") | 
				
			||||||
 | 
					        self.assertEqual(error.data, None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPCMethodNotFound(unittest.TestCase): | 
				
			||||||
 | 
					    def test_code_message(self): | 
				
			||||||
 | 
					        error = JSONRPCMethodNotFound() | 
				
			||||||
 | 
					        self.assertEqual(error.code, -32601) | 
				
			||||||
 | 
					        self.assertEqual(error.message, "Method not found") | 
				
			||||||
 | 
					        self.assertEqual(error.data, None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPCDispatchException(unittest.TestCase): | 
				
			||||||
 | 
					    def test_code_message(self): | 
				
			||||||
 | 
					        error = JSONRPCDispatchException(message="message", | 
				
			||||||
 | 
					                                         code=400, data={"param": 1}) | 
				
			||||||
 | 
					        self.assertEqual(error.error.code, 400) | 
				
			||||||
 | 
					        self.assertEqual(error.error.message, "message") | 
				
			||||||
 | 
					        self.assertEqual(error.error.data, {"param": 1}) | 
				
			||||||
@ -0,0 +1,175 @@ | 
				
			|||||||
 | 
					import sys | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ..manager import JSONRPCResponseManager | 
				
			||||||
 | 
					from ..jsonrpc2 import ( | 
				
			||||||
 | 
					    JSONRPC20BatchRequest, | 
				
			||||||
 | 
					    JSONRPC20BatchResponse, | 
				
			||||||
 | 
					    JSONRPC20Request, | 
				
			||||||
 | 
					    JSONRPC20Response, | 
				
			||||||
 | 
					) | 
				
			||||||
 | 
					from ..jsonrpc1 import JSONRPC10Request, JSONRPC10Response | 
				
			||||||
 | 
					from ..exceptions import JSONRPCDispatchException | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if sys.version_info < (3, 3): | 
				
			||||||
 | 
					    from mock import MagicMock | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    from unittest.mock import MagicMock | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if sys.version_info < (2, 7): | 
				
			||||||
 | 
					    import unittest2 as unittest | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    import unittest | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPCResponseManager(unittest.TestCase): | 
				
			||||||
 | 
					    def setUp(self): | 
				
			||||||
 | 
					        def raise_(e): | 
				
			||||||
 | 
					            raise e | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.long_time_method = MagicMock() | 
				
			||||||
 | 
					        self.dispatcher = { | 
				
			||||||
 | 
					            "add": sum, | 
				
			||||||
 | 
					            "multiply": lambda a, b: a * b, | 
				
			||||||
 | 
					            "list_len": len, | 
				
			||||||
 | 
					            "101_base": lambda **kwargs: int("101", **kwargs), | 
				
			||||||
 | 
					            "error": lambda: raise_(KeyError("error_explanation")), | 
				
			||||||
 | 
					            "type_error": lambda: raise_(TypeError("TypeError inside method")), | 
				
			||||||
 | 
					            "long_time_method": self.long_time_method, | 
				
			||||||
 | 
					            "dispatch_error": lambda x: raise_( | 
				
			||||||
 | 
					                JSONRPCDispatchException(code=4000, message="error", | 
				
			||||||
 | 
					                                         data={"param": 1})), | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_dispatch_error(self): | 
				
			||||||
 | 
					        request = JSONRPC20Request("dispatch_error", ["test"], _id=0) | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(request.json, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(response, JSONRPC20Response)) | 
				
			||||||
 | 
					        self.assertEqual(response.error["message"], "error") | 
				
			||||||
 | 
					        self.assertEqual(response.error["code"], 4000) | 
				
			||||||
 | 
					        self.assertEqual(response.error["data"], {"param": 1}) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_returned_type_response(self): | 
				
			||||||
 | 
					        request = JSONRPC20Request("add", [[]], _id=0) | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(request.json, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(response, JSONRPC20Response)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_returned_type_butch_response(self): | 
				
			||||||
 | 
					        request = JSONRPC20BatchRequest( | 
				
			||||||
 | 
					            JSONRPC20Request("add", [[]], _id=0)) | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(request.json, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(response, JSONRPC20BatchResponse)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_returned_type_response_rpc10(self): | 
				
			||||||
 | 
					        request = JSONRPC10Request("add", [[]], _id=0) | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(request.json, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(response, JSONRPC10Response)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_parse_error(self): | 
				
			||||||
 | 
					        req = '{"jsonrpc": "2.0", "method": "foobar, "params": "bar", "baz]' | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(response, JSONRPC20Response)) | 
				
			||||||
 | 
					        self.assertEqual(response.error["message"], "Parse error") | 
				
			||||||
 | 
					        self.assertEqual(response.error["code"], -32700) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_invalid_request(self): | 
				
			||||||
 | 
					        req = '{"jsonrpc": "2.0", "method": 1, "params": "bar"}' | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(req, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(response, JSONRPC20Response)) | 
				
			||||||
 | 
					        self.assertEqual(response.error["message"], "Invalid Request") | 
				
			||||||
 | 
					        self.assertEqual(response.error["code"], -32600) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_method_not_found(self): | 
				
			||||||
 | 
					        request = JSONRPC20Request("does_not_exist", [[]], _id=0) | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(request.json, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(response, JSONRPC20Response)) | 
				
			||||||
 | 
					        self.assertEqual(response.error["message"], "Method not found") | 
				
			||||||
 | 
					        self.assertEqual(response.error["code"], -32601) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_invalid_params(self): | 
				
			||||||
 | 
					        request = JSONRPC20Request("add", {"a": 0}, _id=0) | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(request.json, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(response, JSONRPC20Response)) | 
				
			||||||
 | 
					        self.assertEqual(response.error["message"], "Invalid params") | 
				
			||||||
 | 
					        self.assertEqual(response.error["code"], -32602) | 
				
			||||||
 | 
					        self.assertIn(response.error["data"]["message"], [ | 
				
			||||||
 | 
					            'sum() takes no keyword arguments', | 
				
			||||||
 | 
					            "sum() got an unexpected keyword argument 'a'", | 
				
			||||||
 | 
					        ]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_invalid_params_custom_function(self): | 
				
			||||||
 | 
					        request = JSONRPC20Request("multiply", [0], _id=0) | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(request.json, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(response, JSONRPC20Response)) | 
				
			||||||
 | 
					        self.assertEqual(response.error["message"], "Invalid params") | 
				
			||||||
 | 
					        self.assertEqual(response.error["code"], -32602) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        request = JSONRPC20Request("multiply", [0, 1, 2], _id=0) | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(request.json, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(response, JSONRPC20Response)) | 
				
			||||||
 | 
					        self.assertEqual(response.error["message"], "Invalid params") | 
				
			||||||
 | 
					        self.assertEqual(response.error["code"], -32602) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        request = JSONRPC20Request("multiply", {"a": 1}, _id=0) | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(request.json, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(response, JSONRPC20Response)) | 
				
			||||||
 | 
					        self.assertEqual(response.error["message"], "Invalid params") | 
				
			||||||
 | 
					        self.assertEqual(response.error["code"], -32602) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        request = JSONRPC20Request("multiply", {"a": 1, "b": 2, "c": 3}, _id=0) | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(request.json, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(response, JSONRPC20Response)) | 
				
			||||||
 | 
					        self.assertEqual(response.error["message"], "Invalid params") | 
				
			||||||
 | 
					        self.assertEqual(response.error["code"], -32602) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_server_error(self): | 
				
			||||||
 | 
					        request = JSONRPC20Request("error", _id=0) | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(request.json, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(response, JSONRPC20Response)) | 
				
			||||||
 | 
					        self.assertEqual(response.error["message"], "Server error") | 
				
			||||||
 | 
					        self.assertEqual(response.error["code"], -32000) | 
				
			||||||
 | 
					        self.assertEqual(response.error["data"]['type'], "KeyError") | 
				
			||||||
 | 
					        self.assertEqual( | 
				
			||||||
 | 
					            response.error["data"]['args'], ('error_explanation',)) | 
				
			||||||
 | 
					        self.assertEqual( | 
				
			||||||
 | 
					            response.error["data"]['message'], "'error_explanation'") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_notification_calls_method(self): | 
				
			||||||
 | 
					        request = JSONRPC20Request("long_time_method", is_notification=True) | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(request.json, self.dispatcher) | 
				
			||||||
 | 
					        self.assertEqual(response, None) | 
				
			||||||
 | 
					        self.long_time_method.assert_called_once_with() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_notification_does_not_return_error_does_not_exist(self): | 
				
			||||||
 | 
					        request = JSONRPC20Request("does_not_exist", is_notification=True) | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(request.json, self.dispatcher) | 
				
			||||||
 | 
					        self.assertEqual(response, None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_notification_does_not_return_error_invalid_params(self): | 
				
			||||||
 | 
					        request = JSONRPC20Request("add", {"a": 0}, is_notification=True) | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(request.json, self.dispatcher) | 
				
			||||||
 | 
					        self.assertEqual(response, None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_notification_does_not_return_error(self): | 
				
			||||||
 | 
					        request = JSONRPC20Request("error", is_notification=True) | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(request.json, self.dispatcher) | 
				
			||||||
 | 
					        self.assertEqual(response, None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_type_error_inside_method(self): | 
				
			||||||
 | 
					        request = JSONRPC20Request("type_error", _id=0) | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(request.json, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(response, JSONRPC20Response)) | 
				
			||||||
 | 
					        self.assertEqual(response.error["message"], "Server error") | 
				
			||||||
 | 
					        self.assertEqual(response.error["code"], -32000) | 
				
			||||||
 | 
					        self.assertEqual(response.error["data"]['type'], "TypeError") | 
				
			||||||
 | 
					        self.assertEqual( | 
				
			||||||
 | 
					            response.error["data"]['args'], ('TypeError inside method',)) | 
				
			||||||
 | 
					        self.assertEqual( | 
				
			||||||
 | 
					            response.error["data"]['message'], 'TypeError inside method') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_invalid_params_before_dispatcher_error(self): | 
				
			||||||
 | 
					        request = JSONRPC20Request( | 
				
			||||||
 | 
					            "dispatch_error", ["invalid", "params"], _id=0) | 
				
			||||||
 | 
					        response = JSONRPCResponseManager.handle(request.json, self.dispatcher) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(response, JSONRPC20Response)) | 
				
			||||||
 | 
					        self.assertEqual(response.error["message"], "Invalid params") | 
				
			||||||
 | 
					        self.assertEqual(response.error["code"], -32602) | 
				
			||||||
@ -0,0 +1,28 @@ | 
				
			|||||||
 | 
					from ..manager import JSONRPCResponseManager | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if sys.version_info < (2, 7): | 
				
			||||||
 | 
					    import unittest2 as unittest | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    import unittest | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONRPCResponseManager(unittest.TestCase): | 
				
			||||||
 | 
					    @unittest.skipIf(sys.version_info < (3, 5), "Test Py3.5+ functionality") | 
				
			||||||
 | 
					    def test_typeerror_with_annotations(self): | 
				
			||||||
 | 
					        """If a function has Python3 annotations and is called with improper | 
				
			||||||
 | 
					        arguments, make sure the framework doesn't fail with inspect.getargspec | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        from .py35_utils import distance | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dispatcher = { | 
				
			||||||
 | 
					            "distance": distance, | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        req = '{"jsonrpc": "2.0", "method": "distance", "params": [], "id": 1}' | 
				
			||||||
 | 
					        result = JSONRPCResponseManager.handle(req, dispatcher) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Make sure this returns JSONRPCInvalidParams rather than raising | 
				
			||||||
 | 
					        # UnboundLocalError | 
				
			||||||
 | 
					        self.assertEqual(result.error['code'], -32602) | 
				
			||||||
@ -0,0 +1,130 @@ | 
				
			|||||||
 | 
					""" Test utility functionality.""" | 
				
			||||||
 | 
					from ..utils import JSONSerializable, DatetimeDecimalEncoder, is_invalid_params | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import datetime | 
				
			||||||
 | 
					import decimal | 
				
			||||||
 | 
					import json | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if sys.version_info < (3, 3): | 
				
			||||||
 | 
					    from mock import patch | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    from unittest.mock import patch | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if sys.version_info < (2, 7): | 
				
			||||||
 | 
					    import unittest2 as unittest | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    import unittest | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestJSONSerializable(unittest.TestCase): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Test JSONSerializable functionality.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self): | 
				
			||||||
 | 
					        class A(JSONSerializable): | 
				
			||||||
 | 
					            @property | 
				
			||||||
 | 
					            def json(self): | 
				
			||||||
 | 
					                pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._class = A | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_abstract_class(self): | 
				
			||||||
 | 
					        with self.assertRaises(TypeError): | 
				
			||||||
 | 
					            JSONSerializable() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._class() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_definse_serialize_deserialize(self): | 
				
			||||||
 | 
					        """ Test classmethods of inherited class.""" | 
				
			||||||
 | 
					        self.assertEqual(self._class.serialize({}), "{}") | 
				
			||||||
 | 
					        self.assertEqual(self._class.deserialize("{}"), {}) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json(self): | 
				
			||||||
 | 
					        self.assertTrue(isinstance(self._class.from_json('{}'), self._class)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_from_json_incorrect(self): | 
				
			||||||
 | 
					        with self.assertRaises(ValueError): | 
				
			||||||
 | 
					            self._class.from_json('[]') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestDatetimeDecimalEncoder(unittest.TestCase): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Test DatetimeDecimalEncoder functionality.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_date_encoder(self): | 
				
			||||||
 | 
					        obj = datetime.date.today() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(TypeError): | 
				
			||||||
 | 
					            json.dumps(obj) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual( | 
				
			||||||
 | 
					            json.dumps(obj, cls=DatetimeDecimalEncoder), | 
				
			||||||
 | 
					            '"{0}"'.format(obj.isoformat()), | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_datetime_encoder(self): | 
				
			||||||
 | 
					        obj = datetime.datetime.now() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(TypeError): | 
				
			||||||
 | 
					            json.dumps(obj) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual( | 
				
			||||||
 | 
					            json.dumps(obj, cls=DatetimeDecimalEncoder), | 
				
			||||||
 | 
					            '"{0}"'.format(obj.isoformat()), | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_decimal_encoder(self): | 
				
			||||||
 | 
					        obj = decimal.Decimal('0.1') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(TypeError): | 
				
			||||||
 | 
					            json.dumps(obj) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result = json.dumps(obj, cls=DatetimeDecimalEncoder) | 
				
			||||||
 | 
					        self.assertTrue(isinstance(result, str)) | 
				
			||||||
 | 
					        self.assertEqual(float(result), float(0.1)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_default(self): | 
				
			||||||
 | 
					        encoder = DatetimeDecimalEncoder() | 
				
			||||||
 | 
					        with patch.object(json.JSONEncoder, 'default') as json_default: | 
				
			||||||
 | 
					            encoder.default("") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(json_default.call_count, 1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestUtils(unittest.TestCase): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Test utils functions.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_is_invalid_params_builtin(self): | 
				
			||||||
 | 
					        self.assertTrue(is_invalid_params(sum, 0, 0)) | 
				
			||||||
 | 
					        # NOTE: builtin functions could not be recognized by inspect.isfunction | 
				
			||||||
 | 
					        # It would raise TypeError if parameters are incorrect already. | 
				
			||||||
 | 
					        # self.assertFalse(is_invalid_params(sum, [0, 0]))  # <- fails | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_is_invalid_params_args(self): | 
				
			||||||
 | 
					        self.assertTrue(is_invalid_params(lambda a, b: None, 0)) | 
				
			||||||
 | 
					        self.assertTrue(is_invalid_params(lambda a, b: None, 0, 1, 2)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_is_invalid_params_kwargs(self): | 
				
			||||||
 | 
					        self.assertTrue(is_invalid_params(lambda a: None, **{})) | 
				
			||||||
 | 
					        self.assertTrue(is_invalid_params(lambda a: None, **{"a": 0, "b": 1})) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_invalid_params_correct(self): | 
				
			||||||
 | 
					        self.assertFalse(is_invalid_params(lambda: None)) | 
				
			||||||
 | 
					        self.assertFalse(is_invalid_params(lambda a: None, 0)) | 
				
			||||||
 | 
					        self.assertFalse(is_invalid_params(lambda a, b=0: None, 0)) | 
				
			||||||
 | 
					        self.assertFalse(is_invalid_params(lambda a, b=0: None, 0, 0)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_is_invalid_params_mixed(self): | 
				
			||||||
 | 
					        self.assertFalse(is_invalid_params(lambda a, b: None, 0, **{"b": 1})) | 
				
			||||||
 | 
					        self.assertFalse(is_invalid_params( | 
				
			||||||
 | 
					            lambda a, b, c=0: None, 0, **{"b": 1})) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_is_invalid_params_py2(self): | 
				
			||||||
 | 
					        with patch('jsonrpc.utils.sys') as mock_sys: | 
				
			||||||
 | 
					            mock_sys.version_info = (2, 7) | 
				
			||||||
 | 
					            with patch('jsonrpc.utils.is_invalid_params_py2') as mock_func: | 
				
			||||||
 | 
					                is_invalid_params(lambda a: None, 0) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert mock_func.call_count == 1 | 
				
			||||||
@ -0,0 +1,135 @@ | 
				
			|||||||
 | 
					""" Utility functions for package.""" | 
				
			||||||
 | 
					from abc import ABCMeta, abstractmethod | 
				
			||||||
 | 
					import datetime | 
				
			||||||
 | 
					import decimal | 
				
			||||||
 | 
					import inspect | 
				
			||||||
 | 
					import json | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from . import six | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONSerializable(six.with_metaclass(ABCMeta, object)): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Common functionality for json serializable objects.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    serialize = staticmethod(json.dumps) | 
				
			||||||
 | 
					    deserialize = staticmethod(json.loads) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @abstractmethod | 
				
			||||||
 | 
					    def json(self): | 
				
			||||||
 | 
					        raise NotImplementedError() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod | 
				
			||||||
 | 
					    def from_json(cls, json_str): | 
				
			||||||
 | 
					        data = cls.deserialize(json_str) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not isinstance(data, dict): | 
				
			||||||
 | 
					            raise ValueError("data should be dict") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return cls(**data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DatetimeDecimalEncoder(json.JSONEncoder): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ Encoder for datetime and decimal serialization. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Usage: json.dumps(object, cls=DatetimeDecimalEncoder) | 
				
			||||||
 | 
					    NOTE: _iterencode does not work | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def default(self, o): | 
				
			||||||
 | 
					        """ Encode JSON. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :return str: A JSON encoded string | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if isinstance(o, decimal.Decimal): | 
				
			||||||
 | 
					            return float(o) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if isinstance(o, (datetime.datetime, datetime.date)): | 
				
			||||||
 | 
					            return o.isoformat() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return json.JSONEncoder.default(self, o) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def is_invalid_params_py2(func, *args, **kwargs): | 
				
			||||||
 | 
					    """ Check, whether function 'func' accepts parameters 'args', 'kwargs'. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    NOTE: Method is called after funct(*args, **kwargs) generated TypeError, | 
				
			||||||
 | 
					    it is aimed to destinguish TypeError because of invalid parameters from | 
				
			||||||
 | 
					    TypeError from inside the function. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded: 1.9.0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    funcargs, varargs, varkwargs, defaults = inspect.getargspec(func) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    unexpected = set(kwargs.keys()) - set(funcargs) | 
				
			||||||
 | 
					    if len(unexpected) > 0: | 
				
			||||||
 | 
					        return True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    params = [funcarg for funcarg in funcargs if funcarg not in kwargs] | 
				
			||||||
 | 
					    funcargs_required = funcargs[:-len(defaults)] \ | 
				
			||||||
 | 
					        if defaults is not None \ | 
				
			||||||
 | 
					        else funcargs | 
				
			||||||
 | 
					    params_required = [ | 
				
			||||||
 | 
					        funcarg for funcarg in funcargs_required | 
				
			||||||
 | 
					        if funcarg not in kwargs | 
				
			||||||
 | 
					    ] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return not (len(params_required) <= len(args) <= len(params)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def is_invalid_params_py3(func, *args, **kwargs): | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    Use inspect.signature instead of inspect.getargspec or | 
				
			||||||
 | 
					    inspect.getfullargspec (based on inspect.signature itself) as it provides | 
				
			||||||
 | 
					    more information about function parameters. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded: 1.11.2 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    signature = inspect.signature(func) | 
				
			||||||
 | 
					    parameters = signature.parameters | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    unexpected = set(kwargs.keys()) - set(parameters.keys()) | 
				
			||||||
 | 
					    if len(unexpected) > 0: | 
				
			||||||
 | 
					        return True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    params = [ | 
				
			||||||
 | 
					        parameter for name, parameter in parameters.items() | 
				
			||||||
 | 
					        if name not in kwargs | 
				
			||||||
 | 
					    ] | 
				
			||||||
 | 
					    params_required = [ | 
				
			||||||
 | 
					        param for param in params | 
				
			||||||
 | 
					        if param.default is param.empty | 
				
			||||||
 | 
					    ] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return not (len(params_required) <= len(args) <= len(params)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def is_invalid_params(func, *args, **kwargs): | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    Method: | 
				
			||||||
 | 
					        Validate pre-defined criteria, if any is True - function is invalid | 
				
			||||||
 | 
					        0. func should be callable | 
				
			||||||
 | 
					        1. kwargs should not have unexpected keywords | 
				
			||||||
 | 
					        2. remove kwargs.keys from func.parameters | 
				
			||||||
 | 
					        3. number of args should be <= remaining func.parameters | 
				
			||||||
 | 
					        4. number of args should be >= remaining func.parameters less default | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    # For builtin functions inspect.getargspec(funct) return error. If builtin | 
				
			||||||
 | 
					    # function generates TypeError, it is because of wrong parameters. | 
				
			||||||
 | 
					    if not inspect.isfunction(func): | 
				
			||||||
 | 
					        return True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if sys.version_info >= (3, 3): | 
				
			||||||
 | 
					        return is_invalid_params_py3(func, *args, **kwargs) | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        # NOTE: use Python2 method for Python 3.2 as well. Starting from Python | 
				
			||||||
 | 
					        # 3.3 it is recommended to use inspect.signature instead. | 
				
			||||||
 | 
					        # In Python 3.0 - 3.2 inspect.getfullargspec is preferred but these | 
				
			||||||
 | 
					        # versions are almost not supported. Users should consider upgrading. | 
				
			||||||
 | 
					        return is_invalid_params_py2(func, *args, **kwargs) | 
				
			||||||
@ -0,0 +1,29 @@ | 
				
			|||||||
 | 
					""" | 
				
			||||||
 | 
					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.55.0" | 
				
			||||||
@ -0,0 +1,447 @@ | 
				
			|||||||
 | 
					""" | 
				
			||||||
 | 
					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] | 
				
			||||||
@ -0,0 +1,351 @@ | 
				
			|||||||
 | 
					""" | 
				
			||||||
 | 
					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.sock.connected: | 
				
			||||||
 | 
					            r, w, e = select.select( | 
				
			||||||
 | 
					                    (self.app.sock.sock, ), (), (), self.ping_timeout) | 
				
			||||||
 | 
					            if r: | 
				
			||||||
 | 
					                if not read_callback(): | 
				
			||||||
 | 
					                    break | 
				
			||||||
 | 
					            check_callback() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SSLDispacther: | 
				
			||||||
 | 
					    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.sock.connected: | 
				
			||||||
 | 
					            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 SSLDispacther(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) | 
				
			||||||
@ -0,0 +1,52 @@ | 
				
			|||||||
 | 
					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())])) | 
				
			||||||
@ -0,0 +1,515 @@ | 
				
			|||||||
 | 
					""" | 
				
			||||||
 | 
					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): | 
				
			||||||
 | 
					        return isinstance(self.sock, ssl.SSLSocket) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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) | 
				
			||||||
 | 
					        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 | 
				
			||||||
@ -0,0 +1,87 @@ | 
				
			|||||||
 | 
					""" | 
				
			||||||
 | 
					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 | 
				
			||||||
@ -0,0 +1,205 @@ | 
				
			|||||||
 | 
					""" | 
				
			||||||
 | 
					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 six.PY3: | 
				
			||||||
 | 
					    from base64 import encodebytes as base64encode | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    from base64 import encodestring as base64encode | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if six.PY3: | 
				
			||||||
 | 
					    if 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] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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", | 
				
			||||||
 | 
					        "Connection: Upgrade" | 
				
			||||||
 | 
					    ] | 
				
			||||||
 | 
					    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) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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=(101, 301, 302, 303)): | 
				
			||||||
 | 
					    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() | 
				
			||||||
@ -0,0 +1,328 @@ | 
				
			|||||||
 | 
					""" | 
				
			||||||
 | 
					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: | 
				
			||||||
 | 
					        if not phost: | 
				
			||||||
 | 
					            addrinfo_list = [ai for ai in socket.getaddrinfo( | 
				
			||||||
 | 
					                hostname, port, 0, 0, socket.SOL_TCP) | 
				
			||||||
 | 
					                if (ai[0] == socket.AF_INET6 and socket.has_ipv6) or ai[0] != socket.AF_INET6 | 
				
			||||||
 | 
					            ] | 
				
			||||||
 | 
					            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() | 
				
			||||||
 | 
					        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 | 
				
			||||||
@ -0,0 +1,82 @@ | 
				
			|||||||
 | 
					""" | 
				
			||||||
 | 
					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"] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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) | 
				
			||||||
@ -0,0 +1,160 @@ | 
				
			|||||||
 | 
					""" | 
				
			||||||
 | 
					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: | 
				
			||||||
 | 
					        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: | 
				
			||||||
 | 
					        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 | 
				
			||||||
@ -0,0 +1,52 @@ | 
				
			|||||||
 | 
					""" | 
				
			||||||
 | 
					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 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    HAVE_SSL = False | 
				
			||||||
@ -0,0 +1,163 @@ | 
				
			|||||||
 | 
					""" | 
				
			||||||
 | 
					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(" ", "") | 
				
			||||||
 | 
					        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 | 
				
			||||||
@ -0,0 +1,110 @@ | 
				
			|||||||
 | 
					""" | 
				
			||||||
 | 
					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 | 
				
			||||||
@ -0,0 +1,6 @@ | 
				
			|||||||
 | 
					HTTP/1.1 101 WebSocket Protocol Handshake | 
				
			||||||
 | 
					Connection: Upgrade  | 
				
			||||||
 | 
					Upgrade: WebSocket | 
				
			||||||
 | 
					Sec-WebSocket-Accept: Kxep+hNu9n51529fGidYu7a3wO0= | 
				
			||||||
 | 
					some_header: something | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,6 @@ | 
				
			|||||||
 | 
					HTTP/1.1 101 WebSocket Protocol Handshake | 
				
			||||||
 | 
					Connection: Upgrade | 
				
			||||||
 | 
					Upgrade WebSocket | 
				
			||||||
 | 
					Sec-WebSocket-Accept: Kxep+hNu9n51529fGidYu7a3wO0= | 
				
			||||||
 | 
					some_header: something | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,98 @@ | 
				
			|||||||
 | 
					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"), "") | 
				
			||||||
@ -0,0 +1,662 @@ | 
				
			|||||||
 | 
					# -*- 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 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() | 
				
			||||||
@ -0,0 +1,305 @@ | 
				
			|||||||
 | 
					Metadata-Version: 1.2 | 
				
			||||||
 | 
					Name: websocket-client | 
				
			||||||
 | 
					Version: 0.55.0 | 
				
			||||||
 | 
					Summary: WebSocket client for Python. hybi13 is supported. | 
				
			||||||
 | 
					Home-page: https://github.com/websocket-client/websocket-client.git | 
				
			||||||
 | 
					Author: liris | 
				
			||||||
 | 
					Author-email: liris.pp@gmail.com | 
				
			||||||
 | 
					License: BSD | 
				
			||||||
 | 
					Description: ================= | 
				
			||||||
 | 
					        websocket-client | 
				
			||||||
 | 
					        ================= | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        websocket-client module  is WebSocket client for python. This provide the low level APIs for WebSocket. All APIs are the synchronous functions. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        websocket-client supports only hybi-13. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        License | 
				
			||||||
 | 
					        ======= | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         - BSD | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Installation | 
				
			||||||
 | 
					        ============ | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        This module is tested on Python 2.7 and Python 3.4+. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Type "python setup.py install" or "pip install websocket-client" to install. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. CAUTION:: | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					          from v0.16.0, we can install by "pip install websocket-client" for Python 3. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        This module depends on | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         - six | 
				
			||||||
 | 
					         - backports.ssl_match_hostname for Python 2.x | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Performance | 
				
			||||||
 | 
					        ----------- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        The "send" and "validate_utf8" methods are too slow on pure python. If you want to get better performace, please install both numpy and wsaccel. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        How about Python 3 | 
				
			||||||
 | 
					        ================== | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Now, we support Python 3 on single source code from version 0.14.0. Thanks, @battlemidget and @ralphbean. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        HTTP Proxy | 
				
			||||||
 | 
					        ========== | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Support websocket access via http proxy. | 
				
			||||||
 | 
					        The proxy server must allow "CONNECT" method to websocket port. | 
				
			||||||
 | 
					        Default squid setting is "ALLOWED TO CONNECT ONLY HTTPS PORT". | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Current implementation of websocket-client is using "CONNECT" method via proxy. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        example | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code:: python | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            import websocket | 
				
			||||||
 | 
					            ws = websocket.WebSocket() | 
				
			||||||
 | 
					            ws.connect("ws://example.com/websocket", http_proxy_host="proxy_host_name", http_proxy_port=3128) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Examples | 
				
			||||||
 | 
					        ======== | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Long-lived connection | 
				
			||||||
 | 
					        --------------------- | 
				
			||||||
 | 
					        This example is similar to how WebSocket code looks in browsers using JavaScript. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code:: python | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            import websocket | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                import thread | 
				
			||||||
 | 
					            except ImportError: | 
				
			||||||
 | 
					                import _thread as thread | 
				
			||||||
 | 
					            import time | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            def on_message(ws, message): | 
				
			||||||
 | 
					                print(message) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            def on_error(ws, error): | 
				
			||||||
 | 
					                print(error) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            def on_close(ws): | 
				
			||||||
 | 
					                print("### closed ###") | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            def on_open(ws): | 
				
			||||||
 | 
					                def run(*args): | 
				
			||||||
 | 
					                    for i in range(3): | 
				
			||||||
 | 
					                        time.sleep(1) | 
				
			||||||
 | 
					                        ws.send("Hello %d" % i) | 
				
			||||||
 | 
					                    time.sleep(1) | 
				
			||||||
 | 
					                    ws.close() | 
				
			||||||
 | 
					                    print("thread terminating...") | 
				
			||||||
 | 
					                thread.start_new_thread(run, ()) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            if __name__ == "__main__": | 
				
			||||||
 | 
					                websocket.enableTrace(True) | 
				
			||||||
 | 
					                ws = websocket.WebSocketApp("ws://echo.websocket.org/", | 
				
			||||||
 | 
					                                          on_message = on_message, | 
				
			||||||
 | 
					                                          on_error = on_error, | 
				
			||||||
 | 
					                                          on_close = on_close) | 
				
			||||||
 | 
					                ws.on_open = on_open | 
				
			||||||
 | 
					                ws.run_forever() | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Short-lived one-off send-receive | 
				
			||||||
 | 
					        -------------------------------- | 
				
			||||||
 | 
					        This is if you want to communicate a short message and disconnect immediately when done. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code:: python | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            from websocket import create_connection | 
				
			||||||
 | 
					            ws = create_connection("ws://echo.websocket.org/") | 
				
			||||||
 | 
					            print("Sending 'Hello, World'...") | 
				
			||||||
 | 
					            ws.send("Hello, World") | 
				
			||||||
 | 
					            print("Sent") | 
				
			||||||
 | 
					            print("Receiving...") | 
				
			||||||
 | 
					            result =  ws.recv() | 
				
			||||||
 | 
					            print("Received '%s'" % result) | 
				
			||||||
 | 
					            ws.close() | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        If you want to customize socket options, set sockopt. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        sockopt example | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code:: python | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            from websocket import create_connection | 
				
			||||||
 | 
					            ws = create_connection("ws://echo.websocket.org/", | 
				
			||||||
 | 
					                                    sockopt=((socket.IPPROTO_TCP, socket.TCP_NODELAY),)) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        More advanced: Custom class | 
				
			||||||
 | 
					        --------------------------- | 
				
			||||||
 | 
					        You can also write your own class for the connection, if you want to handle the nitty-gritty details yourself. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code:: python | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            import socket | 
				
			||||||
 | 
					            from websocket import create_connection, WebSocket | 
				
			||||||
 | 
					            class MyWebSocket(WebSocket): | 
				
			||||||
 | 
					                def recv_frame(self): | 
				
			||||||
 | 
					                    frame = super().recv_frame() | 
				
			||||||
 | 
					                    print('yay! I got this frame: ', frame) | 
				
			||||||
 | 
					                    return frame | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            ws = create_connection("ws://echo.websocket.org/", | 
				
			||||||
 | 
					                                    sockopt=((socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),), class_=MyWebSocket) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        FAQ | 
				
			||||||
 | 
					        === | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        How to disable ssl cert verification? | 
				
			||||||
 | 
					        ------------------------------------- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Please set sslopt to {"cert_reqs": ssl.CERT_NONE}. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        WebSocketApp sample | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code:: python | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            ws = websocket.WebSocketApp("wss://echo.websocket.org") | 
				
			||||||
 | 
					            ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE}) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        create_connection sample | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code:: python | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            ws = websocket.create_connection("wss://echo.websocket.org", | 
				
			||||||
 | 
					              sslopt={"cert_reqs": ssl.CERT_NONE}) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        WebSocket sample | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code:: python | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            ws = websocket.WebSocket(sslopt={"cert_reqs": ssl.CERT_NONE}) | 
				
			||||||
 | 
					            ws.connect("wss://echo.websocket.org") | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        How to disable hostname verification? | 
				
			||||||
 | 
					        ------------------------------------- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Please set sslopt to {"check_hostname": False}. | 
				
			||||||
 | 
					        (since v0.18.0) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        WebSocketApp sample | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code:: python | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            ws = websocket.WebSocketApp("wss://echo.websocket.org") | 
				
			||||||
 | 
					            ws.run_forever(sslopt={"check_hostname": False}) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        create_connection sample | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code:: python | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            ws = websocket.create_connection("wss://echo.websocket.org", | 
				
			||||||
 | 
					              sslopt={"check_hostname": False}) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        WebSocket sample | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code:: python | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            ws = websocket.WebSocket(sslopt={"check_hostname": False}) | 
				
			||||||
 | 
					            ws.connect("wss://echo.websocket.org") | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        How to enable `SNI <http://en.wikipedia.org/wiki/Server_Name_Indication>`_? | 
				
			||||||
 | 
					        --------------------------------------------------------------------------- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        SNI support is available for Python 2.7.9+ and 3.2+. It will be enabled automatically whenever possible. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Sub Protocols. | 
				
			||||||
 | 
					        -------------- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        The server needs to support sub protocols, please set the subprotocol like this. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Subprotocol sample | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code:: python | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            ws = websocket.create_connection("ws://example.com/websocket", subprotocols=["binary", "base64"]) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        wsdump.py | 
				
			||||||
 | 
					        ========= | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        wsdump.py is simple WebSocket test(debug) tool. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        sample for echo.websocket.org:: | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					          $ wsdump.py ws://echo.websocket.org/ | 
				
			||||||
 | 
					          Press Ctrl+C to quit | 
				
			||||||
 | 
					          > Hello, WebSocket | 
				
			||||||
 | 
					          < Hello, WebSocket | 
				
			||||||
 | 
					          > How are you? | 
				
			||||||
 | 
					          < How are you? | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Usage | 
				
			||||||
 | 
					        ----- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        usage:: | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					          wsdump.py [-h] [-v [VERBOSE]] ws_url | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        WebSocket Simple Dump Tool | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        positional arguments: | 
				
			||||||
 | 
					          ws_url                websocket url. ex. ws://echo.websocket.org/ | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        optional arguments: | 
				
			||||||
 | 
					          -h, --help                           show this help message and exit | 
				
			||||||
 | 
					        WebSocketApp | 
				
			||||||
 | 
					          -v VERBOSE, --verbose VERBOSE    set verbose mode. If set to 1, show opcode. If set to 2, enable to trace websocket module | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        example:: | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					          $ wsdump.py ws://echo.websocket.org/ | 
				
			||||||
 | 
					          $ wsdump.py ws://echo.websocket.org/ -v | 
				
			||||||
 | 
					          $ wsdump.py ws://echo.websocket.org/ -vv | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					Keywords: websockets | 
				
			||||||
 | 
					Platform: UNKNOWN | 
				
			||||||
 | 
					Classifier: Development Status :: 4 - Beta | 
				
			||||||
 | 
					Classifier: License :: OSI Approved :: BSD License | 
				
			||||||
 | 
					Classifier: Programming Language :: Python | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2.6 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2.7 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.4 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.5 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.6 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.7 | 
				
			||||||
 | 
					Classifier: Operating System :: MacOS :: MacOS X | 
				
			||||||
 | 
					Classifier: Operating System :: POSIX | 
				
			||||||
 | 
					Classifier: Operating System :: Microsoft :: Windows | 
				
			||||||
 | 
					Classifier: Topic :: Internet | 
				
			||||||
 | 
					Classifier: Topic :: Software Development :: Libraries :: Python Modules | 
				
			||||||
 | 
					Classifier: Intended Audience :: Developers | 
				
			||||||
 | 
					Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* | 
				
			||||||
@ -0,0 +1,32 @@ | 
				
			|||||||
 | 
					ChangeLog | 
				
			||||||
 | 
					LICENSE | 
				
			||||||
 | 
					MANIFEST.in | 
				
			||||||
 | 
					README.rst | 
				
			||||||
 | 
					setup.cfg | 
				
			||||||
 | 
					setup.py | 
				
			||||||
 | 
					bin/wsdump.py | 
				
			||||||
 | 
					examples/echo_client.py | 
				
			||||||
 | 
					examples/echoapp_client.py | 
				
			||||||
 | 
					websocket/__init__.py | 
				
			||||||
 | 
					websocket/_abnf.py | 
				
			||||||
 | 
					websocket/_app.py | 
				
			||||||
 | 
					websocket/_cookiejar.py | 
				
			||||||
 | 
					websocket/_core.py | 
				
			||||||
 | 
					websocket/_exceptions.py | 
				
			||||||
 | 
					websocket/_handshake.py | 
				
			||||||
 | 
					websocket/_http.py | 
				
			||||||
 | 
					websocket/_logging.py | 
				
			||||||
 | 
					websocket/_socket.py | 
				
			||||||
 | 
					websocket/_ssl_compat.py | 
				
			||||||
 | 
					websocket/_url.py | 
				
			||||||
 | 
					websocket/_utils.py | 
				
			||||||
 | 
					websocket/tests/__init__.py | 
				
			||||||
 | 
					websocket/tests/test_cookiejar.py | 
				
			||||||
 | 
					websocket/tests/test_websocket.py | 
				
			||||||
 | 
					websocket/tests/data/header01.txt | 
				
			||||||
 | 
					websocket/tests/data/header02.txt | 
				
			||||||
 | 
					websocket_client.egg-info/PKG-INFO | 
				
			||||||
 | 
					websocket_client.egg-info/SOURCES.txt | 
				
			||||||
 | 
					websocket_client.egg-info/dependency_links.txt | 
				
			||||||
 | 
					websocket_client.egg-info/requires.txt | 
				
			||||||
 | 
					websocket_client.egg-info/top_level.txt | 
				
			||||||
@ -0,0 +1 @@ | 
				
			|||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,40 @@ | 
				
			|||||||
 | 
					../../../../bin/wsdump.py | 
				
			||||||
 | 
					../websocket/__init__.py | 
				
			||||||
 | 
					../websocket/__init__.pyc | 
				
			||||||
 | 
					../websocket/_abnf.py | 
				
			||||||
 | 
					../websocket/_abnf.pyc | 
				
			||||||
 | 
					../websocket/_app.py | 
				
			||||||
 | 
					../websocket/_app.pyc | 
				
			||||||
 | 
					../websocket/_cookiejar.py | 
				
			||||||
 | 
					../websocket/_cookiejar.pyc | 
				
			||||||
 | 
					../websocket/_core.py | 
				
			||||||
 | 
					../websocket/_core.pyc | 
				
			||||||
 | 
					../websocket/_exceptions.py | 
				
			||||||
 | 
					../websocket/_exceptions.pyc | 
				
			||||||
 | 
					../websocket/_handshake.py | 
				
			||||||
 | 
					../websocket/_handshake.pyc | 
				
			||||||
 | 
					../websocket/_http.py | 
				
			||||||
 | 
					../websocket/_http.pyc | 
				
			||||||
 | 
					../websocket/_logging.py | 
				
			||||||
 | 
					../websocket/_logging.pyc | 
				
			||||||
 | 
					../websocket/_socket.py | 
				
			||||||
 | 
					../websocket/_socket.pyc | 
				
			||||||
 | 
					../websocket/_ssl_compat.py | 
				
			||||||
 | 
					../websocket/_ssl_compat.pyc | 
				
			||||||
 | 
					../websocket/_url.py | 
				
			||||||
 | 
					../websocket/_url.pyc | 
				
			||||||
 | 
					../websocket/_utils.py | 
				
			||||||
 | 
					../websocket/_utils.pyc | 
				
			||||||
 | 
					../websocket/tests/__init__.py | 
				
			||||||
 | 
					../websocket/tests/__init__.pyc | 
				
			||||||
 | 
					../websocket/tests/data/header01.txt | 
				
			||||||
 | 
					../websocket/tests/data/header02.txt | 
				
			||||||
 | 
					../websocket/tests/test_cookiejar.py | 
				
			||||||
 | 
					../websocket/tests/test_cookiejar.pyc | 
				
			||||||
 | 
					../websocket/tests/test_websocket.py | 
				
			||||||
 | 
					../websocket/tests/test_websocket.pyc | 
				
			||||||
 | 
					PKG-INFO | 
				
			||||||
 | 
					SOURCES.txt | 
				
			||||||
 | 
					dependency_links.txt | 
				
			||||||
 | 
					requires.txt | 
				
			||||||
 | 
					top_level.txt | 
				
			||||||
@ -0,0 +1,2 @@ | 
				
			|||||||
 | 
					six | 
				
			||||||
 | 
					backports.ssl_match_hostname | 
				
			||||||
@ -0,0 +1 @@ | 
				
			|||||||
 | 
					websocket | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue