Mercurial > libervia-web
comparison libervia/server/server.py @ 1504:409d10211b20
server, browser: dynamic pages refactoring:
dynamic pages has been reworked, to change the initial basic implementation.
Pages are now dynamic by default, and a websocket is established by the first connected
page of a session. The socket is used to transmit bridge signals, and then the signal is
broadcasted to other tabs using broadcast channel.
If the connecting tab is closed, an other one is chosen.
Some tests are made to retry connecting in case of problem, and sometimes reload the pages
(e.g. if profile is connected).
Signals (or other data) are cached during reconnection phase, to avoid lost of data.
All previous partial rendering mechanism have been removed, chat page is temporarily not
working anymore, but will be eventually redone (one of the goal of this work is to have
proper chat).
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 01 Mar 2023 18:02:44 +0100 |
parents | 6643855770a5 |
children | a169cbc315f0 |
comparison
equal
deleted
inserted
replaced
1503:2796e73ed50c | 1504:409d10211b20 |
---|---|
842 class Libervia(service.Service): | 842 class Libervia(service.Service): |
843 debug = defer.Deferred.debug # True if twistd/Libervia is launched in debug mode | 843 debug = defer.Deferred.debug # True if twistd/Libervia is launched in debug mode |
844 | 844 |
845 def __init__(self, options): | 845 def __init__(self, options): |
846 self.options = options | 846 self.options = options |
847 websockets.host = self | |
847 | 848 |
848 def _init(self): | 849 def _init(self): |
849 # we do init here and not in __init__ to avoid doule initialisation with twistd | 850 # we do init here and not in __init__ to avoid doule initialisation with twistd |
850 # this _init is called in startService | 851 # this _init is called in startService |
851 self.initialised = defer.Deferred() | 852 self.initialised = defer.Deferred() |
1197 d.addCallback(self._namespacesGetCb) | 1198 d.addCallback(self._namespacesGetCb) |
1198 d.addErrback(self._namespacesGetEb) | 1199 d.addErrback(self._namespacesGetEb) |
1199 | 1200 |
1200 # websocket | 1201 # websocket |
1201 if self.options["connection_type"] in ("https", "both"): | 1202 if self.options["connection_type"] in ("https", "both"): |
1202 wss = websockets.LiberviaPageWSProtocol.getResource(self, secure=True) | 1203 wss = websockets.LiberviaPageWSProtocol.getResource(secure=True) |
1203 self.putChildAll(b'wss', wss) | 1204 self.putChildAll(b'wss', wss) |
1204 if self.options["connection_type"] in ("http", "both"): | 1205 if self.options["connection_type"] in ("http", "both"): |
1205 ws = websockets.LiberviaPageWSProtocol.getResource(self, secure=False) | 1206 ws = websockets.LiberviaPageWSProtocol.getResource(secure=False) |
1206 self.putChildAll(b'ws', ws) | 1207 self.putChildAll(b'ws', ws) |
1207 | 1208 |
1208 ## following signal is needed for cache handling in Libervia pages | 1209 ## following signal is needed for cache handling in Libervia pages |
1209 self.bridge.register_signal( | 1210 self.bridge.register_signal( |
1210 "psEventRaw", partial(LiberviaPage.onNodeEvent, self), "plugin" | 1211 "psEventRaw", partial(LiberviaPage.onNodeEvent, self), "plugin" |
1211 ) | 1212 ) |
1212 self.bridge.register_signal( | 1213 self.bridge.register_signal( |
1213 "messageNew", partial(LiberviaPage.onSignal, self, "messageNew") | 1214 "messageNew", partial(self.on_signal, "messageNew") |
1214 ) | 1215 ) |
1215 | 1216 |
1216 # Progress handling | 1217 # Progress handling |
1217 self.bridge.register_signal( | 1218 self.bridge.register_signal( |
1218 "progressStarted", partial(ProgressHandler._signal, "started") | 1219 "progressStarted", partial(ProgressHandler._signal, "started") |
1323 kwargs["callback"] = _callback | 1324 kwargs["callback"] = _callback |
1324 kwargs["errback"] = _errback | 1325 kwargs["errback"] = _errback |
1325 getattr(self.bridge, method_name)(*args, **kwargs) | 1326 getattr(self.bridge, method_name)(*args, **kwargs) |
1326 return d | 1327 return d |
1327 | 1328 |
1329 def on_signal(self, signal_name, *args): | |
1330 profile = args[-1] | |
1331 if not profile: | |
1332 log.error(f"got signal without profile: {signal_name}, {args}") | |
1333 return | |
1334 try: | |
1335 sockets = websockets.LiberviaPageWSProtocol.profile_map[profile] | |
1336 except KeyError: | |
1337 log.debug(f"no socket opened for profile {profile}") | |
1338 return | |
1339 for socket in sockets: | |
1340 socket.send("bridge", {"signal": signal_name, "args": args}) | |
1341 | |
1328 async def _logged(self, profile, request): | 1342 async def _logged(self, profile, request): |
1329 """Set everything when a user just logged in | 1343 """Set everything when a user just logged in |
1330 | 1344 |
1331 @param profile | 1345 @param profile |
1332 @param request | 1346 @param request |
1358 _("profile cache resource added from {uuid} to {path}").format( | 1372 _("profile cache resource added from {uuid} to {path}").format( |
1359 uuid=sat_session.uuid, path=cache_dir | 1373 uuid=sat_session.uuid, path=cache_dir |
1360 ) | 1374 ) |
1361 ) | 1375 ) |
1362 | 1376 |
1363 def onExpire(): | 1377 def on_expire(): |
1364 log.info("Session expired (profile={profile})".format(profile=profile)) | 1378 log.info("Session expired (profile={profile})".format(profile=profile)) |
1365 self.cache_resource.delEntity(sat_session.uuid.encode('utf-8')) | 1379 self.cache_resource.delEntity(sat_session.uuid.encode('utf-8')) |
1366 log.debug( | 1380 log.debug( |
1367 _("profile cache resource {uuid} deleted").format(uuid=sat_session.uuid) | 1381 _("profile cache resource {uuid} deleted").format(uuid=sat_session.uuid) |
1368 ) | 1382 ) |
1383 sat_session.on_expire() | |
1384 if sat_session.ws_socket is not None: | |
1385 sat_session.ws_socket.close() | |
1369 # and now we disconnect the profile | 1386 # and now we disconnect the profile |
1370 self.bridgeCall("disconnect", profile) | 1387 self.bridgeCall("disconnect", profile) |
1371 | 1388 |
1372 session.notifyOnExpire(onExpire) | 1389 session.notifyOnExpire(on_expire) |
1373 | 1390 |
1374 # FIXME: those session infos should be returned by connect or isConnected | 1391 # FIXME: those session infos should be returned by connect or isConnected |
1375 infos = await self.bridgeCall("sessionInfosGet", profile) | 1392 infos = await self.bridgeCall("sessionInfosGet", profile) |
1376 sat_session.jid = jid.JID(infos["jid"]) | 1393 sat_session.jid = jid.JID(infos["jid"]) |
1377 own_bare_jid_s = sat_session.jid.userhost() | 1394 own_bare_jid_s = sat_session.jid.userhost() |
1766 def purgeSession(self, request): | 1783 def purgeSession(self, request): |
1767 """helper method to purge a session during request handling""" | 1784 """helper method to purge a session during request handling""" |
1768 session = request.session | 1785 session = request.session |
1769 if session is not None: | 1786 if session is not None: |
1770 log.debug(_("session purge")) | 1787 log.debug(_("session purge")) |
1788 sat_session = self.getSessionData(request, session_iface.ISATSession) | |
1789 socket = sat_session.ws_socket | |
1790 if socket is not None: | |
1791 socket.close() | |
1792 session.ws_socket = None | |
1771 session.expire() | 1793 session.expire() |
1772 # FIXME: not clean but it seems that it's the best way to reset | 1794 # FIXME: not clean but it seems that it's the best way to reset |
1773 # session during request handling | 1795 # session during request handling |
1774 request._secureSession = request._insecureSession = None | 1796 request._secureSession = request._insecureSession = None |
1775 | 1797 |
1822 sat_session.setAffiliation(service, node, affiliation) | 1844 sat_session.setAffiliation(service, node, affiliation) |
1823 defer.returnValue(affiliation) | 1845 defer.returnValue(affiliation) |
1824 | 1846 |
1825 ## Websocket (dynamic pages) ## | 1847 ## Websocket (dynamic pages) ## |
1826 | 1848 |
1827 def getWebsocketURL(self, request): | 1849 def get_websocket_url(self, request): |
1828 base_url_split = self.getExtBaseURLData(request) | 1850 base_url_split = self.getExtBaseURLData(request) |
1829 if base_url_split.scheme.endswith("s"): | 1851 if base_url_split.scheme.endswith("s"): |
1830 scheme = "wss" | 1852 scheme = "wss" |
1831 else: | 1853 else: |
1832 scheme = "ws" | 1854 scheme = "ws" |
1833 | 1855 |
1834 return self.getExtBaseURL(request, path=scheme, scheme=scheme) | 1856 return self.getExtBaseURL(request, path=scheme, scheme=scheme) |
1835 | 1857 |
1836 def registerWSToken(self, token, page, request): | |
1837 # we make a shallow copy of request to avoid losing request.channel when | |
1838 # connection is lost (which would result as request.isSecure() being always | |
1839 # False). See #327 | |
1840 request._signal_id = id(request) | |
1841 websockets.LiberviaPageWSProtocol.registerToken(token, page, copy.copy(request)) | |
1842 | 1858 |
1843 ## Various utils ## | 1859 ## Various utils ## |
1844 | 1860 |
1845 def getHTTPDate(self, timestamp=None): | 1861 def getHTTPDate(self, timestamp=None): |
1846 now = time.gmtime(timestamp) | 1862 now = time.gmtime(timestamp) |