XSocket 2.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
xsocket-2.1.0/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ # License
2
+
3
+ [The MIT License (MIT)](http://opensource.org/licenses/MIT)
4
+
5
+ Copyright (c) 2009-2013 Jeff Mott
6
+ Copyright (c) 2013-2016 Evan Vosberg
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in
16
+ all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ THE SOFTWARE.
xsocket-2.1.0/PKG-INFO ADDED
@@ -0,0 +1,122 @@
1
+ Metadata-Version: 2.4
2
+ Name: xsocket
3
+ Version: 2.1.0
4
+ Summary: WebSocket client and server for Python (asyncio), compatible with xSocket for Node.js and the browser
5
+ Author: pr!de
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/pride-oO/xsocket
8
+ Keywords: socket,ws,wss,websocket,message,client,server,asyncio
9
+ Requires-Python: >=3.9
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: websockets>=13
13
+ Dynamic: license-file
14
+
15
+ rfr# xSocket for Python
16
+
17
+ WebSocket client and server for Python (asyncio). Same protocol as [xSocket](https://github.com/pride-oO/xsocket) for Node.js and the browser: a Python server works with JS clients, a Python client works with a Node.js server.
18
+
19
+ - Request-response with TTL (`sendReady`), clock synchronization with the server
20
+ - Automatic reconnect and session recovery within `socketObjectTimeout`
21
+ - Auth hook on the server (`use('auth')`)
22
+ - `ws://` or `wss://` depending on certificate, optional `perMessageDeflate` compression
23
+ - Binary messages: `bytes`, `bytearray` or `memoryview` as data, received as `bytes`
24
+ - Python 3.9+, depends on [websockets](https://pypi.org/project/websockets/)
25
+
26
+ ```
27
+ pip install xsocket
28
+ ```
29
+
30
+ The API mirrors the JS version. Methods that return a `Promise` in JS (`send`, `sendReady`, `response`) return an awaitable task here: the message is sent even without `await`. Event handlers can be regular functions or coroutines and receive the same arguments as in JS. `Client` and `Server` must be created inside a running event loop.
31
+
32
+ ## Server:
33
+ ```python
34
+ import asyncio
35
+ import xsocket
36
+
37
+ async def main():
38
+ xServer = xsocket.Server({
39
+ 'port' : 8752, # (Not necessary) Port listen (Default: 443 with certificate, 80 without)
40
+ 'socketObjectTimeout' : 30000, # (Not necessary) Allows the user to reconnect within 30 seconds (Default: 0)
41
+ 'perMessageDeflate' : True, # (Not necessary) Compress messages (Default: False)
42
+ 'https' : { # (Not necessary) Without certificate server uses ws:// instead of wss://
43
+ 'cert' : '<path>/cert.pem',
44
+ 'key' : '<path>/private.key'
45
+ }
46
+ })
47
+
48
+ # (Not necessary) Auth, by default all connections are allowed. Can be async
49
+ def auth(xSocketObject, success, failed):
50
+ if xSocketObject.getQueryVal('name'):
51
+ return success()
52
+ failed('Access denied')
53
+
54
+ xServer.use('auth', auth)
55
+
56
+ def onConnect(xSocketObject):
57
+ print('Connect event:', xSocketObject.getID(), xSocketObject.getIP(), xSocketObject.getOrigin(), xSocketObject.getQuery(), xSocketObject.getReqHeaders())
58
+
59
+ async def onData(xSocketData):
60
+ print('Data event', xSocketData.getID(), xSocketData.getType(), xSocketData.getData())
61
+ await asyncio.sleep(1)
62
+ try:
63
+ await xSocketData.response({'welcome' : 'Hello ' + xSocketObject.getQueryVal('name')})
64
+ # OR
65
+ # await xSocketData.response({}, 'Permision denied')
66
+ except xsocket.XSocketError as e:
67
+ print('Response send failed', xSocketData.getID(), e)
68
+
69
+ xSocketObject.on('data', onData)
70
+ xSocketObject.on('disconnect', lambda xSocketObject, msg: print('Disconnect event:', xSocketObject.getID(), msg))
71
+ xSocketObject.on('reconnect', lambda xSocketObject: print('Reconnect event:', xSocketObject.getID()))
72
+ xSocketObject.on('destroy', lambda xSocketObject, msg: print('Destroy event:', xSocketObject.getID(), msg))
73
+
74
+ xServer.on('connect', onConnect)
75
+ xServer.on('listen', lambda: print('Listen event', xServer.getPort()))
76
+
77
+ await xServer.listen()
78
+ await asyncio.Future()
79
+
80
+ asyncio.run(main())
81
+ ```
82
+
83
+ ## Client:
84
+ ```python
85
+ import asyncio
86
+ import xsocket
87
+
88
+ async def main():
89
+ xClient = xsocket.Client(['wss://server_domain.com:8752'], {'name' : 'Bob'}) # ws:// if server without certificate
90
+ # Self-signed certificate: xsocket.Client(url, query, {'ssl' : ssl.SSLContext})
91
+
92
+ xClient.on('reconnect', lambda xSocketObject: print('reconnect event', xSocketObject.getID()))
93
+ xClient.on('disconnect', lambda xSocketObject, msg: print('disconnect', xSocketObject.getID(), msg))
94
+ xClient.on('destroy', lambda xSocketObject, msg: print('destroy', xSocketObject.getID(), msg))
95
+
96
+ # Messages from server
97
+ xClient.on('data', lambda xSocketData: xSocketData.response({'ok' : True}))
98
+
99
+ xSocketObject = await xClient.getSocketObjectReady()
100
+ print('connect event:', xSocketObject.getID())
101
+
102
+ try:
103
+ xSocketData = await xClient.sendReady(
104
+ 'helloServer', # Name message
105
+ {'message' : 'Hello server'}, # Data message
106
+ 2000 # TTL ready response (Default: 3s)
107
+ )
108
+ print('Server responsed #' + xSocketData.getID(), xSocketData.getType(), xSocketData.getResData())
109
+ except xsocket.XSocketError as e:
110
+ print('Server responsed error', e)
111
+
112
+ # Binary message, response can be binary too
113
+ # xSocketData = await xClient.sendReady('file', b'\x01\x02\x03')
114
+
115
+ # Without waiting for response
116
+ # await xClient.send('log', {'message' : 'Hello'}, 0)
117
+
118
+ # Close connection and destroy session on server
119
+ await xClient.close()
120
+
121
+ asyncio.run(main())
122
+ ```
@@ -0,0 +1,108 @@
1
+ rfr# xSocket for Python
2
+
3
+ WebSocket client and server for Python (asyncio). Same protocol as [xSocket](https://github.com/pride-oO/xsocket) for Node.js and the browser: a Python server works with JS clients, a Python client works with a Node.js server.
4
+
5
+ - Request-response with TTL (`sendReady`), clock synchronization with the server
6
+ - Automatic reconnect and session recovery within `socketObjectTimeout`
7
+ - Auth hook on the server (`use('auth')`)
8
+ - `ws://` or `wss://` depending on certificate, optional `perMessageDeflate` compression
9
+ - Binary messages: `bytes`, `bytearray` or `memoryview` as data, received as `bytes`
10
+ - Python 3.9+, depends on [websockets](https://pypi.org/project/websockets/)
11
+
12
+ ```
13
+ pip install xsocket
14
+ ```
15
+
16
+ The API mirrors the JS version. Methods that return a `Promise` in JS (`send`, `sendReady`, `response`) return an awaitable task here: the message is sent even without `await`. Event handlers can be regular functions or coroutines and receive the same arguments as in JS. `Client` and `Server` must be created inside a running event loop.
17
+
18
+ ## Server:
19
+ ```python
20
+ import asyncio
21
+ import xsocket
22
+
23
+ async def main():
24
+ xServer = xsocket.Server({
25
+ 'port' : 8752, # (Not necessary) Port listen (Default: 443 with certificate, 80 without)
26
+ 'socketObjectTimeout' : 30000, # (Not necessary) Allows the user to reconnect within 30 seconds (Default: 0)
27
+ 'perMessageDeflate' : True, # (Not necessary) Compress messages (Default: False)
28
+ 'https' : { # (Not necessary) Without certificate server uses ws:// instead of wss://
29
+ 'cert' : '<path>/cert.pem',
30
+ 'key' : '<path>/private.key'
31
+ }
32
+ })
33
+
34
+ # (Not necessary) Auth, by default all connections are allowed. Can be async
35
+ def auth(xSocketObject, success, failed):
36
+ if xSocketObject.getQueryVal('name'):
37
+ return success()
38
+ failed('Access denied')
39
+
40
+ xServer.use('auth', auth)
41
+
42
+ def onConnect(xSocketObject):
43
+ print('Connect event:', xSocketObject.getID(), xSocketObject.getIP(), xSocketObject.getOrigin(), xSocketObject.getQuery(), xSocketObject.getReqHeaders())
44
+
45
+ async def onData(xSocketData):
46
+ print('Data event', xSocketData.getID(), xSocketData.getType(), xSocketData.getData())
47
+ await asyncio.sleep(1)
48
+ try:
49
+ await xSocketData.response({'welcome' : 'Hello ' + xSocketObject.getQueryVal('name')})
50
+ # OR
51
+ # await xSocketData.response({}, 'Permision denied')
52
+ except xsocket.XSocketError as e:
53
+ print('Response send failed', xSocketData.getID(), e)
54
+
55
+ xSocketObject.on('data', onData)
56
+ xSocketObject.on('disconnect', lambda xSocketObject, msg: print('Disconnect event:', xSocketObject.getID(), msg))
57
+ xSocketObject.on('reconnect', lambda xSocketObject: print('Reconnect event:', xSocketObject.getID()))
58
+ xSocketObject.on('destroy', lambda xSocketObject, msg: print('Destroy event:', xSocketObject.getID(), msg))
59
+
60
+ xServer.on('connect', onConnect)
61
+ xServer.on('listen', lambda: print('Listen event', xServer.getPort()))
62
+
63
+ await xServer.listen()
64
+ await asyncio.Future()
65
+
66
+ asyncio.run(main())
67
+ ```
68
+
69
+ ## Client:
70
+ ```python
71
+ import asyncio
72
+ import xsocket
73
+
74
+ async def main():
75
+ xClient = xsocket.Client(['wss://server_domain.com:8752'], {'name' : 'Bob'}) # ws:// if server without certificate
76
+ # Self-signed certificate: xsocket.Client(url, query, {'ssl' : ssl.SSLContext})
77
+
78
+ xClient.on('reconnect', lambda xSocketObject: print('reconnect event', xSocketObject.getID()))
79
+ xClient.on('disconnect', lambda xSocketObject, msg: print('disconnect', xSocketObject.getID(), msg))
80
+ xClient.on('destroy', lambda xSocketObject, msg: print('destroy', xSocketObject.getID(), msg))
81
+
82
+ # Messages from server
83
+ xClient.on('data', lambda xSocketData: xSocketData.response({'ok' : True}))
84
+
85
+ xSocketObject = await xClient.getSocketObjectReady()
86
+ print('connect event:', xSocketObject.getID())
87
+
88
+ try:
89
+ xSocketData = await xClient.sendReady(
90
+ 'helloServer', # Name message
91
+ {'message' : 'Hello server'}, # Data message
92
+ 2000 # TTL ready response (Default: 3s)
93
+ )
94
+ print('Server responsed #' + xSocketData.getID(), xSocketData.getType(), xSocketData.getResData())
95
+ except xsocket.XSocketError as e:
96
+ print('Server responsed error', e)
97
+
98
+ # Binary message, response can be binary too
99
+ # xSocketData = await xClient.sendReady('file', b'\x01\x02\x03')
100
+
101
+ # Without waiting for response
102
+ # await xClient.send('log', {'message' : 'Hello'}, 0)
103
+
104
+ # Close connection and destroy session on server
105
+ await xClient.close()
106
+
107
+ asyncio.run(main())
108
+ ```
@@ -0,0 +1,23 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "xsocket"
7
+ dynamic = ["version"]
8
+ description = "WebSocket client and server for Python (asyncio), compatible with xSocket for Node.js and the browser"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ authors = [{ name = "pr!de" }]
12
+ requires-python = ">=3.9"
13
+ dependencies = ["websockets>=13"]
14
+ keywords = ["socket", "ws", "wss", "websocket", "message", "client", "server", "asyncio"]
15
+
16
+ [project.urls]
17
+ Homepage = "https://github.com/pride-oO/xsocket"
18
+
19
+ [tool.setuptools]
20
+ packages = ["xsocket"]
21
+
22
+ [tool.setuptools.dynamic]
23
+ version = { attr = "xsocket.helpers.VERSION" }
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,7 @@
1
+ from .helpers import VERSION, EventEmitter, XSocketError
2
+ from .data import Data
3
+ from .socket_object import SocketObject
4
+ from .client import Client
5
+ from .server import Server
6
+
7
+ __version__ = VERSION
@@ -0,0 +1,197 @@
1
+ import asyncio
2
+ from urllib.parse import quote, urlencode
3
+
4
+ from websockets.asyncio.client import connect
5
+ from websockets.exceptions import ConnectionClosed
6
+
7
+ from . import helpers
8
+ from .helpers import Connection, EventEmitter, XSocketError, logger
9
+ from .socket_object import SocketObject
10
+
11
+
12
+ class Client(EventEmitter):
13
+
14
+ def __init__(self, serverUrl, query=None, settings=None):
15
+ super().__init__()
16
+ self.setMaxListeners(0)
17
+
18
+ if isinstance(serverUrl, str):
19
+ self._urlList = [serverUrl]
20
+ elif isinstance(serverUrl, (list, tuple)):
21
+ self._urlList = list(serverUrl)
22
+ else:
23
+ self._urlList = []
24
+ if not self._urlList:
25
+ raise XSocketError('Invalid urlList')
26
+ self._query = dict(query) if isinstance(query, dict) else {}
27
+ self._settings = dict(settings) if isinstance(settings, dict) else {}
28
+ self._ws = None
29
+ self._socketObject = None
30
+ self._isVersionWarned = False
31
+ self._isClose = False
32
+
33
+ self.on('connect', self.__onConnect)
34
+ self._runTask = helpers.promise(self.__run())
35
+
36
+ def getSettings(self):
37
+ return self._settings
38
+
39
+ def getSettingsVal(self, key, default=None):
40
+ return self.getSettings().get(key) or default
41
+
42
+ def getSocketObject(self):
43
+ return self._socketObject or False
44
+
45
+ async def getSocketObjectReady(self):
46
+ if self.getSocketObject():
47
+ return self.getSocketObject()
48
+ future = asyncio.get_running_loop().create_future()
49
+ self.once('connect', lambda socketObject: future.done() or future.set_result(socketObject))
50
+ return await future
51
+
52
+ def send(self, type, data=None, ttl=None):
53
+ return helpers.promise(self.__send(type, data, ttl))
54
+
55
+ async def __send(self, type, data, ttl):
56
+ socketObject = await self.getSocketObjectReady()
57
+ return await socketObject.send(type, data, ttl)
58
+
59
+ def sendReady(self, type, data=None, ttl=None):
60
+ return helpers.promise(self.__sendReady(type, data, ttl))
61
+
62
+ sendReadyResponse = sendReady
63
+
64
+ async def __sendReady(self, type, data, ttl):
65
+ socketObject = await self.getSocketObjectReady()
66
+ return await socketObject.sendReady(type, data, ttl)
67
+
68
+ def disconnect(self, msg=None):
69
+ socketObject = self.getSocketObject()
70
+ if not socketObject:
71
+ return False
72
+ return socketObject.disconnect(msg)
73
+
74
+ async def close(self, msg='end'):
75
+ self._isClose = True
76
+ if not self._ws or not self._ws.closeConnection(msg):
77
+ self._runTask.cancel()
78
+ await asyncio.gather(self._runTask, return_exceptions=True)
79
+ if self._socketObject:
80
+ self._socketObject.destroy(msg)
81
+
82
+ def __getUrl(self):
83
+ url = self._urlList[helpers.getRandInt(0, len(self._urlList) - 1)]
84
+ query = dict(self._query)
85
+ query['xV'] = helpers.VERSION
86
+ if self._socketObject and self._socketObject.getID() and self._socketObject.getSign():
87
+ query['xSOId'] = self._socketObject.getID()
88
+ query['xSOSign'] = self._socketObject.getSign()
89
+ return url + ('&' if '?' in url else '?') + urlencode({str(key): str(val) for key, val in query.items()}, quote_via=quote)
90
+
91
+ async def __run(self):
92
+ while not self._isClose:
93
+ try:
94
+ websocket = await connect(self.__getUrl(), ssl=self.getSettingsVal('ssl'), max_size=helpers.MAX_MESSAGE_SIZE)
95
+ except Exception as e:
96
+ self.emit('ws|error', str(e))
97
+ await asyncio.sleep(1)
98
+ continue
99
+ await self.__handleConnection(websocket)
100
+ if not self._isClose:
101
+ await asyncio.sleep(1)
102
+
103
+ async def __handleConnection(self, websocket):
104
+ ws = Connection(websocket)
105
+ self._ws = ws
106
+ if self._isClose:
107
+ ws.closeConnection('end')
108
+ self.emit('ws|connect', ws)
109
+ msgInc = 0
110
+
111
+ async def pongCheck():
112
+ nonlocal msgInc
113
+ while True:
114
+ await asyncio.sleep(21)
115
+ if not msgInc:
116
+ ws.closeConnection('pingTimeout')
117
+ msgInc = 0
118
+
119
+ pongTask = helpers.promise(pongCheck())
120
+ try:
121
+ async for message in websocket:
122
+ msgInc += 1
123
+ if message == 'ping':
124
+ ws.sendMessage('pong')
125
+ continue
126
+ try:
127
+ self.__onMessage(ws, message)
128
+ except Exception:
129
+ logger.exception('Message handler error')
130
+ except ConnectionClosed:
131
+ pass
132
+ finally:
133
+ pongTask.cancel()
134
+ await websocket.wait_closed()
135
+ ws.stop()
136
+ self._ws = None
137
+ msg = websocket.close_reason or 'close'
138
+ self.emit('ws|disconnect', msg)
139
+ if self._socketObject:
140
+ self._socketObject.emit('ws|disconnect', msg)
141
+
142
+ def __onMessage(self, ws, message):
143
+ body = helpers.decodeMessage(message)
144
+ if not body:
145
+ return
146
+ if body[0] != helpers.MSG_SO_UPDATE:
147
+ if self._socketObject:
148
+ self._socketObject.emit('ws|message', body)
149
+ return
150
+ body = body + [None] * (6 - len(body))
151
+ if body[4] != helpers.VERSION and not self._isVersionWarned:
152
+ self._isVersionWarned = True
153
+ logger.warning('Version mismatch client %s, server %s', helpers.VERSION, body[4] or 'unknown')
154
+ ws.setTimeOffset(body[5] - helpers.getTime() if helpers.isNumber(body[5]) else 0)
155
+ ws.setQuery({
156
+ 'xSOId' : str(body[1]),
157
+ 'xSOSign' : str(body[2]),
158
+ 'configure' : body[3] if isinstance(body[3], dict) else {}
159
+ })
160
+ if self._socketObject:
161
+ query = ws.getQuery()
162
+ if query['xSOId'] == self._socketObject.getID() and query['xSOSign'] == self._socketObject.getSign() and self._socketObject.update(ws):
163
+ return
164
+ self._socketObject.destroy('update')
165
+ self._socketObject = None
166
+ socketObject = self._socketObject = SocketObject()
167
+ socketObject.once('connect', lambda socketObject: self.emit('connect', socketObject))
168
+ socketObject.on('destroy', self.__onSocketObjectDestroy)
169
+ socketObject.on('disconnect', lambda socketObject, msg: self.emit('disconnect', socketObject, msg))
170
+ socketObject.on('reconnect', lambda socketObject: self.emit('reconnect', socketObject))
171
+ socketObject.on('data', lambda socketData: self.emit('data', socketData))
172
+ if not socketObject.update(ws):
173
+ ws.closeConnection('update')
174
+
175
+ def __onSocketObjectDestroy(self, socketObject, msg):
176
+ if self._socketObject is socketObject:
177
+ self._socketObject = None
178
+ self.emit('destroy', socketObject, msg)
179
+
180
+ def __onConnect(self, socketObject):
181
+ loop = asyncio.get_running_loop()
182
+ timerMap = {'destroy' : None}
183
+
184
+ def setDestroyTimeout(action, ttl=0):
185
+ if timerMap['destroy']:
186
+ timerMap['destroy'].cancel()
187
+ timerMap['destroy'] = None
188
+ if action:
189
+ timerMap['destroy'] = loop.call_later(max(ttl, 0) / 1000, socketObject.destroy, 'socketObjectTimeout')
190
+
191
+ def disconnectHandler(socketObject, msg):
192
+ configure = socketObject.getQueryVal('configure', {})
193
+ setDestroyTimeout(True, helpers.toInt(configure.get('socketObjectTimeout') if isinstance(configure, dict) else 0))
194
+
195
+ socketObject.on('reconnect', lambda socketObject: setDestroyTimeout(False))
196
+ socketObject.on('disconnect', disconnectHandler)
197
+ socketObject.once('destroy', lambda socketObject, msg: setDestroyTimeout(False))
@@ -0,0 +1,169 @@
1
+ import asyncio
2
+
3
+ from . import helpers
4
+ from .helpers import EventEmitter, XSocketError
5
+
6
+
7
+ class Data(EventEmitter):
8
+
9
+ STATUS_NEW = 0
10
+ STATUS_SEND = 1
11
+ STATUS_RESPONSE = 2
12
+ DEFAULT_TTL = 3000
13
+
14
+ def __init__(self, isOutput, dataObject=None, socketObject=None):
15
+ super().__init__()
16
+ self.setMaxListeners(0)
17
+ dataObject = dataObject if isinstance(dataObject, dict) else {}
18
+
19
+ ID = dataObject.get('ID')
20
+ type = dataObject.get('type')
21
+ data = dataObject.get('data')
22
+ ttl = dataObject.get('ttl')
23
+ time = dataObject.get('time')
24
+
25
+ self._isOutput = bool(isOutput)
26
+ self._socketObject = socketObject
27
+ self._ID = ID if isinstance(ID, str) else ''
28
+ self._type = type if isinstance(type, str) else ''
29
+ self._data = data if helpers.isObject(data) else {}
30
+ self._ttl = ttl if helpers.isInt(ttl) and ttl >= 0 else Data.DEFAULT_TTL
31
+ now = socketObject.getTime() if socketObject else helpers.getTime()
32
+ self._time = time if helpers.isNumber(time) and time > 0 else now
33
+ self._resData = {}
34
+ self._error = False
35
+ self._status = Data.STATUS_NEW
36
+ self._destroy = False
37
+ self._ttlTimeout = asyncio.get_running_loop().call_later(
38
+ max(0, min(self._ttl, self._ttl - (now - self._time))) / 1000,
39
+ self.destroy, 'ttl', True
40
+ )
41
+
42
+ def getID(self):
43
+ return str(self._ID)
44
+
45
+ def getType(self):
46
+ return str(self._type)
47
+
48
+ def getTTL(self):
49
+ return self._ttl
50
+
51
+ def getTime(self):
52
+ return self._time
53
+
54
+ def getData(self):
55
+ return self._data
56
+
57
+ def getDataVal(self, key, default=None):
58
+ try:
59
+ return self.getData()[key] or default
60
+ except (KeyError, IndexError, TypeError):
61
+ return default
62
+
63
+ def getResData(self):
64
+ return self._resData
65
+
66
+ def getResDataVal(self, key, default=None):
67
+ try:
68
+ return self.getResData()[key] or default
69
+ except (KeyError, IndexError, TypeError):
70
+ return default
71
+
72
+ def getStatus(self):
73
+ return self._status
74
+
75
+ def getDestroy(self):
76
+ return self._destroy or False
77
+
78
+ def getSocketObject(self):
79
+ return self._socketObject
80
+
81
+ def getError(self):
82
+ return self._error or False
83
+
84
+ def isNew(self):
85
+ return self._status == Data.STATUS_NEW
86
+
87
+ def isSend(self):
88
+ return self._status == Data.STATUS_SEND
89
+
90
+ def isResponse(self):
91
+ return self._status == Data.STATUS_RESPONSE
92
+
93
+ def response(self, data=None, error=None):
94
+ return helpers.promise(self.__response(data, error))
95
+
96
+ async def __response(self, data, error):
97
+ if self._isOutput:
98
+ raise XSocketError('Output data')
99
+ if self._destroy or self.isResponse() or not self._ttl:
100
+ raise XSocketError('destroy')
101
+ self.setResData(data)
102
+ self.setError(error)
103
+ future = asyncio.get_running_loop().create_future()
104
+
105
+ def responseHandler(resData, error):
106
+ if not future.done():
107
+ future.set_result(self)
108
+
109
+ def destroyHandler(msg):
110
+ if not future.done():
111
+ future.set_exception(XSocketError(msg))
112
+
113
+ self.once('response', responseHandler)
114
+ self.once('destroy', destroyHandler)
115
+ try:
116
+ ws = await self._socketObject.getWsReady()
117
+ if ws.sendObject([
118
+ helpers.MSG_SD_UPDATE,
119
+ int(self._ID),
120
+ self.getResData(),
121
+ self.getError()
122
+ ]):
123
+ self.setStatus(Data.STATUS_RESPONSE)
124
+ else:
125
+ self.destroy('Send failed')
126
+ except Exception as e:
127
+ self.destroy(str(e) or 'Send failed')
128
+ return await future
129
+
130
+ def setStatus(self, status):
131
+ if (
132
+ status not in (Data.STATUS_SEND, Data.STATUS_RESPONSE) or
133
+ self._status == Data.STATUS_RESPONSE or
134
+ self._status == status
135
+ ):
136
+ return False
137
+ self._status = status
138
+ self.emit('setStatus', status)
139
+ if status == Data.STATUS_RESPONSE:
140
+ self.emit('response', self.getResData(), self.getError())
141
+ self.destroy('response')
142
+ return True
143
+
144
+ def setError(self, errorMsg):
145
+ if not isinstance(errorMsg, str):
146
+ return False
147
+ self._error = errorMsg
148
+ return True
149
+
150
+ def setResData(self, data):
151
+ if data is None:
152
+ data = {}
153
+ if not helpers.isObject(data):
154
+ return False
155
+ self._resData = data
156
+ return True
157
+
158
+ def destroy(self, msg=None, isSetError=False):
159
+ if self._destroy:
160
+ return False
161
+ self._destroy = msg if isinstance(msg, str) and msg else 'destroy'
162
+ self._ttlTimeout.cancel()
163
+ if isSetError and not self._error:
164
+ self.setError(self._destroy)
165
+ if self._status != Data.STATUS_RESPONSE:
166
+ self.setStatus(Data.STATUS_RESPONSE)
167
+ self.emit('destroy', self._destroy)
168
+ self.clearAllListeners()
169
+ return True