Mercurial > libervia-web
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']}, " |