vortexpy 4.0.23__py3-none-any.whl

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.
Files changed (87) hide show
  1. vortex/DataWrapTuple.py +27 -0
  2. vortex/DeferUtil.py +224 -0
  3. vortex/Jsonable.py +304 -0
  4. vortex/Payload.py +115 -0
  5. vortex/PayloadEndpoint.py +216 -0
  6. vortex/PayloadEndpointTest.py +164 -0
  7. vortex/PayloadEnvelope.py +166 -0
  8. vortex/PayloadFilterKeys.py +15 -0
  9. vortex/PayloadIO.py +152 -0
  10. vortex/PayloadPriority.py +11 -0
  11. vortex/PayloadResponse.py +173 -0
  12. vortex/PayloadResponseTest.py +39 -0
  13. vortex/PayloadTest.py +136 -0
  14. vortex/PerformTestActionTuple.py +30 -0
  15. vortex/SerialiseUtil.py +273 -0
  16. vortex/TestTuple.py +61 -0
  17. vortex/Tuple.py +968 -0
  18. vortex/TupleAction.py +72 -0
  19. vortex/TupleActionVortex.py +5 -0
  20. vortex/TupleDataForTest.py +25 -0
  21. vortex/TuplePerfTest.py +71 -0
  22. vortex/TuplePolymorphicTupleTypeFieldArgTest.py +74 -0
  23. vortex/TupleRestfulTest.py +140 -0
  24. vortex/TupleSelector.py +105 -0
  25. vortex/TupleTest.py +127 -0
  26. vortex/VortexABC.py +55 -0
  27. vortex/VortexClientHttp.py +308 -0
  28. vortex/VortexClientHttpTest.py +71 -0
  29. vortex/VortexClientTcp.py +355 -0
  30. vortex/VortexClientWebsocketFactory.py +383 -0
  31. vortex/VortexConnectionABC.py +88 -0
  32. vortex/VortexFactory.py +932 -0
  33. vortex/VortexPayloadProtocol.py +180 -0
  34. vortex/VortexServer.py +367 -0
  35. vortex/VortexServerConnection.py +127 -0
  36. vortex/VortexServerHttpResource.py +193 -0
  37. vortex/VortexServerTcp.py +104 -0
  38. vortex/VortexServerWebsocket.py +287 -0
  39. vortex/VortexTcpMemoryLeakPayloadTest.py +611 -0
  40. vortex/VortexTcpMemoryLeakRawTest.py +87 -0
  41. vortex/VortexTest.py +22 -0
  42. vortex/VortexUtil.py +314 -0
  43. vortex/VortexWritePushProducer.py +321 -0
  44. vortex/__init__.py +10 -0
  45. vortex/data_loader/TupleDataLiveLockManager.py +383 -0
  46. vortex/data_loader/TupleDataLoader.py +326 -0
  47. vortex/data_loader/TupleDataLoaderDelegate.py +90 -0
  48. vortex/data_loader/TupleDataLoaderTupleABC.py +10 -0
  49. vortex/data_loader/TupleDataLoaderTuples.py +53 -0
  50. vortex/data_loader/__init__.py +0 -0
  51. vortex/event_bus/TupleChangeEventABC.py +15 -0
  52. vortex/event_bus/TupleChangeEventBus.py +192 -0
  53. vortex/event_bus/TupleChangeEventBusObserverABC.py +11 -0
  54. vortex/event_bus/__init__.py +0 -0
  55. vortex/handler/TupleActionProcessor.py +270 -0
  56. vortex/handler/TupleActionProcessorProxy.py +218 -0
  57. vortex/handler/TupleActionProcessorTestHandler.py +27 -0
  58. vortex/handler/TupleDataActionClient.py +67 -0
  59. vortex/handler/TupleDataObservableCache.py +201 -0
  60. vortex/handler/TupleDataObservableHandler.py +371 -0
  61. vortex/handler/TupleDataObservableProxyHandler.py +340 -0
  62. vortex/handler/TupleDataObservableTestHandler.py +60 -0
  63. vortex/handler/TupleDataObserverClient.py +158 -0
  64. vortex/handler/VortexJSTupleLoaderTestHandler.py +42 -0
  65. vortex/handler/__init__.py +0 -0
  66. vortex/restful/GzippedDataHttpClient.py +170 -0
  67. vortex/restful/GzippedDataHttpClientTest.py +103 -0
  68. vortex/restful/GzippedPayloadHttpClient.py +181 -0
  69. vortex/restful/RestfulHttpClient.py +107 -0
  70. vortex/restful/RestfulHttpClientTest.py +60 -0
  71. vortex/restful/RestfulResource.py +153 -0
  72. vortex/restful/RestfulResourceTest.py +100 -0
  73. vortex/restful/__init__.py +0 -0
  74. vortex/rpc/RPC.py +446 -0
  75. vortex/rpc/RPCTest.py +12 -0
  76. vortex/rpc/__init__.py +0 -0
  77. vortex/sqla_orm/OrmCrudHandler.py +431 -0
  78. vortex/sqla_orm/TupleProviderForDb.py +42 -0
  79. vortex/sqla_orm/VortexTupleJson.py +24 -0
  80. vortex/sqla_orm/__init__.py +0 -0
  81. vortex/storage/TupleStorageSqlite.py +359 -0
  82. vortex/storage/__init__.py +0 -0
  83. vortexpy-4.0.23.dist-info/LICENSE +21 -0
  84. vortexpy-4.0.23.dist-info/METADATA +43 -0
  85. vortexpy-4.0.23.dist-info/RECORD +87 -0
  86. vortexpy-4.0.23.dist-info/WHEEL +5 -0
  87. vortexpy-4.0.23.dist-info/top_level.txt +1 -0
