comparison sat/core/xmpp.py @ 3153:2c7b42f53e9a

core (xmpp): avoid starting several clients at the same time: a check is done to avoid running startConnection several times at once, which would lead to the creation of several SatXMPPEntity instances at the same time, resulting in many issues. If startConnection is called while a previous one is not finished yet, a CancelError is now raised.
author Goffi <goffi@goffi.org>
date Mon, 03 Feb 2020 13:46:24 +0100
parents f3700175c6a3
children b5c058c7692e
comparison
equal deleted inserted replaced
3152:7ce31f347ca3 3153:2c7b42f53e9a
54 # we use 2 "@" which is illegal in a jid, to be sure we are not mixing keys 54 # we use 2 "@" which is illegal in a jid, to be sure we are not mixing keys
55 # with roster jids 55 # with roster jids
56 ROSTER_VER_KEY = "@version@" 56 ROSTER_VER_KEY = "@version@"
57 57
58 58
59 class SatXMPPEntity(object): 59 class SatXMPPEntity:
60 """Common code for Client and Component""" 60 """Common code for Client and Component"""
61 # profile is added there when startConnection begins and removed when it is finished
62 profiles_connecting = set()
61 63
62 def __init__(self, host_app, profile, max_retries): 64 def __init__(self, host_app, profile, max_retries):
63 factory = self.factory 65 factory = self.factory
64 66
65 # we monkey patch clientConnectionLost to handle networkEnabled/networkDisabled 67 # we monkey patch clientConnectionLost to handle networkEnabled/networkDisabled
152 # (client is deleted then recreated from scratch) 154 # (client is deleted then recreated from scratch)
153 # most of methods called here should be called once on first connection 155 # most of methods called here should be called once on first connection
154 # (e.g. adding subprotocols) 156 # (e.g. adding subprotocols)
155 # but client should not be deleted except if session is finished 157 # but client should not be deleted except if session is finished
156 # (independently of connection/deconnection) 158 # (independently of connection/deconnection)
159 if profile in cls.profiles_connecting:
160 raise exceptions.CancelError(f"{profile} is already being connected")
161 cls.profiles_connecting.add(profile)
157 try: 162 try:
158 port = int( 163 port = int(
159 host.memory.getParamA( 164 host.memory.getParamA(
160 C.FORCE_PORT_PARAM, "Connection", profile_key=profile 165 C.FORCE_PORT_PARAM, "Connection", profile_key=profile
161 ) 166 )
167 ) # will use default value 5222 or be retrieved from a DNS SRV record 172 ) # will use default value 5222 or be retrieved from a DNS SRV record
168 173
169 password = yield host.memory.asyncGetParamA( 174 password = yield host.memory.asyncGetParamA(
170 "Password", "Connection", profile_key=profile 175 "Password", "Connection", profile_key=profile
171 ) 176 )
172 if profile in host.profiles:
173 raise exceptions.InternalError(
174 f"There is already a profile of name {profile} in host")
175 177
176 entity_jid_s = yield host.memory.asyncGetParamA( 178 entity_jid_s = yield host.memory.asyncGetParamA(
177 "JabberID", "Connection", profile_key=profile) 179 "JabberID", "Connection", profile_key=profile)
178 entity_jid = jid.JID(entity_jid_s) 180 entity_jid = jid.JID(entity_jid_s)
179 181
193 195
194 log.info(_("We'll use the stable resource {resource}").format( 196 log.info(_("We'll use the stable resource {resource}").format(
195 resource=resource)) 197 resource=resource))
196 entity_jid.resource = resource 198 entity_jid.resource = resource
197 199
200 if profile in host.profiles:
201 raise exceptions.InternalError(
202 f"There is already a profile of name {profile} in host")
198 entity = host.profiles[profile] = cls( 203 entity = host.profiles[profile] = cls(
199 host, profile, entity_jid, password, 204 host, profile, entity_jid, password,
200 host.memory.getParamA(C.FORCE_SERVER_PARAM, "Connection", 205 host.memory.getParamA(C.FORCE_SERVER_PARAM, "Connection",
201 profile_key=profile) or None, 206 profile_key=profile) or None,
202 port, max_retries, 207 port, max_retries,
246 251
247 yield list_d.addCallback( 252 yield list_d.addCallback(
248 logPluginResults 253 logPluginResults
249 ) # FIXME: we should have a timeout here, and a way to know if a plugin freeze 254 ) # FIXME: we should have a timeout here, and a way to know if a plugin freeze
250 # TODO: mesure launch time of each plugin 255 # TODO: mesure launch time of each plugin
256
257 cls.profiles_connecting.remove(profile)
251 258
252 def _disconnectionCb(self, __): 259 def _disconnectionCb(self, __):
253 self._connected_d = None 260 self._connected_d = None
254 261
255 def _disconnectionEb(self, failure_): 262 def _disconnectionEb(self, failure_):
782 self.presence.setHandlerParent(self) 789 self.presence.setHandlerParent(self)
783 790
784 @classmethod 791 @classmethod
785 @defer.inlineCallbacks 792 @defer.inlineCallbacks
786 def startConnection(cls, host, profile, max_retries): 793 def startConnection(cls, host, profile, max_retries):
787 yield super(SatXMPPClient, cls).startConnection(host, profile, max_retries) 794 try:
795 yield super(SatXMPPClient, cls).startConnection(host, profile, max_retries)
796 except exceptions.CancelError as e:
797 log.warning(f"startConnection cancelled: {e}")
798 return
788 entity = host.profiles[profile] 799 entity = host.profiles[profile]
789 # we finally send our presence 800 # we finally send our presence
790 entity.presence.available() 801 entity.presence.available()
791 802
792 def entityConnected(self): 803 def entityConnected(self):