Quellcode durchsuchen

Update socketio libraries (#117)

This commit updates the socketio libraries in AToMPM. The aim of this is:

    to improve maintainability, as a socketio library is used instead of custom code
    to improve security, as the old libraries and their dependencies have noted security issues

The changes can be divided into:

    adding documentation and CI commands for installing the new libraries
    removing the old library code
    changing the node code to use the new API (of the Javascript socketio library)
    a fairly extensive rewrite of the model transformation websocket code to use the new Python socketio library
BentleyJOakes vor 4 Jahren
Ursprung
Commit
f05596a165

+ 3 - 3
.github/workflows/atompm_test.yml

@@ -44,12 +44,12 @@ jobs:
 #        restore-keys: |
 #          ${{ runner.os }}-pip-
       
-    - name: install igraph
+    - name: install igraph and socketio dependencies
 #      if: steps.cache.outputs.cache-hit != 'true'
       run: |
-        pip3 install six wheel
+        pip3 install six wheel websocket-client python-socketio[client] python-socketio
         pip3 install --user python-igraph
-      
+        
     - name: run tests
       run: |
         chromium --headless --disable-gpu --remote-debugging-port=9222 http://localhost &

+ 4 - 6
README.md

@@ -13,19 +13,17 @@ To run the portable version, execute `AToMPM.bat`.
 
 To install AToMPM, follow these steps:
 1. Download and install the latest Python
-    * This can be either Python 2.7.X or 3.X.X, but 3.X.X is strongly recommended
+    * Python 2.7.X is unsupported. Please use 3.X.X.
     * Use a package manager on Linux
     * Or visit http://python.org/download/
 1. Download and install python-igraph
     * Use the pip package manager (comes with Python)
-        * For Python2: `pip install python-igraph`
-        * For Python3: `pip3 install python-igraph`
+        * `pip3 install python-igraph`
     * For Windows, you may need to install the compiled igraph core
         * `http://www.lfd.uci.edu/~gohlke/pythonlibs/#python-igraph`
-1. Download and install six
+1. Download and install the six and python-socketio libraries
     * Use the pip package manager (comes with Python)
-        * For Python2: `pip install six`
-        * For Python3: `pip3 install six`
+        * `pip3 install six python-socketio python-socketio[client] websocket-client`
 1. Download and install node.js
     * Required version: >= 8.0
     * Use a package manager on Linux

+ 33 - 28
client/init.js

@@ -49,9 +49,39 @@ function __initClient()
 			var _arg = arg.split('=');
 			params[_arg[0]] = _arg[1];
 		});
-	
-	var socket = io.connect(
-			window.location.hostname,{'port':8124,'reconnect':false});
+
+    let socket = io(
+        window.location.hostname + ':8124', {
+            // 'port':8124,
+            'reconnect': false,
+            'timeout': 200000
+        });
+
+    socket.on('connect',
+        function () {
+
+            if (window.location.search == '' ||
+                ('aswid' in params && 'cswid' in params))
+                HttpUtils.httpReq(
+                    'POST',
+                    '/csworker',
+                    undefined,
+                    function (statusCode, resp) {
+                        __wid = resp;
+                        socket.emit(
+                            'message',
+                            {'method': 'POST', 'url': '/changeListener?wid=' + __wid});
+                    });
+
+            else if ('cswid' in params)
+                socket.emit(
+                    'message',
+                    {'method': 'POST', 'url': '/changeListener?wid=' + params['cswid']});
+
+            else
+                WindowManagement.openDialog(__FATAL_ERROR, 'invalid URL parameters ' +
+                    utils.jsons(params));
+        });
 
 	socket.on('message', 
 		function(msg)	
@@ -141,32 +171,7 @@ function __initClient()
 			WindowManagement.openDialog(__FATAL_ERROR, 'lost connection to back-end');
 		});
 
-	socket.on('connect', 
-		function()	
-		{  
-			if( window.location.search == '' ||
-				 ('aswid' in params && 'cswid' in params) )
-				HttpUtils.httpReq(
-					'POST',
-					'/csworker',
-					undefined,
-					function(statusCode,resp)
-					{
-						__wid = resp;
-						socket.emit(
-							'message',
-							{'method':'POST','url':'/changeListener?wid='+__wid});
-					});								
-				
-			else if( 'cswid' in params )
-				socket.emit(
-					'message',
-					{'method':'POST','url':'/changeListener?wid='+params['cswid']});
 
-			else
-				WindowManagement.openDialog(__FATAL_ERROR, 'invalid URL parameters '+
-						utils.jsons(params));
-		});
 		
 
 	/** PART 2 **/

+ 7 - 6
csworker.js