@@ -0,0 +1,180 @@
1
+ """
2
+ * Created by Synerty Pty Ltd
3
+ *
4
+ * This software is open source, the MIT license applies.
5
+ *
6
+ * Website : http://www.synerty.com
7
+ * Support : support@synerty.com
8
+ """
9
+
10
+ import logging
11
+ from abc import ABCMeta, abstractmethod
12
+ from collections import deque
13
+
14
+ from twisted.internet import reactor
15
+ from twisted.internet.defer import inlineCallbacks
16
+ from twisted.internet.error import ConnectionDone, ConnectionLost
17
+ from twisted.internet.protocol import Protocol, connectionDone
18
+
19
+ from vortex.DeferUtil import nonConcurrentMethod
20
+ from vortex.PayloadEnvelope import PayloadEnvelope
21
+ from vortex.PayloadIO import PayloadIO
22
+
23
+ logger = logging.getLogger(name=__name__)
24
+
25
+
26
+ class VortexPayloadProtocol(Protocol, metaclass=ABCMeta):
27
+ def __init__(self, logger):
28
+ self._serverVortexUuid = None
29
+ self._serverVortexName = None
30
+ self._logger = logger
31
+
32
+ self._data = b""
33
+ self._vortexMsgsQueue = deque()
34
+
35
+ @abstractmethod
36
+ def _beat(self):
37
+ """
38
+ EG :
39
+ if self._vortexClient:
40
+ self._vortexClient._beat()
41
+ """
42
+
43
+ @abstractmethod
44
+ def _nameAndUuidReceived(self, name, uuid):
45
+ """
46
+ EG :
47
+ if self._vortexClient:
48
+ self._vortexClient._setNameAndUuid(name=self._serverVortexName,
49
+ uuid=self._serverVortexUuid)
50
+ """
51
+
52
+ @abstractmethod
53
+ def _createResponseSenderCallable(self):
54
+ """
55
+ EG :
56
+ def sendResponse(vortexMsgs: Union[VortexMsgList, bytes]):
57
+ return self._vortexClient.sendVortexMsg(vortexMsgs=vortexMsgs)
58
+ """
59
+
60
+ def vortexMsgReceived(self, vortexMsg):
61
+ """Vortex Msg Received
62
+
63
+ For protocols where framing is used, we don't need to process messages
64
+ with dots on the end, for those protocols, call this method to process
65
+ the whole message.
66
+ """
67
+ self._data = b""
68
+ self._beat()
69
+ if vortexMsg == b".":
70
+ return
71
+
72
+ self._vortexMsgsQueue.append(vortexMsg)
73
+
74
+ if self._vortexMsgsQueue and not self._processVortexMsgs.running:
75
+ reactor.callLater(0, self._processVortexMsgs)
76
+
77
+ def dataReceived(self, bytesIn):
78
+ """Data Received
79
+
80
+ This method is used for protocols that don't have framing. We use a
81
+ '.' to delineate messages.
82
+
83
+ """
84
+ if bytesIn.startswith(b"<"):
85
+ raise Exception("Not Logged In")
86
+
87
+ self._data += bytesIn
88
+ self._beat()
89
+ self._processData()
90
+
91
+ def connectionLost(self, reason=connectionDone):
92
+ reasonFailure = reason
93
+ self._processData()
94
+ if reasonFailure.check(ConnectionDone):
95
+ self._logger.info(
96
+ "Connection closed by other end (it may be shutting down)"
97
+ )
98
+
99
+ elif isinstance(reasonFailure.value, ConnectionLost):
100
+ self._logger.info(
101
+ "Connection to other end lost (We may be shutting down)"
102
+ )
103
+
104
+ else:
105
+ self._logger.error("Closed with error")
106
+ try:
107
+ self._logger.exception(reason.getErrorMessage())
108
+ self._logger.exception(reason.getTraceback())
109
+ except:
110
+ self._logger.exception(reasonFailure.value)
111
+
112
+ def _processData(self):
113
+ def getNextChunkIter():
114
+ try:
115
+ while True:
116
+ yield self._data.index(b".")
117
+ except ValueError:
118
+ # There is no '.' in it, wait for more data.
119
+ return
120
+
121
+ if self._data:
122
+ for nextChunk in getNextChunkIter():
123
+ vortexMsg = self._data[:nextChunk]
124
+ self._data = self._data[nextChunk + 1 :]
125
+
126
+ # If we get two heartbeats in a row, this will be false
127
+ if len(vortexMsg):
128
+ self._vortexMsgsQueue.append(vortexMsg)
129
+
130
+ if self._vortexMsgsQueue and not self._processVortexMsgs.running:
131
+ reactor.callLater(0, self._processVortexMsgs)
132
+
133
+ @inlineCallbacks
134
+ @nonConcurrentMethod
135
+ def _processVortexMsgs(self):
136
+ while self._vortexMsgsQueue:
137
+ vortexMsg = self._vortexMsgsQueue.popleft()
138
+
139
+ try:
140
+ payloadEnvelope = yield PayloadEnvelope().fromVortexMsgDefer(
141
+ vortexMsg
142
+ )
143
+
144
+ if payloadEnvelope.isEmpty():
145
+ self._processServerInfoPayload(payloadEnvelope)
146
+ else:
147
+ self._deliverPayload(payloadEnvelope)
148
+
149
+ except Exception as e:
150
+ print(vortexMsg)
151
+ print(e)
152
+ self._logger.exception(e)
153
+ raise
154
+
155
+ def _processServerInfoPayload(self, payload):
156
+ """Process Server Info Payload
157
+
158
+ The first payload a server sends to the client contains information about it's
159
+ self.
160
+
161
+ """
162
+
163
+ if PayloadEnvelope.vortexUuidKey in payload.filt:
164
+ self._serverVortexUuid = payload.filt[PayloadEnvelope.vortexUuidKey]
165
+
166
+ if PayloadEnvelope.vortexNameKey in payload.filt:
167
+ self._serverVortexName = payload.filt[PayloadEnvelope.vortexNameKey]
168
+
169
+ self._nameAndUuidReceived(
170
+ name=self._serverVortexName, uuid=self._serverVortexUuid
171
+ )
172
+
173
+ def _deliverPayload(self, payloadEnvelope):
174
+ PayloadIO().process(
175
+ payloadEnvelope,
176
+ vortexUuid=self._serverVortexUuid,
177
+ vortexName=self._serverVortexName,
178
+ httpSession=None,
179
+ sendResponse=self._createResponseSenderCallable(),
180
+ )
vortex/VortexServer.py ADDED
@@ -0,0 +1,367 @@
1
+ """
2
+ * Created by Synerty Pty Ltd
3
+ *
4
+ * This software is open source, the MIT license applies.
5
+ *
6
+ * Website : http://www.synerty.com
7
+ * Support : support@synerty.com
8
+ """
9
+
10
+ import logging
11
+ import uuid
12
+ from typing import Any
13
+ from typing import Dict
14
+ from typing import Optional
15
+ from typing import Union
16
+ from weakref import WeakValueDictionary
17
+
18
+ from twisted.internet import reactor
19
+ from twisted.internet import task
20
+ from twisted.internet.defer import Deferred
21
+ from twisted.internet.defer import inlineCallbacks
22
+ from twisted.python.components import registerAdapter
23
+ from twisted.web.server import Session
24
+ from zope.interface import Attribute
25
+ from zope.interface import Interface
26
+ from zope.interface.declarations import implementer
27
+
28
+ from .DeferUtil import isMainThread
29
+ from .DeferUtil import vortexLogFailure
30
+ from .PayloadEnvelope import PayloadEnvelope
31
+ from .PayloadEnvelope import VortexMsgList
32
+ from .PayloadFilterKeys import rapuiServerEcho
33
+ from .PayloadIO import PayloadIO
34
+ from .PayloadPriority import DEFAULT_PRIORITY
35
+ from .VortexABC import VortexABC
36
+ from .VortexABC import VortexInfo
37
+ from .VortexUtil import logLargeMessages
38
+
39
+ logger = logging.getLogger(__name__)
40
+
41
+ """ ---------------------------------------------------------------------------
42
+ VortexServer
43
+ """
44
+
45
+ HEART_BEAT_PERIOD = 10.0
46
+ HEART_BEAT_TIMEOUT = 180.0
47
+
48
+
49
+ class VortexServer(VortexABC):
50
+ """VortexServer
51
+ The static instance of the controller
52
+ """
53
+
54
+ _DEBUG_LOGGING = False
55
+
56
+ def __init__(self, name: str, requiresBase64Encoding=True) -> None:
57
+ # Simple initialisations up the top
58
+ self._name = name
59
+ self._requiresBase64Encoding = requiresBase64Encoding
60
+ self._uuid = str(uuid.uuid4())
61
+ self._shutdown = False
62
+
63
+ # Store all our sessions
64
+ self._httpSessionsBySessionUuid: WeakValueDictionary = (
65
+ WeakValueDictionary()
66
+ )
67
+ self._connectionByVortexUuid: Dict[str, Any] = {}
68
+ self._remoteVortexInfo: tuple[VortexInfo] = tuple()
69
+
70
+ def name(self):
71
+ return self._name
72
+
73
+ def uuid(self):
74
+ return self._uuid
75
+
76
+ @property
77
+ def requiresBase64Encoding(self):
78
+ return self._requiresBase64Encoding
79
+
80
+ @property
81
+ def localVortexInfo(self) -> VortexInfo:
82
+ return VortexInfo(name=self._name, uuid=self._uuid)
83
+
84
+ @property
85
+ def remoteVortexInfo(self) -> tuple[VortexInfo]:
86
+ return self._remoteVortexInfo
87
+
88
+ def _connectionsChanged(
89
+ self, vortexClosedNames: list[str], nowOnline: bool
90
+ ):
91
+ vortexInfos = []
92
+
93
+ for conn in self._connectionByVortexUuid.values():
94
+ vortexInfos.append(
95
+ VortexInfo(
96
+ name=conn.remoteVortexName, uuid=conn.remoteVortexUuid
97
+ )
98
+ )
99
+
100
+ self._remoteVortexInfo = tuple(vortexInfos)
101
+
102
+ from vortex.VortexFactory import VortexFactory
103
+
104
+ VortexFactory.connectionChanged()
105
+
106
+ for vortexName in vortexClosedNames:
107
+ # noinspection PyProtectedMember
108
+ VortexFactory._notifyOfVortexStatusChange(
109
+ vortexName, online=nowOnline
110
+ )
111
+
112
+ @property
113
+ def connections(self) -> Dict[str, Any]:
114
+ return self._connectionByVortexUuid
115
+
116
+ def isShutdown(self):
117
+ return self._shutdown
118
+
119
+ def shutdown(self):
120
+ self._shutdown = True
121
+
122
+ for sess in list(self._httpSessionsBySessionUuid.values()):
123
+ sess.expire()
124
+
125
+ for conn in list(self._connectionByVortexUuid.values()):
126
+ conn.close()
127
+
128
+ def vortexUuids(self):
129
+ return list(self._connectionByVortexUuid.keys())
130
+
131
+ def isVortexAlive(self, vortexUuid):
132
+ return vortexUuid in self._connectionByVortexUuid
133
+
134
+ def vortexClientIpPort(self, vortexUuid):
135
+ connection = self._connectionByVortexUuid.get(vortexUuid)
136
+ if not connection:
137
+ return None
138
+
139
+ return "%s:%s" % (connection.ip, connection.port)
140
+
141
+ def connectionOpened(self, httpSession, vortexConnection):
142
+ if self._DEBUG_LOGGING:
143
+ logger.debug("VortexServer - connectionOpened")
144
+
145
+ vortexUuid = vortexConnection.remoteVortexUuid
146
+ assert vortexUuid
147
+
148
+ if httpSession:
149
+ # If this is a new session, Make sure we have an expire callback on it
150
+ if httpSession.uid not in self._httpSessionsBySessionUuid:
151
+ self._httpSessionsBySessionUuid[httpSession.uid] = httpSession
152
+ httpSession.notifyOnExpire(
153
+ lambda: self._sessionExpired(httpSession.uid)
154
+ )
155
+
156
+ # Update the connection dict in the sessions object
157
+ httpSessionConns = VortexSessionI(httpSession).connections
158
+ httpSessionConns[vortexUuid] = vortexConnection
159
+
160
+ # Get the old connection if it exists
161
+ if vortexUuid in self._connectionByVortexUuid:
162
+ self._connectionByVortexUuid[vortexUuid].close()
163
+
164
+ # Update the _connectionsByvortexUuid
165
+ self._connectionByVortexUuid[vortexUuid] = vortexConnection
166
+ self._connectionsChanged(
167
+ [vortexConnection.remoteVortexName], nowOnline=True
168
+ )
169
+
170
+ def connectionClosed(self, conn):
171
+ if self._DEBUG_LOGGING:
172
+ logger.debug("VortexServer - connectionClosed")
173
+
174
+ vortexUuid = conn.remoteVortexUuid
175
+
176
+ if conn.httpSessionUuid in self._httpSessionsBySessionUuid:
177
+ session = self._httpSessionsBySessionUuid[conn.httpSessionUuid]
178
+ assert session
179
+
180
+ # cleanup _sessionsBySessionUuid
181
+ conns = VortexSessionI(session).connections
182
+ if conns[vortexUuid] == conn:
183
+ del conns[vortexUuid]
184
+
185
+ # cleanup _connectionsByvortexUuid
186
+ vortexClosedNames = []
187
+ if conn.remoteVortexUuid in self._connectionByVortexUuid:
188
+ if self._connectionByVortexUuid[conn.remoteVortexUuid] == conn:
189
+ del self._connectionByVortexUuid[conn.remoteVortexUuid]
190
+ vortexClosedNames.append(conn.remoteVortexName)
191
+
192
+ self._connectionsChanged(vortexClosedNames, nowOnline=False)
193
+
194
+ def _sessionExpired(self, httpSessionUuid):
195
+ logger.debug(
196
+ "VortexServer - _sessionExpired, Session %s has expired"
197
+ % httpSessionUuid
198
+ )
199
+
200
+ # cleanup _sessionsBySessionUuid
201
+ del self._httpSessionsBySessionUuid[httpSessionUuid]
202
+
203
+ # cleanup _connectionsByvortexUuid
204
+ vortexClosedNames = []
205
+ for vortexUuid, conn in list(self._connectionByVortexUuid.items()):
206
+ if conn.httpSessionUuid == httpSessionUuid:
207
+ del self._connectionByVortexUuid[vortexUuid]
208
+ vortexClosedNames.append(conn.remoteVortexName)
209
+
210
+ self._connectionsChanged(vortexClosedNames, nowOnline=False)
211
+
212
+ def payloadReveived(self, httpSession, vortexUuid, vortexName, payload):
213
+ # print "VortexServer - payloadReveived"
214
+
215
+ if rapuiServerEcho in payload.filt:
216
+ payload.filt.pop(rapuiServerEcho)
217
+ self.sendVortexMsg(payload.toVortexMsg(), vortexUuid)
218
+
219
+ def sendResponse(
220
+ vortexMsg: bytes, priority: int = DEFAULT_PRIORITY
221
+ ) -> Deferred:
222
+ """Send Back
223
+
224
+ Sends a response back to where this payload come from.
225
+
226
+ """
227
+ return self.sendVortexMsg(
228
+ vortexMsg, vortexUuid=vortexUuid, priority=priority
229
+ )
230
+
231
+ PayloadIO().process(
232
+ payload,
233
+ vortexUuid=vortexUuid,
234
+ vortexName=vortexName,
235
+ httpSession=httpSession,
236
+ sendResponse=sendResponse,
237
+ )
238
+
239
+ def sendVortexMsg(
240
+ self,
241
+ vortexMsgs: Union[VortexMsgList, bytes, None] = None,
242
+ vortexUuid: Optional[str] = None,
243
+ priority: int = DEFAULT_PRIORITY,
244
+ ):
245
+ """Send Vortex Msg
246
+
247
+ Sends the vortex message to any conencted clients with vortexUuid.
248
+ Or broadcast it to all connected vortex clients if it's None
249
+
250
+ :param vortexMsgs: The vortex message to send
251
+ :param vortexUuid: The vortexUuid of the client to send to.
252
+ """
253
+ if vortexMsgs is None:
254
+ vortexMsgs = [PayloadEnvelope().toVortexMsg()]
255
+
256
+ logLargeMessages(logger, vortexMsgs, vortexUuid)
257
+
258
+ if isMainThread():
259
+ return self._sendVortexMsgLater(
260
+ vortexMsgs, vortexUuid=vortexUuid, priority=priority
261
+ )
262
+
263
+ return task.deferLater(
264
+ reactor,
265
+ 0,
266
+ self._sendVortexMsgLater,
267
+ vortexMsgs,
268
+ vortexUuid=vortexUuid,
269
+ priority=priority,
270
+ )
271
+
272
+ @inlineCallbacks
273
+ def _sendVortexMsgLater(
274
+ self,
275
+ vortexMsgs: Union[VortexMsgList, bytes],
276
+ vortexUuid: Optional[str],
277
+ priority: int,
278
+ ):
279
+ """Send the message.
280
+
281
+ Send it later,
282
+ This also means it doesn't matter what thread this is called from
283
+
284
+ """
285
+ from vortex.VortexFactory import VortexFactory
286
+
287
+ yield None
288
+
289
+ if not isinstance(vortexMsgs, list):
290
+ vortexMsgs = [vortexMsgs]
291
+
292
+ # Deliver locally
293
+ if vortexUuid == self._uuid:
294
+ for vortexMsg in vortexMsgs:
295
+
296
+ def sendResponse(vortexMsg_, priority_=DEFAULT_PRIORITY):
297
+ self._sendVortexMsgLater(vortexMsg_, self._uuid, priority_)
298
+
299
+ def cb(payloadEnvelope: PayloadEnvelope) -> None:
300
+ PayloadIO().process(
301
+ payloadEnvelope,
302
+ vortexUuid=self._uuid,
303
+ vortexName=self._name,
304
+ httpSession=None,
305
+ sendResponse=sendResponse,
306
+ )
307
+
308
+ d = PayloadEnvelope.fromVortexMsgDefer(vortexMsg)
309
+ d.addCallback(cb)
310
+ d.addErrback(vortexLogFailure, logger, consumeError=True)
311
+
312
+ return
313
+
314
+ # If any transports require base64 encoding, then encode them all
315
+ if self.requiresBase64Encoding:
316
+ for index, vortexMsg in enumerate(vortexMsgs):
317
+ if vortexMsg.startswith(b"{"):
318
+ vortexMsg = yield PayloadEnvelope.base64EncodeDefer(
319
+ vortexMsg
320
+ )
321
+ vortexMsgs[index] = vortexMsg + b"."
322
+
323
+ if vortexUuid is None:
324
+ conns = self._connectionByVortexUuid.values()
325
+ elif vortexUuid in self._connectionByVortexUuid:
326
+ conns = [self._connectionByVortexUuid[vortexUuid]]
327
+ else:
328
+ logger.debug(
329
+ "Vortex %s offline, not sending message of size %s",
330
+ vortexUuid,
331
+ ", ".join(["{:,}".format(len(m)) for m in vortexMsgs]),
332
+ )
333
+ return False
334
+
335
+ for conn in conns:
336
+ for vortexMsg in vortexMsgs:
337
+
338
+ # noinspection PyProtectedMember
339
+ if VortexFactory._DEBUG_LOGGING:
340
+ logger.debug(
341
+ "Sending vortexMsg to %s", conn.remoteVortexUuid
342
+ )
343
+
344
+ conn.write(vortexMsg, priority)
345
+
346
+ return True
347
+
348
+
349
+ """ ---------------------------------------------------------------------------
350
+ VortexServer Session
351
+ """
352
+
353
+
354
+ class VortexSessionI(Interface):
355
+ connections: Dict = Attribute(
356
+ "VortexServer connections for this session, by window uuid"
357
+ )
358
+
359
+
360
+ @implementer(VortexSessionI)
361
+ class VortexSession(object):
362
+ def __init__(self, session):
363
+ session.sessionTimeout = HEART_BEAT_TIMEOUT
364
+ self.connections = {}
365
+
366
+
367
+ registerAdapter(VortexSession, Session, VortexSessionI)
@@ -0,0 +1,127 @@
1
+ """
2
+ * Created by Synerty Pty Ltd
3
+ *
4
+ * This software is open source, the MIT license applies.
5
+ *
6
+ * Website : http://www.synerty.com
7
+ * Support : support@synerty.com
8
+ """
9
+ import logging
10
+ from datetime import datetime
11
+
12
+ import pytz
13
+ from twisted.internet import task
14
+
15
+ from .PayloadPriority import DEFAULT_PRIORITY
16
+ from .VortexConnectionABC import VortexConnectionABC
17
+ from .VortexServer import VortexServer, HEART_BEAT_PERIOD, HEART_BEAT_TIMEOUT
18
+ from .VortexWritePushProducer import VortexWritePushProducer
19
+
20
+ logger = logging.getLogger(name=__name__)
21
+
22
+
23
+ class VortexServerConnection(VortexConnectionABC):
24
+ def __init__(
25
+ self,
26
+ vortexServer: VortexServer,
27
+ remoteVortexUuid: str,
28
+ remoteVortexName: str,
29
+ httpSession,
30
+ transport,
31
+ addr,
32
+ ) -> None:
33
+ VortexConnectionABC.__init__(
34
+ self,
35
+ logger,
36
+ vortexServer,
37
+ remoteVortexUuid=remoteVortexUuid,
38
+ remoteVortexName=remoteVortexName,
39
+ httpSessionUuid=httpSession,
40
+ )
41
+
42
+ self._lastHeartBeatTime = datetime.now(pytz.utc)
43
+ self._lastHeartBeatCheckTime = datetime.now(pytz.utc)
44
+
45
+ self._transport = transport
46
+ self._addr = addr
47
+
48
+ # Start our heart beat
49
+ self._beatLoopingCall = task.LoopingCall(self._beat)
50
+ d = self._beatLoopingCall.start(HEART_BEAT_PERIOD, now=False)
51
+ d.addErrback(lambda f: logger.exception(f.value))
52
+
53
+ self._producer = None
54
+
55
+ # Register the producer if there isn't one already.
56
+ # The websocket server already has one.
57
+ if not self._transport.producer:
58
+ self._producer = VortexWritePushProducer(
59
+ transport, lambda: self.close(), remoteVortexName
60
+ )
61
+
62
+ transport.registerProducer(self._producer, True)
63
+
64
+ def beatReceived(self):
65
+ self._lastHeartBeatTime = datetime.now(pytz.utc)
66
+
67
+ def _beat(self):
68
+ # If we're closed, do nothing
69
+ if self._closed:
70
+ if self._beatLoopingCall.running:
71
+ self._beatLoopingCall.stop()
72
+ return
73
+
74
+ beatTimeout = (
75
+ datetime.now(pytz.utc) - self._lastHeartBeatTime
76
+ ).seconds > HEART_BEAT_TIMEOUT
77
+
78
+ # If we've been asleep, then make note of that (VM suspended)
79
+ checkTimout = (
80
+ datetime.now(pytz.utc) - self._lastHeartBeatCheckTime
81
+ ).seconds > HEART_BEAT_TIMEOUT
82
+
83
+ # Mark that we've just checked it
84
+ self._lastHeartBeatCheckTime = datetime.now(pytz.utc)
85
+
86
+ if checkTimout:
87
+ self._lastHeartBeatTime = datetime.now(pytz.utc)
88
+ return
89
+
90
+ # If we havn't heard from the client, then close the connection
91
+ if beatTimeout:
92
+ self._beatLoopingCall.stop()
93
+ self._timedOut = True
94
+ self.close()
95
+ return
96
+
97
+ self._write(b".", DEFAULT_PRIORITY)
98
+
99
+ @property
100
+ def ip(self):
101
+ return self._addr.host
102
+
103
+ @property
104
+ def port(self):
105
+ return self._addr.port
106
+
107
+ def write(self, payloadVortexStr: bytes, priority: int = DEFAULT_PRIORITY):
108
+ assert not self._closed
109
+ self._write(payloadVortexStr, priority)
110
+
111
+ def _write(self, payloadVortexStr: bytes, priority: int):
112
+ if self._producer:
113
+ self._producer.write(payloadVortexStr, priority)
114
+ else:
115
+ self._transport.write(payloadVortexStr)
116
+
117
+ def close(self):
118
+ if self._beatLoopingCall.running:
119
+ self._beatLoopingCall.stop()
120
+
121
+ self._transport.loseConnection()
122
+
123
+ def transportClosed(self):
124
+ if self._producer and hasattr(self._producer, "close"):
125
+ self._producer.close()
126
+ self._producer = None
127
+ VortexConnectionABC.close(self)