comparison libervia/web/pages/_browser/bridge.py @ 1545:be20e6ac9f22

browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
author Goffi <goffi@goffi.org>
date Thu, 06 Jul 2023 12:10:25 +0200
parents eb00d593801d
children 5db55d01ce05
comparison
equal deleted inserted replaced
1544:9b451115e726 1545:be20e6ac9f22
37 class WebSocket: 37 class WebSocket:
38 38
39 def __init__(self, broadcast_channel): 39 def __init__(self, broadcast_channel):
40 self.broadcast_channel = broadcast_channel 40 self.broadcast_channel = broadcast_channel
41 self.token = window.ws_token 41 self.token = window.ws_token
42 self.socket = None
42 self.create_socket() 43 self.create_socket()
43 self.retrying = False 44 self.retrying = False
44 self.network_error = False 45 self.network_error = False
46 self._ready_fut = aio.Future()
45 47
46 @property 48 @property
47 def profile(self): 49 def profile(self):
48 return self.broadcast_channel.profile 50 return self.broadcast_channel.profile
51
52 @property
53 def is_ready(self):
54 return False if self.socket is None else self.socket.readyState == "OPEN"
55
56 @property
57 def connection_ready_fut(self):
58 """Future resolved when connection is ready"""
59 return self._ready_fut
49 60
50 def retry_connect(self) -> None: 61 def retry_connect(self) -> None:
51 if self.retrying: 62 if self.retrying:
52 return 63 return
53 self.retrying = True 64 self.retrying = True
98 self.broadcast_channel.ws = None 109 self.broadcast_channel.ws = None
99 self.socket.close() 110 self.socket.close()
100 111
101 def on_open(self, evt) -> None: 112 def on_open(self, evt) -> None:
102 log.info("websocket connection opened") 113 log.info("websocket connection opened")
114 self._ready_fut.set_result(None)
103 self.send("init", {"profile": self.profile, "token": self.token}) 115 self.send("init", {"profile": self.profile, "token": self.token})
104 116
105 def on_error(self, evt) -> None: 117 def on_error(self, evt) -> None:
106 if not self.network_error and time.time() - self.socket_start < 5: 118 if not self.network_error and time.time() - self.socket_start < 5:
107 # disconnection is happening fast, we try to reload 119 # disconnection is happening fast, we try to reload
149 class BroadcastChannel: 161 class BroadcastChannel:
150 handlers = {} 162 handlers = {}
151 163
152 def __init__(self): 164 def __init__(self):
153 log.debug(f"BroadcastChannel init with profile {self.profile!r}") 165 log.debug(f"BroadcastChannel init with profile {self.profile!r}")
166 self.ws = None
167 self._connected = False
154 self.start = time.time() 168 self.start = time.time()
155 self.bc = window.BroadcastChannel.new("libervia") 169 self.bc = window.BroadcastChannel.new("libervia")
156 self.bc.bind("message", self.on_message) 170 self.bc.bind("message", self.on_message)
157 # there is no way to check if there is already a connection in BroadcastChannel 171 # there is no way to check if there is already a connection in BroadcastChannel
158 # API, thus we wait a bit to see if somebody is answering. If not, we are probably 172 # API, thus we wait a bit to see if somebody is answering. If not, we are probably
159 # the first tab. 173 # the first tab.
160 self.check_connection_timer = timer.set_timeout(self.establish_connection, 20) 174 self.check_connection_timer = timer.set_timeout(self.establish_connection, 20)
161 self.ws = None
162 # set of all known tab ids 175 # set of all known tab ids
163 self.tabs_ids = {tab_id} 176 self.tabs_ids = {tab_id}
164 self.post("salut_a_vous", { 177 self.post("salut_a_vous", {
165 "id": tab_id, 178 "id": tab_id,
166 "profile": self.profile 179 "profile": self.profile
167 }) 180 })
168 window.bind("unload", self.on_unload) 181 window.bind("unload", self.on_unload)
182 self._wait_connection_fut = aio.Future()
169 183
170 @property 184 @property
171 def profile(self): 185 def profile(self):
172 return window.profile or "" 186 return window.profile or ""
173 187
174 @property 188 @property
175 def connecting_tab(self) -> bool: 189 def connecting_tab(self) -> bool:
176 """True is this tab is the one establishing the websocket connection""" 190 """True is this tab is the one establishing the websocket connection"""
177 return self.ws is not None 191 return self.ws is not None
192
193 async def _wait_for_ws(self):
194 assert self.ws is not None
195 await self.ws.connection_ready_fut
196 self._wait_connection_fut.set_result(None)
197 self._connected = True
178 198
179 @connecting_tab.setter 199 @connecting_tab.setter
180 def connecting_tab(self, connecting: bool) -> None: 200 def connecting_tab(self, connecting: bool) -> None:
181 if connecting: 201 if connecting:
182 if self.ws is None: 202 if self.ws is None:
183 self.ws = WebSocket(self) 203 self.ws = WebSocket(self)
184 self.post("connection", { 204 self.post("connection", {
185 "tab_id": tab_id 205 "tab_id": tab_id
186 }) 206 })
207 aio.run(self._wait_for_ws())
208
187 elif self.ws is not None: 209 elif self.ws is not None:
188 self.ws.close() 210 self.ws.close()
211
212 @property
213 def connected(self):
214 return self._connected
215
216 async def wait_for_connection(self):
217 if self._connected:
218 return
219 await self._wait_connection_fut
189 220
190 def establish_connection(self) -> None: 221 def establish_connection(self) -> None:
191 """Called when there is no existing connection""" 222 """Called when there is no existing connection"""
192 timer.clear_timeout(self.check_connection_timer) 223 timer.clear_timeout(self.check_connection_timer)
193 log.debug(f"Establishing connection {tab_id=}") 224 log.debug(f"Establishing connection {tab_id=}")
217 if data["connecting_tab"] and self.check_connection_timer is not None: 248 if data["connecting_tab"] and self.check_connection_timer is not None:
218 # this tab has the websocket connection to server 249 # this tab has the websocket connection to server
219 log.info(f"there is already a connection to server at tab {other_tab_id}") 250 log.info(f"there is already a connection to server at tab {other_tab_id}")
220 timer.clear_timeout(self.check_connection_timer) 251 timer.clear_timeout(self.check_connection_timer)
221 self.check_connection_timer = None 252 self.check_connection_timer = None
253 self._wait_connection_fut.set_result(None)
254 self._connected = True
222 elif data["type"] == "salut_a_vous": 255 elif data["type"] == "salut_a_vous":
223 # a new tab has just been created 256 # a new tab has just been created
224 if data["profile"] != self.profile: 257 if data["profile"] != self.profile:
225 log.info( 258 log.info(
226 f"we are now connected with the profile {data['profile']}, " 259 f"we are now connected with the profile {data['profile']}, "