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,193 @@
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 collections
11
+ import logging
12
+ from _pyio import BytesIO
13
+ from typing import Deque
14
+
15
+ from twisted.internet import task
16
+ from twisted.internet.defer import inlineCallbacks
17
+ from twisted.web.resource import Resource
18
+ from twisted.web.server import NOT_DONE_YET
19
+
20
+ from vortex.PayloadEnvelope import PayloadEnvelope
21
+ from vortex.VortexServer import HEART_BEAT_PERIOD
22
+ from .PayloadPriority import DEFAULT_PRIORITY
23
+ from .VortexConnectionABC import VortexConnectionABC
24
+ from .VortexServer import VortexServer
25
+
26
+ logger = logging.getLogger(name=__name__)
27
+
28
+
29
+ class VortexServerHttpResource(Resource):
30
+ """VortexServer Resource
31
+ This resource is the server endpoint for the vortex.
32
+ """
33
+
34
+ isLeaf = True
35
+
36
+ def __init__(self, vortex: VortexServer) -> None:
37
+ Resource.__init__(self)
38
+ self.__vortex = vortex
39
+
40
+ def render_GET(self, request):
41
+ request.code = 405
42
+ return b"You have reached the vortex. It only likes POST methods."
43
+
44
+ def render_POST(self, request):
45
+
46
+ from vortex.VortexFactory import VortexFactory
47
+
48
+ if not VortexFactory.canConnect(str(request.client.host)):
49
+ return
50
+
51
+ remoteVortexUuid = request.args[b"vortexUuid"][0].decode()
52
+ remoteVortexName = request.args[b"vortexName"][0].decode()
53
+
54
+ if self.__vortex.isShutdown():
55
+ return None
56
+
57
+ httpSession = request.getSession()
58
+ conn = VortexResourceConnection(
59
+ self.__vortex, remoteVortexUuid, remoteVortexName, request
60
+ )
61
+
62
+ # Send a heart beat down the new connection, tell it who we are.
63
+ connectPayloadFilt = {}
64
+ connectPayloadFilt[PayloadEnvelope.vortexUuidKey] = self.__vortex.uuid()
65
+ connectPayloadFilt[PayloadEnvelope.vortexNameKey] = self.__vortex.name()
66
+ conn.write(PayloadEnvelope(filt=connectPayloadFilt).toVortexMsg())
67
+
68
+ data = request.content.read()
69
+ if len(data):
70
+ for vortexStr in data.strip(b".").split(b"."):
71
+ self._processVortexMsg(
72
+ httpSession, conn, vortexStr.decode("UTF-8")
73
+ )
74
+
75
+ # Request will be around for a while, do some cleanups
76
+ request.content = BytesIO()
77
+ request.args = {}
78
+
79
+ self.__vortex.connectionOpened(httpSession, conn)
80
+
81
+ def connClosed(err):
82
+ logger.debug("VortexServer connection ended by client")
83
+ self.__vortex.connectionClosed(conn)
84
+
85
+ request.notifyFinish().addErrback(connClosed)
86
+
87
+ return NOT_DONE_YET
88
+
89
+ @inlineCallbacks
90
+ def _processVortexMsg(self, httpSession, conn, vortexMsg):
91
+ payload = yield PayloadEnvelope().fromVortexMsgDefer(vortexMsg)
92
+ self.__vortex.payloadReveived(
93
+ httpSession=httpSession,
94
+ vortexUuid=conn.remoteVortexUuid,
95
+ vortexName=conn.remoteVortexName,
96
+ payload=payload,
97
+ )
98
+
99
+
100
+ class VortexResourceConnection(VortexConnectionABC):
101
+ def __init__(
102
+ self,
103
+ vortexServer: VortexServer,
104
+ remoteVortexUuid: str,
105
+ remoteVortexName: str,
106
+ request,
107
+ ) -> None:
108
+ VortexConnectionABC.__init__(
109
+ self,
110
+ logger,
111
+ vortexServer,
112
+ remoteVortexUuid=remoteVortexUuid,
113
+ remoteVortexName=remoteVortexName,
114
+ httpSessionUuid=request.getSession().uid,
115
+ )
116
+
117
+ self._request = request
118
+ self._request.responseHeaders.setRawHeaders(
119
+ b"content-type", [b"text/text"]
120
+ )
121
+
122
+ self._buffer: Deque[bytes] = collections.deque()
123
+
124
+ self._requestReadyCheckTimer = task.LoopingCall(
125
+ self._checkIfRequestIsReady
126
+ )
127
+ self._requestReadyCheckTimer.start(0.001)
128
+
129
+ # Start our heart beat
130
+ self._beatLoopingCall = task.LoopingCall(self._beat)
131
+ d = self._beatLoopingCall.start(HEART_BEAT_PERIOD)
132
+ d.addErrback(lambda f: logger.exception(f.value))
133
+
134
+ def _beat(self):
135
+ if (
136
+ self._closed
137
+ or self._request.finished
138
+ or self._request.channel is None
139
+ ):
140
+ self._beatLoopingCall.stop()
141
+ return
142
+
143
+ # Send the heartbeats
144
+ self._request.write(b".")
145
+
146
+ # Touch the session
147
+ self._request.getSession().touch()
148
+
149
+ @property
150
+ def ip(self):
151
+ return self._request.client.host
152
+
153
+ @property
154
+ def port(self):
155
+ return self._request.client.port
156
+
157
+ def _checkIfRequestIsReady(self):
158
+ # This seems to be the fingerprint of the fact that request will not write
159
+ # our data yet.
160
+ if self._request.chunked and not self._request.startedWriting:
161
+ return
162
+
163
+ if self._requestReadyCheckTimer.running:
164
+ self._requestReadyCheckTimer.stop()
165
+
166
+ while self._buffer:
167
+ self._write(self._buffer.popleft())
168
+ self._buffer = None
169
+
170
+ def write(self, payloadVortexStr: bytes, priority: int = DEFAULT_PRIORITY):
171
+ assert not self._closed
172
+ # If we have a buffer, write it.
173
+ if self._buffer:
174
+ self._buffer.append(payloadVortexStr)
175
+ else:
176
+ self._write(payloadVortexStr)
177
+
178
+ def _write(self, payloadVortexStr: bytes):
179
+ if self._request.finished or self._request.channel is None:
180
+ logger.error("VORTEX CLOSED, CAN'T WRITE PAYLOAD")
181
+ self._closed = True
182
+ self._vortexServer.connectionClosed(self)
183
+ return
184
+
185
+ self._request.write(payloadVortexStr)
186
+ self._request.write(b".")
187
+
188
+ def close(self):
189
+ if self._beatLoopingCall.running:
190
+ self._beatLoopingCall.stop()
191
+
192
+ self._closed = True
193
+ self._request.finish()
@@ -0,0 +1,104 @@
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 typing import Union
11
+
12
+ from twisted.internet.defer import succeed
13
+ from twisted.internet.protocol import connectionDone, Factory
14
+
15
+ from vortex.VortexPayloadProtocol import VortexPayloadProtocol
16
+ from vortex.VortexServerConnection import VortexServerConnection
17
+ from .PayloadEnvelope import VortexMsgList, PayloadEnvelope
18
+ from .PayloadPriority import DEFAULT_PRIORITY
19
+ from .VortexServer import VortexServer
20
+
21
+ logger = logging.getLogger(name=__name__)
22
+
23
+
24
+ class VortexTcpServerProtocol(VortexPayloadProtocol):
25
+ def __init__(self, vortex: VortexServer, addr) -> None:
26
+ VortexPayloadProtocol.__init__(self, logger)
27
+ self._vortex = vortex
28
+ self._addr = addr
29
+
30
+ self._conn = None
31
+
32
+ self._dataBuffer = b""
33
+ self._remoteVortexUuid = None
34
+ self._remoteVortexName = None
35
+ self._httpSession = None
36
+
37
+ def _beat(self):
38
+ if self._conn:
39
+ self._conn.beatReceived()
40
+
41
+ def _nameAndUuidReceived(self, name, uuid):
42
+ self._remoteVortexUuid = uuid
43
+ self._remoteVortexName = name
44
+ self._conn = VortexServerConnection(
45
+ self._vortex,
46
+ self._remoteVortexUuid,
47
+ self._remoteVortexName,
48
+ self._httpSession,
49
+ self.transport,
50
+ self._addr,
51
+ )
52
+
53
+ # Send a heart beat down the new connection, tell it who we are.
54
+ connectPayloadFilt = {}
55
+ connectPayloadFilt[PayloadEnvelope.vortexUuidKey] = self._vortex.uuid()
56
+ connectPayloadFilt[PayloadEnvelope.vortexNameKey] = self._vortex.name()
57
+ self._conn.write(PayloadEnvelope(filt=connectPayloadFilt).toVortexMsg())
58
+
59
+ self._vortex.connectionOpened(self._httpSession, self._conn)
60
+
61
+ def _createResponseSenderCallable(self):
62
+ def sendResponse(
63
+ vortexMsgs: Union[VortexMsgList, bytes],
64
+ priority: int = DEFAULT_PRIORITY,
65
+ ):
66
+ if isinstance(vortexMsgs, bytes):
67
+ vortexMsgs = [vortexMsgs]
68
+
69
+ for vortexMsg in vortexMsgs:
70
+ self._conn.write(vortexMsg, priority)
71
+
72
+ return succeed(True)
73
+
74
+ return sendResponse
75
+
76
+ def dataReceived(self, data):
77
+
78
+ if self._vortex.isShutdown():
79
+ return None
80
+
81
+ VortexPayloadProtocol.dataReceived(self, data)
82
+
83
+ def connectionLost(self, reason=connectionDone):
84
+ if self._conn:
85
+ self._conn.transportClosed()
86
+
87
+ VortexPayloadProtocol.connectionLost(self, reason)
88
+
89
+
90
+ class VortexTcpServerFactory(Factory):
91
+ protocol = None
92
+
93
+ def __init__(self, vortexServer: VortexServer) -> None:
94
+ self._vortexServer = vortexServer
95
+
96
+ def buildProtocol(self, addr):
97
+ from vortex.VortexFactory import VortexFactory
98
+
99
+ if not VortexFactory.canConnect(str(addr)):
100
+ return
101
+
102
+ p = VortexTcpServerProtocol(self._vortexServer, addr)
103
+ p.factory = self
104
+ return p
@@ -0,0 +1,287 @@
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 collections import deque
12
+ from random import random
13
+ from urllib.parse import parse_qs
14
+ from urllib.parse import urlparse
15
+
16
+ from twisted.internet import reactor
17
+ from twisted.internet.defer import inlineCallbacks
18
+ from twisted.internet.protocol import Factory
19
+ from twisted.internet.protocol import Protocol
20
+ from twisted.internet.protocol import connectionDone
21
+ from twisted.protocols.policies import WrappingFactory
22
+ from twisted.web import resource
23
+ from twisted.web.server import NOT_DONE_YET
24
+
25
+ from txwebsocket.txws import WebSocketProtocol
26
+ from vortex.DeferUtil import vortexLogFailure, nonConcurrentMethod
27
+ from vortex.PayloadEnvelope import PayloadEnvelope
28
+ from vortex.VortexServerConnection import VortexServerConnection
29
+ from .PayloadPriority import DEFAULT_PRIORITY
30
+ from .VortexServer import VortexServer
31
+ from .VortexWritePushProducer import VortexWritePushProducer
32
+
33
+ logger = logging.getLogger(name=__name__)
34
+
35
+
36
+ class VortexWebsocketServerProtocol(Protocol):
37
+ _DEBUG_LOGGING = False
38
+
39
+ def __init__(self, vortex: VortexServer, addr, httpSession=None) -> None:
40
+ self._vortex = vortex
41
+ self._addr = addr
42
+ self._logger = logger
43
+
44
+ self._conn = None
45
+
46
+ self._remoteVortexUuid = None
47
+ self._remoteVortexName = None
48
+ self._httpSession = httpSession
49
+
50
+ def httpSessionExpiredCallback():
51
+ self._httpSession = None
52
+ self.transport.loseConnection()
53
+
54
+ if self._httpSession:
55
+ self._httpSession.notifyOnExpire(httpSessionExpiredCallback)
56
+
57
+ # Most messages don't need a buffer, but websockets can split messages
58
+ # around the 128kb mark
59
+ self._data = b""
60
+
61
+ self._vortexMsgsQueue = deque()
62
+
63
+ def __initConnection(self):
64
+ # self.transport.setBinaryMode(True)
65
+
66
+ params = parse_qs(urlparse(self.transport.location).query)
67
+
68
+ if "vortexUuid" not in params or "vortexName" not in params:
69
+ raise Exception(
70
+ "This isn't a vortex capable websocket. Check the URL"
71
+ )
72
+
73
+ self._remoteVortexUuid = params["vortexUuid"][0]
74
+ self._remoteVortexName = params["vortexName"][0]
75
+ self._conn = VortexServerConnection(
76
+ self._vortex,
77
+ self._remoteVortexUuid,
78
+ self._remoteVortexName,
79
+ self._httpSession,
80
+ self.transport,
81
+ self._addr,
82
+ )
83
+
84
+ # noinspection PyProtectedMember
85
+ assert isinstance(self.transport, WebSocketProtocol) and isinstance(
86
+ self.transport._producerForWrite, VortexWritePushProducer
87
+ ), "The Websocket transport is not how we expected it."
88
+
89
+ self.transport._producerForWrite.setRemoteVortexName(
90
+ f"{self._remoteVortexName}({self._remoteVortexUuid})"
91
+ )
92
+
93
+ # Send a heart beat down the new connection, tell it who we are.
94
+ connectPayloadFilt = {}
95
+ connectPayloadFilt[PayloadEnvelope.vortexUuidKey] = self._vortex.uuid()
96
+ connectPayloadFilt[PayloadEnvelope.vortexNameKey] = self._vortex.name()
97
+ self._conn.write(
98
+ PayloadEnvelope(filt=connectPayloadFilt).toVortexMsg(),
99
+ DEFAULT_PRIORITY,
100
+ )
101
+
102
+ self._vortex.connectionOpened(self._httpSession, self._conn)
103
+
104
+ def dataReceived(self, data):
105
+ if not self._conn:
106
+ self.__initConnection()
107
+
108
+ if self._vortex.isShutdown():
109
+ return None
110
+
111
+ if self._httpSession:
112
+ self._httpSession.touch()
113
+
114
+ self._conn.beatReceived()
115
+
116
+ if data.startswith(b"{"):
117
+ # If the data starts with a { then the payloadEnvelope is al in
118
+ # the websocket message.
119
+ assert data.endswith(b"}"), (
120
+ "%s payloadEnvelope doesn't fit in websocket message" % __name__
121
+ )
122
+ self._vortexMsgsQueue.append(data)
123
+ else:
124
+ # Else it's a base64 encoded payloadEnvelope, and it will be separated
125
+ # by b'.'
126
+ self._data += data
127
+
128
+ self._processData()
129
+
130
+ def connectionLost(self, reason=connectionDone):
131
+ if self._DEBUG_LOGGING:
132
+ logger.debug(
133
+ "VortexWebsocketServerProtocol.connectionLost reason=%s", reason
134
+ )
135
+ self._processData()
136
+ if self._conn:
137
+ self._conn.transportClosed()
138
+ self._vortex.connectionClosed(self._conn)
139
+
140
+ def _processData(self):
141
+ def getNextChunkIter():
142
+ try:
143
+ while True:
144
+ yield self._data.index(b".")
145
+
146
+ except ValueError:
147
+ # There is no '.' in it, wait for more data.
148
+ return
149
+
150
+ if self._data:
151
+ for nextChunk in getNextChunkIter():
152
+ vortexMsg = self._data[:nextChunk]
153
+ self._data = self._data[nextChunk + 1 :]
154
+
155
+ # If we get two heartbeats in a row, this will be false
156
+ if len(vortexMsg):
157
+ self._vortexMsgsQueue.append(vortexMsg)
158
+
159
+ if self._vortexMsgsQueue and not self._processVortexMsgs.running:
160
+ reactor.callLater(0, self._processVortexMsgs)
161
+
162
+ @inlineCallbacks
163
+ @nonConcurrentMethod
164
+ def _processVortexMsgs(self):
165
+ while self._vortexMsgsQueue:
166
+ vortexMsg = self._vortexMsgsQueue.popleft()
167
+
168
+ try:
169
+ payloadEnvelope = yield PayloadEnvelope().fromVortexMsgDefer(
170
+ vortexMsg
171
+ )
172
+
173
+ self._deliverPayload(payloadEnvelope)
174
+
175
+ except Exception as e:
176
+ print(vortexMsg)
177
+ print(e)
178
+ self._logger.exception(e)
179
+ raise
180
+
181
+ def _deliverPayload(self, payloadEnvelope):
182
+ self._vortex.payloadReveived(
183
+ httpSession=self._httpSession,
184
+ vortexUuid=self._remoteVortexUuid,
185
+ vortexName=self._remoteVortexName,
186
+ payload=payloadEnvelope,
187
+ )
188
+
189
+
190
+ class VortexWebsocketServerFactory(Factory):
191
+ protocol = None
192
+
193
+ def __init__(self, vortexServer: VortexServer) -> None:
194
+ self._vortexServer = vortexServer
195
+
196
+ def buildProtocol(self, addr, httpSession=None):
197
+ # VortexWebSocketUpgradeResource provides this check
198
+ # if we're connected via a HTTP upgrade
199
+ if not httpSession:
200
+ from vortex.VortexFactory import VortexFactory
201
+
202
+ if not VortexFactory.canConnect(str(addr)):
203
+ return
204
+
205
+ p = VortexWebsocketServerProtocol(self._vortexServer, addr, httpSession)
206
+ p.factory = self
207
+ return p
208
+
209
+
210
+ class VortexWrappedWebSocketFactory(WrappingFactory):
211
+ """
212
+ Factory which wraps another factory to provide WebSockets transports for
213
+ all of its protocols.
214
+ """
215
+
216
+ protocol = WebSocketProtocol
217
+
218
+ def buildProtocol(self, addr, httpSession):
219
+ return self.protocol(
220
+ self, self.wrappedFactory.buildProtocol(addr, httpSession)
221
+ )
222
+
223
+
224
+ class VortexWebSocketUpgradeResource(resource.Resource):
225
+ """Vortex Websocket Upgrade Resource
226
+
227
+ If this resource is hit, it will attempt to upgrade the connection to a websocket.
228
+
229
+ """
230
+
231
+ isLeaf = 1
232
+
233
+ def __init__(self, websocketFactory):
234
+ """Constructor
235
+
236
+ @:param websocketFactory: A factory that will build a WebsocketProtocol (above)
237
+ """
238
+ resource.Resource.__init__(self)
239
+ self._websocketFactory = websocketFactory
240
+
241
+ def render(self, request):
242
+ from vortex.VortexFactory import VortexFactory
243
+
244
+ if not VortexFactory.canConnect(str(request.client.host)):
245
+ request.setResponseCode(503)
246
+ request.setHeader("Retry-After", str(max(10, int(random() * 100))))
247
+ return b"Rejected due to new connections throttling."
248
+
249
+ httpSession = request.getSession()
250
+
251
+ websocketProtocol = self._websocketFactory.buildProtocol(
252
+ request.client, httpSession
253
+ )
254
+ websocketProtocol.makeConnection(request.channel.transport)
255
+ websocketProtocol.initFromRequest(request)
256
+ request.channel.upgradeToWebsocket(websocketProtocol)
257
+
258
+ def unregister():
259
+ request.channel.unregisterProducer()
260
+ websocketProtocol.unregisterProducer()
261
+
262
+ producer = VortexWritePushProducer(
263
+ request.channel.transport,
264
+ stopProducingCallback=lambda: unregister(),
265
+ # The websocket frames this before this producer gets it
266
+ writeWholeFrames=False,
267
+ terminateFrameWithDot=False,
268
+ splitFrames=True,
269
+ )
270
+ producer.WARNING_DATA_LENGTH = 10 * 1024 * 1024
271
+ producer.ERROR_DATA_LENGTH = 50 * 1024 * 1024
272
+
273
+ unregister()
274
+ request.channel.registerProducer(producer, streaming=True)
275
+ websocketProtocol.registerProducer(producer, streaming=True)
276
+
277
+ def closedError(failure):
278
+ # logger.error("Got closedError %s" % failure)
279
+ websocketProtocol.connectionLost(failure.value)
280
+
281
+ def closedOk(data):
282
+ pass
283
+ # logger.debug("Got closedOk %s" % data)
284
+
285
+ request.notifyFinish().addCallbacks(closedOk, closedError)
286
+
287
+ return NOT_DONE_YET