@@ -635,16 +635,17 @@ const _siocl = require('socket.io-client');
 			var self = this;
 			return function(callback,errback)
 			{
-				var socket = _siocl.connect('127.0.0.1',{port:8124});	
-				socket.on('connect', 
-					function()	
+				let io = _siocl('http://localhost:8124');
+
+				io.on('connect',
+					function()
 					{
-						socket.emit('message',
+						io.emit('message',
 							{'method':'POST','url':'/changeListener?wid='+aswid});
 					});
-				socket.on('disconnect', 
+				io.on('disconnect',
 					function()	{self.__aswid = undefined;});
-				socket.on('message', 	
+				io.on('message',
 					function(msg)	
 					{
 						/* on POST /changeListener response */

+ 8 - 12
httpwsd.js

@@ -80,11 +80,10 @@ function __respond(response, statusCode, reason, data, headers)
 /** Syntactic sugar to build and send a socket.io message **/
 function __send(socket, statusCode, reason, data, headers)
 {
-	socket.json.emit('message',
+	socket.emit('message',
 			{'statusCode':statusCode,
 			 'reason':reason,
-			 'headers':(headers || {'Content-Type': 'text/plain',
-			 'Access-Control-Allow-Origin': '*'}),
+			 'headers':(headers || {'Content-Type': 'text/plain'}),
 			 'data':data});
 }
 
@@ -671,7 +670,7 @@ var httpserver = _http.createServer(
 								function(sid)
 								{
 									__send(
-										wsserver.sockets.sockets[sid],
+										wsserver.sockets.sockets.get(sid),
 										undefined,
 										undefined,
 										_msg);
@@ -745,17 +744,14 @@ var httpserver = _http.createServer(
 						 'uriData':url['query'],
 						 'respIndex':responses.push(resp)-1});
 		});
-httpserver.listen(8124);
 
+let port = 8124;
+httpserver.listen(port);
+console.log("AToMPM listening on port: " + port)
 
+let wsserver = new _sio.Server(httpserver);
 
-var wsserver = _sio.listen(httpserver);
-wsserver.configure(
-	function()
-	{
-		wsserver.set('log level',2);
-	});
-wsserver.sockets.on('connection', 
+wsserver.sockets.on('connection',
 	function(socket)
 	{
 		/* unregister this socket from the specified worker ... when a worker

+ 1 - 2
mt/mtworker.py

@@ -130,14 +130,13 @@ class mtworkerThread(threading.Thread) :
 				self._ptcal = PyTCoreAbstractionLayer(
 					{'httpReq':self._aswHttpReq, 'wid':self._aswid}, self.wid)
 				try :
-					self._ws = WebSocket(self._ptcal)
+					self._ws = WebSocket(self._aswid, self._ptcal)
 				except Exception as e :
 					self._postMessage(
 						msg['resp'],
 						{'statusCode':500,
 						 'reason':str(e)})
 
-				self._ws.subscribe(self._aswid)
 				def respond(resp) :
 					if self._ws.subscribed == False :
 						self._ws.close()

+ 0 - 3
mt/websocket/README

@@ -1,3 +0,0 @@
-websocket-client
-https://github.com/websocket-client/websocket-client/
-version 0.48.0 - May 27th, 2018

+ 0 - 29
mt/websocket/__init__.py

@@ -1,29 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor,
-    Boston, MA  02110-1335  USA
-
-"""
-from ._abnf import *
-from ._app import WebSocketApp
-from ._core import *
-from ._exceptions import *
-from ._logging import *
-from ._socket import *
-
-__version__ = "0.48.0"

+ 0 - 447
mt/websocket/_abnf.py

@@ -1,447 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor,
-    Boston, MA  02110-1335  USA
-
-"""
-import array
-import os
-import struct
-
-import six
-
-from ._exceptions import *
-from ._utils import validate_utf8
-from threading import Lock
-
-try:
-    if six.PY3:
-        import numpy
-    else:
-        numpy = None
-except ImportError:
-    numpy = None
-
-try:
-    # If wsaccel is available we use compiled routines to mask data.
-    if not numpy:
-        from wsaccel.xormask import XorMaskerSimple
-
-        def _mask(_m, _d):
-            return XorMaskerSimple(_m).process(_d)
-except ImportError:
-    # wsaccel is not available, we rely on python implementations.
-    def _mask(_m, _d):
-        for i in range(len(_d)):
-            _d[i] ^= _m[i % 4]
-
-        if six.PY3:
-            return _d.tobytes()
-        else:
-            return _d.tostring()
-
-
-__all__ = [
-    'ABNF', 'continuous_frame', 'frame_buffer',
-    'STATUS_NORMAL',
-    'STATUS_GOING_AWAY',
-    'STATUS_PROTOCOL_ERROR',
-    'STATUS_UNSUPPORTED_DATA_TYPE',
-    'STATUS_STATUS_NOT_AVAILABLE',
-    'STATUS_ABNORMAL_CLOSED',
-    'STATUS_INVALID_PAYLOAD',
-    'STATUS_POLICY_VIOLATION',
-    'STATUS_MESSAGE_TOO_BIG',
-    'STATUS_INVALID_EXTENSION',
-    'STATUS_UNEXPECTED_CONDITION',
-    'STATUS_BAD_GATEWAY',
-    'STATUS_TLS_HANDSHAKE_ERROR',
-]
-
-# closing frame status codes.
-STATUS_NORMAL = 1000
-STATUS_GOING_AWAY = 1001
-STATUS_PROTOCOL_ERROR = 1002
-STATUS_UNSUPPORTED_DATA_TYPE = 1003
-STATUS_STATUS_NOT_AVAILABLE = 1005
-STATUS_ABNORMAL_CLOSED = 1006
-STATUS_INVALID_PAYLOAD = 1007
-STATUS_POLICY_VIOLATION = 1008
-STATUS_MESSAGE_TOO_BIG = 1009
-STATUS_INVALID_EXTENSION = 1010
-STATUS_UNEXPECTED_CONDITION = 1011
-STATUS_BAD_GATEWAY = 1014
-STATUS_TLS_HANDSHAKE_ERROR = 1015
-
-VALID_CLOSE_STATUS = (
-    STATUS_NORMAL,
-    STATUS_GOING_AWAY,
-    STATUS_PROTOCOL_ERROR,
-    STATUS_UNSUPPORTED_DATA_TYPE,
-    STATUS_INVALID_PAYLOAD,
-    STATUS_POLICY_VIOLATION,
-    STATUS_MESSAGE_TOO_BIG,
-    STATUS_INVALID_EXTENSION,
-    STATUS_UNEXPECTED_CONDITION,
-    STATUS_BAD_GATEWAY,
-)
-
-
-class ABNF(object):
-    """
-    ABNF frame class.
-    see http://tools.ietf.org/html/rfc5234
-    and http://tools.ietf.org/html/rfc6455#section-5.2
-    """
-
-    # operation code values.
-    OPCODE_CONT = 0x0
-    OPCODE_TEXT = 0x1
-    OPCODE_BINARY = 0x2
-    OPCODE_CLOSE = 0x8
-    OPCODE_PING = 0x9
-    OPCODE_PONG = 0xa
-
-    # available operation code value tuple
-    OPCODES = (OPCODE_CONT, OPCODE_TEXT, OPCODE_BINARY, OPCODE_CLOSE,
-               OPCODE_PING, OPCODE_PONG)
-
-    # opcode human readable string
-    OPCODE_MAP = {
-        OPCODE_CONT: "cont",
-        OPCODE_TEXT: "text",
-        OPCODE_BINARY: "binary",
-        OPCODE_CLOSE: "close",
-        OPCODE_PING: "ping",
-        OPCODE_PONG: "pong"
-    }
-
-    # data length threshold.
-    LENGTH_7 = 0x7e
-    LENGTH_16 = 1 << 16
-    LENGTH_63 = 1 << 63
-
-    def __init__(self, fin=0, rsv1=0, rsv2=0, rsv3=0,
-                 opcode=OPCODE_TEXT, mask=1, data=""):
-        """
-        Constructor for ABNF.
-        please check RFC for arguments.
-        """
-        self.fin = fin
-        self.rsv1 = rsv1
-        self.rsv2 = rsv2
-        self.rsv3 = rsv3
-        self.opcode = opcode
-        self.mask = mask
-        if data is None:
-            data = ""
-        self.data = data
-        self.get_mask_key = os.urandom
-
-    def validate(self, skip_utf8_validation=False):
-        """
-        validate the ABNF frame.
-        skip_utf8_validation: skip utf8 validation.
-        """
-        if self.rsv1 or self.rsv2 or self.rsv3:
-            raise WebSocketProtocolException("rsv is not implemented, yet")
-
-        if self.opcode not in ABNF.OPCODES:
-            raise WebSocketProtocolException("Invalid opcode %r", self.opcode)
-
-        if self.opcode == ABNF.OPCODE_PING and not self.fin:
-            raise WebSocketProtocolException("Invalid ping frame.")
-
-        if self.opcode == ABNF.OPCODE_CLOSE:
-            l = len(self.data)
-            if not l:
-                return
-            if l == 1 or l >= 126:
-                raise WebSocketProtocolException("Invalid close frame.")
-            if l > 2 and not skip_utf8_validation and not validate_utf8(self.data[2:]):
-                raise WebSocketProtocolException("Invalid close frame.")
-
-            code = 256 * \
-                six.byte2int(self.data[0:1]) + six.byte2int(self.data[1:2])
-            if not self._is_valid_close_status(code):
-                raise WebSocketProtocolException("Invalid close opcode.")
-
-    @staticmethod
-    def _is_valid_close_status(code):
-        return code in VALID_CLOSE_STATUS or (3000 <= code < 5000)
-
-    def __str__(self):
-        return "fin=" + str(self.fin) \
-            + " opcode=" + str(self.opcode) \
-            + " data=" + str(self.data)
-
-    @staticmethod
-    def create_frame(data, opcode, fin=1):
-        """
-        create frame to send text, binary and other data.
-
-        data: data to send. This is string value(byte array).
-            if opcode is OPCODE_TEXT and this value is unicode,
-            data value is converted into unicode string, automatically.
-
-        opcode: operation code. please see OPCODE_XXX.
-
-        fin: fin flag. if set to 0, create continue fragmentation.
-        """
-        if opcode == ABNF.OPCODE_TEXT and isinstance(data, six.text_type):
-            data = data.encode("utf-8")
-        # mask must be set if send data from client
-        return ABNF(fin, 0, 0, 0, opcode, 1, data)
-
-    def format(self):
-        """
-        format this object to string(byte array) to send data to server.
-        """
-        if any(x not in (0, 1) for x in [self.fin, self.rsv1, self.rsv2, self.rsv3]):
-            raise ValueError("not 0 or 1")
-        if self.opcode not in ABNF.OPCODES:
-            raise ValueError("Invalid OPCODE")
-        length = len(self.data)
-        if length >= ABNF.LENGTH_63:
-            raise ValueError("data is too long")
-
-        frame_header = chr(self.fin << 7
-                           | self.rsv1 << 6 | self.rsv2 << 5 | self.rsv3 << 4
-                           | self.opcode)
-        if length < ABNF.LENGTH_7:
-            frame_header += chr(self.mask << 7 | length)
-            frame_header = six.b(frame_header)
-        elif length < ABNF.LENGTH_16:
-            frame_header += chr(self.mask << 7 | 0x7e)
-            frame_header = six.b(frame_header)
-            frame_header += struct.pack("!H", length)
-        else:
-            frame_header += chr(self.mask << 7 | 0x7f)
-            frame_header = six.b(frame_header)
-            frame_header += struct.pack("!Q", length)
-
-        if not self.mask:
-            return frame_header + self.data
-        else:
-            mask_key = self.get_mask_key(4)
-            return frame_header + self._get_masked(mask_key)
-
-    def _get_masked(self, mask_key):
-        s = ABNF.mask(mask_key, self.data)
-
-        if isinstance(mask_key, six.text_type):
-            mask_key = mask_key.encode('utf-8')
-
-        return mask_key + s
-
-    @staticmethod
-    def mask(mask_key, data):
-        """
-        mask or unmask data. Just do xor for each byte
-
-        mask_key: 4 byte string(byte).
-
-        data: data to mask/unmask.
-        """
-        if data is None:
-            data = ""
-
-        if isinstance(mask_key, six.text_type):
-            mask_key = six.b(mask_key)
-
-        if isinstance(data, six.text_type):
-            data = six.b(data)
-
-        if numpy:
-            origlen = len(data)
-            _mask_key = mask_key[3] << 24 | mask_key[2] << 16 | mask_key[1] << 8 | mask_key[0]
-
-            # We need data to be a multiple of four...
-            data += bytes(" " * (4 - (len(data) % 4)), "us-ascii")
-            a = numpy.frombuffer(data, dtype="uint32")
-            masked = numpy.bitwise_xor(a, [_mask_key]).astype("uint32")
-            if len(data) > origlen:
-              return masked.tobytes()[:origlen]
-            return masked.tobytes()
-        else:
-            _m = array.array("B", mask_key)
-            _d = array.array("B", data)
-            return _mask(_m, _d)
-
-
-class frame_buffer(object):
-    _HEADER_MASK_INDEX = 5
-    _HEADER_LENGTH_INDEX = 6
-
-    def __init__(self, recv_fn, skip_utf8_validation):
-        self.recv = recv_fn
-        self.skip_utf8_validation = skip_utf8_validation
-        # Buffers over the packets from the layer beneath until desired amount
-        # bytes of bytes are received.
-        self.recv_buffer = []
-        self.clear()
-        self.lock = Lock()
-
-    def clear(self):
-        self.header = None
-        self.length = None
-        self.mask = None
-
-    def has_received_header(self):
-        return self.header is None
-
-    def recv_header(self):
-        header = self.recv_strict(2)
-        b1 = header[0]
-
-        if six.PY2:
-            b1 = ord(b1)
-
-        fin = b1 >> 7 & 1
-        rsv1 = b1 >> 6 & 1
-        rsv2 = b1 >> 5 & 1
-        rsv3 = b1 >> 4 & 1
-        opcode = b1 & 0xf
-        b2 = header[1]
-
-        if six.PY2:
-            b2 = ord(b2)
-
-        has_mask = b2 >> 7 & 1
-        length_bits = b2 & 0x7f
-
-        self.header = (fin, rsv1, rsv2, rsv3, opcode, has_mask, length_bits)
-
-    def has_mask(self):
-        if not self.header:
-            return False
-        return self.header[frame_buffer._HEADER_MASK_INDEX]
-
-    def has_received_length(self):
-        return self.length is None
-
-    def recv_length(self):
-        bits = self.header[frame_buffer._HEADER_LENGTH_INDEX]
-        length_bits = bits & 0x7f
-        if length_bits == 0x7e:
-            v = self.recv_strict(2)
-            self.length = struct.unpack("!H", v)[0]
-        elif length_bits == 0x7f:
-            v = self.recv_strict(8)
-            self.length = struct.unpack("!Q", v)[0]
-        else:
-            self.length = length_bits
-
-    def has_received_mask(self):
-        return self.mask is None
-
-    def recv_mask(self):
-        self.mask = self.recv_strict(4) if self.has_mask() else ""
-
-    def recv_frame(self):
-
-        with self.lock:
-            # Header
-            if self.has_received_header():
-                self.recv_header()
-            (fin, rsv1, rsv2, rsv3, opcode, has_mask, _) = self.header
-
-            # Frame length
-            if self.has_received_length():
-                self.recv_length()
-            length = self.length
-
-            # Mask
-            if self.has_received_mask():
-                self.recv_mask()
-            mask = self.mask
-
-            # Payload
-            payload = self.recv_strict(length)
-            if has_mask:
-                payload = ABNF.mask(mask, payload)
-
-            # Reset for next frame
-            self.clear()
-
-            frame = ABNF(fin, rsv1, rsv2, rsv3, opcode, has_mask, payload)
-            frame.validate(self.skip_utf8_validation)
-
-        return frame
-
-    def recv_strict(self, bufsize):
-        shortage = bufsize - sum(len(x) for x in self.recv_buffer)
-        while shortage > 0:
-            # Limit buffer size that we pass to socket.recv() to avoid
-            # fragmenting the heap -- the number of bytes recv() actually
-            # reads is limited by socket buffer and is relatively small,
-            # yet passing large numbers repeatedly causes lots of large
-            # buffers allocated and then shrunk, which results in
-            # fragmentation.
-            bytes_ = self.recv(min(16384, shortage))
-            self.recv_buffer.append(bytes_)
-            shortage -= len(bytes_)
-
-        unified = six.b("").join(self.recv_buffer)
-
-        if shortage == 0:
-            self.recv_buffer = []
-            return unified
-        else:
-            self.recv_buffer = [unified[bufsize:]]
-            return unified[:bufsize]
-
-
-class continuous_frame(object):
-
-    def __init__(self, fire_cont_frame, skip_utf8_validation):
-        self.fire_cont_frame = fire_cont_frame
-        self.skip_utf8_validation = skip_utf8_validation
-        self.cont_data = None
-        self.recving_frames = None
-
-    def validate(self, frame):
-        if not self.recving_frames and frame.opcode == ABNF.OPCODE_CONT:
-            raise WebSocketProtocolException("Illegal frame")
-        if self.recving_frames and \
-                frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY):
-            raise WebSocketProtocolException("Illegal frame")
-
-    def add(self, frame):
-        if self.cont_data:
-            self.cont_data[1] += frame.data
-        else:
-            if frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY):
-                self.recving_frames = frame.opcode
-            self.cont_data = [frame.opcode, frame.data]
-
-        if frame.fin:
-            self.recving_frames = None
-
-    def is_fire(self, frame):
-        return frame.fin or self.fire_cont_frame
-
-    def extract(self, frame):
-        data = self.cont_data
-        self.cont_data = None
-        frame.data = data[1]
-        if not self.fire_cont_frame and data[0] == ABNF.OPCODE_TEXT and not self.skip_utf8_validation and not validate_utf8(frame.data):
-            raise WebSocketPayloadException(
-                "cannot decode: " + repr(frame.data))
-
-        return [data[0], frame]

+ 0 - 325
mt/websocket/_app.py

@@ -1,325 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor,
-    Boston, MA  02110-1335  USA
-
-"""
-
-"""
-WebSocketApp provides higher level APIs.
-"""
-import 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) # Use a 10 second timeout to avoid to wait forever on close
-            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 obosleted and ignored it.
-        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)
-
-    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):
-        """
-        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.
-        """
-
-        if not ping_timeout or 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 sockopt is None:
-            sockopt = []
-        if sslopt is None:
-            sslopt = {}
-        if self.sock:
-            raise WebSocketException("socket is already opened")
-        thread = None
-        close_frame = None
-        self.keep_running = True
-        self.last_ping_tm = 0
-        self.last_pong_tm = 0
-
-        def teardown():
-            if thread and thread.isAlive():
-                event.set()
-                thread.join()
-            self.keep_running = False
-            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 and True or False,
-                skip_utf8_validation=skip_utf8_validation)
-            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)
-            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:
-                    close_frame = frame
-                    return teardown()
-                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 and self.last_ping_tm \
-                        and time.time() - self.last_ping_tm > ping_timeout \
-                        and self.last_ping_tm - self.last_pong_tm > ping_timeout:
-                    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()
-
-    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 """
-        import inspect
-        # 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:
-                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 - 52
mt/websocket/_cookiejar.py

@@ -1,52 +0,0 @@
-try:
-    import Cookie
-except:
-    import http.cookies as Cookie
-
-
-class SimpleCookieJar(object):
-    def __init__(self):
-        self.jar = dict()
-
-    def add(self, set_cookie):
-        if set_cookie:
-            try:
-                simpleCookie = Cookie.SimpleCookie(set_cookie)
-            except:
-                simpleCookie = Cookie.SimpleCookie(set_cookie.encode('ascii', 'ignore'))
-
-            for k, v in simpleCookie.items():
-                domain = v.get("domain")
-                if domain:
-                    if not domain.startswith("."):
-                        domain = "." + domain
-                    cookie = self.jar.get(domain) if self.jar.get(domain) else Cookie.SimpleCookie()
-                    cookie.update(simpleCookie)
-                    self.jar[domain.lower()] = cookie
-
-    def set(self, set_cookie):
-        if set_cookie:
-            try:
-                simpleCookie = Cookie.SimpleCookie(set_cookie)
-            except:
-                simpleCookie = Cookie.SimpleCookie(set_cookie.encode('ascii', 'ignore'))
-
-            for k, v in simpleCookie.items():
-                domain = v.get("domain")
-                if domain:
-                    if not domain.startswith("."):
-                        domain = "." + domain
-                    self.jar[domain.lower()] = simpleCookie
-
-    def get(self, host):
-        if not host:
-            return ""
-
-        cookies = []
-        for domain, simpleCookie in self.jar.items():
-            host = host.lower()
-            if host.endswith(domain) or host == domain[1:]:
-                cookies.append(self.jar.get(domain))
-
-        return "; ".join(filter(None, ["%s=%s" % (k, v.value) for cookie in filter(None, sorted(cookies)) for k, v in
-                                       sorted(cookie.items())]))

+ 0 - 495
mt/websocket/_core.py

@@ -1,495 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor,
-    Boston, MA  02110-1335  USA
-
-"""
-from __future__ import print_function
-
-import socket
-import struct
-import threading
-
-import 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.
-                 "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
-                 "subprotocols" - array of available sub protocols.
-                                  default is None.
-                 "socket" - pre-initialized stream socket.
-
-        """
-        self.sock, addrs = connect(url, self.sock_opt, proxy_info(**options),
-                                   options.pop('socket', None))
-
-        try:
-            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)
-                try:
-                    frame = self.recv_frame()
-                    if isEnabledForError():
-                        recv_status = struct.unpack("!H", frame.data[0:2])[0]
-                        if recv_status != STATUS_NORMAL:
-                            error("close status: " + repr(recv_status))
-                except:
-                    pass
-                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.
-             "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.
-             "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 - 87
mt/websocket/_exceptions.py

@@ -1,87 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor,
-    Boston, MA  02110-1335  USA
-
-"""
-
-
-"""
-define websocket exceptions
-"""
-
-
-class WebSocketException(Exception):
-    """
-    websocket exception class.
-    """
-    pass
-
-
-class WebSocketProtocolException(WebSocketException):
-    """
-    If the websocket protocol is invalid, this exception will be raised.
-    """
-    pass
-
-
-class WebSocketPayloadException(WebSocketException):
-    """
-    If the websocket payload is invalid, this exception will be raised.
-    """
-    pass
-
-
-class WebSocketConnectionClosedException(WebSocketException):
-    """
-    If remote host closed the connection or some network error happened,
-    this exception will be raised.
-    """
-    pass
-
-
-class WebSocketTimeoutException(WebSocketException):
-    """
-    WebSocketTimeoutException will be raised at socket timeout during read/write data.
-    """
-    pass
-
-
-class WebSocketProxyException(WebSocketException):
-    """
-    WebSocketProxyException will be raised when proxy error occurred.
-    """
-    pass
-
-
-class WebSocketBadStatusException(WebSocketException):
-    """
-    WebSocketBadStatusException will be raised when we get bad handshake status code.
-    """
-
-    def __init__(self, message, status_code, status_message=None):
-        msg = message % (status_code, status_message) if status_message is not None \
-            else  message % status_code
-        super(WebSocketBadStatusException, self).__init__(msg)
-        self.status_code = status_code
-
-class WebSocketAddressException(WebSocketException):
-    """
-    If the websocket address info cannot be found, this exception will be raised.
-    """
-    pass

+ 0 - 180
mt/websocket/_handshake.py

@@ -1,180 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor,
-    Boston, MA  02110-1335  USA
-
-"""
-import hashlib
-import hmac
-import os
-
-import six
-
-from ._cookiejar import SimpleCookieJar
-from ._exceptions import *
-from ._http import *
-from ._logging import *
-from ._socket import *
-
-if six.PY3:
-    from base64 import encodebytes as base64encode
-else:
-    from base64 import encodestring as base64encode
-
-__all__ = ["handshake_response", "handshake"]
-
-if hasattr(hmac, "compare_digest"):
-    compare_digest = hmac.compare_digest
-else:
-    def compare_digest(s1, s2):
-        return s1 == s2
-
-# websocket supported version.
-VERSION = 13
-
-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)
-    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 "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()
-    headers.append("Sec-WebSocket-Key: %s" % key)
-    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 = map(": ".join, header.items())
-        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_status=101):
-    status, resp_headers, status_message = read_headers(sock)
-    if status != success_status:
-        raise WebSocketBadStatusException("Handshake status %d %s", status, status_message)
-    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 - 319
mt/websocket/_http.py

@@ -1,319 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor,
-    Boston, MA  02110-1335  USA
-
-"""
-import errno
-import os
-import socket
-import sys
-
-import six
-
-from ._exceptions import *
-from ._logging import *
-from ._socket import*
-from ._ssl_compat import *
-from ._url import *
-
-if six.PY3:
-    from base64 import encodebytes as base64encode
-else:
-    from base64 import encodestring as base64encode
-
-__all__ = ["proxy_info", "connect", "read_headers"]
-
-try:
-    import socks
-    ProxyConnectionError = socks.ProxyConnectionError
-    HAS_PYSOCKS = True
-except:
-    class ProxyConnectionError(BaseException):
-        pass
-    HAS_PYSOCKS = False
-
-class proxy_info(object):
-
-    def __init__(self, **options):
-        self.type = options.get("proxy_type", "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 = socket.getaddrinfo(
-                hostname, port, 0, 0, socket.SOL_TCP)
-            return addrinfo_list, False, None
-        else:
-            pport = pport and pport or 80
-            # when running on windows 10, the getaddrinfo used above
-            # returns a socktype 0. This generates an error exception:
-            #_on_error: exception Socket type must be stream or datagram, not 0
-            # Force the socket type to SOCK_STREAM
-            addrinfo_list = socket.getaddrinfo(phost, pport, 0, socket.SOCK_STREAM, socket.SOL_TCP)
-            return addrinfo_list, True, pauth
-    except socket.gaierror as e:
-        raise WebSocketAddressException(e)
-
-
-def _open_socket(addrinfo_list, sockopt, timeout):
-    err = None
-    for addrinfo in addrinfo_list:
-        family, socktype, proto = addrinfo[:3]
-        sock = socket.socket(family, socktype, proto)
-        sock.settimeout(timeout)
-        for opts in DEFAULT_SOCKET_OPTION:
-            sock.setsockopt(*opts)
-        for opts in sockopt:
-            sock.setsockopt(*opts)
-
-        address = addrinfo[4]
-        try:
-            sock.connect(address)
-            err = None
-        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 in eConnRefused:
-                err = error
-                continue
-            else:
-                raise error
-        else:
-            break
-    else:
-        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 - 75
mt/websocket/_logging.py

@@ -1,75 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor,
-    Boston, MA  02110-1335  USA
-
-"""
-import logging
-
-_logger = logging.getLogger('websocket')
-_logger.addHandler(logging.NullHandler())
-_traceEnabled = False
-
-__all__ = ["enableTrace", "dump", "error", "warning", "debug", "trace",
-           "isEnabledForError", "isEnabledForDebug"]
-
-
-def enableTrace(traceable):
-    """
-    turn on/off the traceability.
-
-    traceable: boolean value. if set True, traceability is enabled.
-    """
-    global _traceEnabled
-    _traceEnabled = traceable
-    if traceable:
-        if not _logger.handlers:
-            _logger.addHandler(logging.StreamHandler())
-        _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 - 126
mt/websocket/_socket.py

@@ -1,126 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor,
-    Boston, MA 02110-1335  USA
-
-"""
-import 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.")
-
-    try:
-        bytes_ = sock.recv(bufsize)
-    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.")
-
-    try:
-        return sock.send(data)
-    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 - 44
mt/websocket/_ssl_compat.py

@@ -1,44 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor,
-    Boston, MA 02110-1335  USA
-
-"""
-__all__ = ["HAVE_SSL", "ssl", "SSLError"]
-
-try:
-    import ssl
-    from ssl import SSLError
-    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
-
-    HAVE_SSL = False

+ 0 - 163
mt/websocket/_url.py

@@ -1,163 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor,
-    Boston, MA  02110-1335  USA
-
-"""
-
-import os
-import socket
-import struct
-
-from six.moves.urllib.parse import urlparse
-
-
-__all__ = ["parse_url", "get_proxy_info"]
-
-
-def parse_url(url):
-    """
-    parse url and the result is tuple of
-    (hostname, port, resource path and the flag of secure mode)
-
-    url: url string.
-    """
-    if ":" not in url:
-        raise ValueError("url is invalid")
-
-    scheme, url = url.split(":", 1)
-
-    parsed = urlparse(url, scheme="ws")
-    if parsed.hostname:
-        hostname = parsed.hostname
-    else:
-        raise ValueError("hostname is invalid")
-    port = 0
-    if parsed.port:
-        port = parsed.port
-
-    is_secure = False
-    if scheme == "ws":
-        if not port:
-            port = 80
-    elif scheme == "wss":
-        is_secure = True
-        if not port:
-            port = 443
-    else:
-        raise ValueError("scheme %s is invalid" % scheme)
-
-    if parsed.path:
-        resource = parsed.path
-    else:
-        resource = "/"
-
-    if parsed.query:
-        resource += "?" + parsed.query
-
-    return hostname, port, resource, is_secure
-
-
-DEFAULT_NO_PROXY_HOST = ["localhost", "127.0.0.1"]
-
-
-def _is_ip_address(addr):
-    try:
-        socket.inet_aton(addr)
-    except socket.error:
-        return False
-    else:
-        return True
-
-
-def _is_subnet_address(hostname):
-    try:
-        addr, netmask = hostname.split("/")
-        return _is_ip_address(addr) and 0 <= int(netmask) < 32
-    except ValueError:
-        return False
-
-
-def _is_address_in_network(ip, net):
-    ipaddr = struct.unpack('I', socket.inet_aton(ip))[0]
-    netaddr, bits = net.split('/')
-    netmask = struct.unpack('I', socket.inet_aton(netaddr))[0] & ((2 << int(bits) - 1) - 1)
-    return ipaddr & netmask == netmask
-
-
-def _is_no_proxy_host(hostname, no_proxy):
-    if not no_proxy:
-        v = os.environ.get("no_proxy", "").replace(" ", "")
-        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 - 105
mt/websocket/_utils.py

@@ -1,105 +0,0 @@
-"""
-websocket - WebSocket client library for Python
-
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor,
-    Boston, MA 02110-1335  USA
-
-"""
-import six
-
-__all__ = ["NoLock", "validate_utf8", "extract_err_message"]
-
-
-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

+ 41 - 103
mt/ws.py

@@ -2,21 +2,12 @@
 Copyright 2011 by the AToMPM team and licensed under the LGPL
 See COPYING.lesser and README.md in the root of this project for full details'''
 
-import re, threading, json, logging
 
-import sys
-
-
-if sys.version_info[0] < 3:
-	import httplib as httplib
-	import websocket._app as websocket
-else:
-	import http.client as httplib
-	import websocket._app as websocket
+import threading, json, logging, sys
+import socketio
 
 '''
-	a friendly wrapper around python-websockets that doubles as a socketio client
-	
+	a friendly wrapper around a socketio client
 	_opened		true when the socket is first opened
 	_chlogh		a reference to an object that implements onchangelog(), this
   					method is called upon reception of changelogs from the asworker
@@ -27,117 +18,64 @@ else:
 						False: subscription failed
 	_ws			the python-websocket '''
 class WebSocket :
-	#socket.io messages types
-	DISCONNECT	 = '0'
-	CONNECT		 = '1'
-	HEARTBEAT	 = '2'
-	MESSAGE		 = '3'
-	JSON_MESSAGE = '4'
-	EVENT			 = '5'
-	ACK			 = '6'
-	ERROR			 = '7'
-	NOOP			 = '8'
-
-
-	def __init__(self,chlogh=None) :
-		assert chlogh == None or 'onchangelog' in dir(chlogh)
+
+	def __init__(self, _aswid, chlogh=None) :
+
+		assert chlogh is None or 'onchangelog' in dir(chlogh)
 		self._opened 	 = False
 		self._chlogh 	 = chlogh
 		self.subscribed = None
-		self.connect()
+		self._aswid = _aswid
 
-	def _start_ws(self, hskey):
-		self._ws = websocket.WebSocketApp(
-			'ws://127.0.0.1:8124/socket.io/1/websocket/' + hskey,
-			on_message = self._onmessage,
-			on_open = self._onopen)
-		self._ws.run_forever()
+		self.socketIO = None
 
-	'''
-		connect to the socketio server
-	  
-		1. perform the HTTP handshake
-		2. open a websocket connection 
-		REF:: https://github.com/LearnBoost/socket.io-spec '''
-	def connect(self) :
-		conn  = httplib.HTTPConnection('127.0.0.1:8124')
-		conn.request('POST','/socket.io/1/')
-		resp  = conn.getresponse()
+		thr = threading.Thread(target = self._start_ws)
+		thr.start()
+
+	def _start_ws(self):
 
-		if resp.status == 200 :
-			resp = resp.read()
+		try:
+			self.socketIO = socketio.Client(logger=False, engineio_logger=False)
 
-			try: #handle bytes
-				resp = resp.decode()
-			except AttributeError:
-				pass
+			self.socketIO.on('connect', self._onopen)
+			self.socketIO.on('message', self._onmessage)
 
-			hskey = resp.split(':')[0]
+			self.socketIO.connect('http://127.0.0.1:8124')
+			self.socketIO.sleep(1)
 
-			# start the websocket on a different thread as it loops forever
-			thr = threading.Thread(target = self._start_ws, args = (hskey, ))
-			thr.start()
+			data = {'method': 'POST', 'url': '/changeListener?wid='+self._aswid}
+			self.socketIO.emit('message', data)
 
-		else :
-			raise Exception('websocket initialization failed :: '+str(resp.reason))
+			self.socketIO.wait()
+		except Exception as e:
+			raise e
 
+	''' 
+		mark socket connection as opened '''
 
+	def _onopen(self) :
+		self._opened = True
 
 	'''
 		close the socket '''
-	def close(self, ws) :
-		self._ws.close()
-
-
+	def close(self) :
+		self.socketIO.close()
 
 	''' 
-		parse and handle incoming message '''
-	def _onmessage(self,ws, msg) :
-
-		logging.debug('## msg recvd '+msg)
-
-		msgType = msg[0]
-		if msgType == WebSocket.CONNECT :
-			return
-
-		elif msgType == WebSocket.ERROR :
-			raise Exception('received error from socketio :: '+str(msg))
-
-		elif msgType == WebSocket.HEARTBEAT :
-			self._ws.send('2::')
-
-		elif msgType == WebSocket.EVENT :
-			msg = json.loads(msg[len(WebSocket.EVENT+':::'):])
-			if msg['name'] != 'message' :
-				raise Exception('received unexpected socketio event :: '+str(msg))
-			msg = msg['args'][0]
-
-			if 'statusCode' in msg and msg['statusCode'] != None :
-				#on POST /changeListener response
-				if msg['statusCode'] == 201 :
-					self.subscribed = True
-				else :
-					self.subscribed = False
-			elif self._chlogh and self.subscribed :
-				self._chlogh.onchangelog(msg['data'])
-		else :
-			pass
 
+		parse and handle incoming message '''
+	def _onmessage(self, data) :
 
+		logging.debug('## data recvd '+ str(data))
 
-	''' 
-		mark socket connection as opened '''
-	def _onopen(self, ws) :
-		self._opened = True
+		if 'statusCode' in data and data['statusCode'] is not None :
+			#on POST /changeListener response
 
+			if data['statusCode'] == 201 :
+				self.subscribed = True
+			else :
+				self.subscribed = False
+		elif self._chlogh and self.subscribed :
+			self._chlogh.onchangelog(data['data'])
 
 
-	'''
-		subscribe to specified asworker '''
-	def subscribe(self,aswid) :
-		if not self._opened :
-			t = threading.Timer(0.25,self.subscribe,[aswid])
-			t.start()
-		else :
-			self._ws.send(
-				'4:::{"method":"POST","url":"/changeListener?wid='+aswid+'"}')

Datei-Diff unterdrückt, da er zu groß ist
+ 4540 - 600
package-lock.json


+ 2 - 2
package.json

@@ -12,8 +12,8 @@
     "url": "https://github.com/AToMPM/atompm"
   },
   "dependencies": {
-    "socket.io": "^0.9.19",
-    "socket.io-client": "^0.9.16"
+    "socket.io": "latest",
+    "socket.io-client": "latest"
   },
   "devDependencies": {
     "chromedriver": "latest",

+ 2 - 0
packaging/docker/Dockerfile

@@ -19,6 +19,8 @@ RUN pip3 install six
 
 RUN pip3 install python-igraph
 
+RUN pip3 install python-socketio python-socketio[client]
+
 RUN apt-get install -y nodejs npm
 
 RUN mkdir /opt/atompm