annotate libervia/pages/_browser/bridge.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 b28025a7cc28
children ce879da7fcf7
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
1 from browser import window, timer, console as log
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
2 import time
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
3 import random
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
4 import json
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
5 import dialog
1297
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
6 import javascript
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
7
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
8
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
9 log.warning = log.warn
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
10 tab_id = random.randint(0, 2**64)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
11 log.info(f"TAB ID is {tab_id}")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
12
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
13
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
14 class WebSocket:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
15
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
16 def __init__(self, broadcast_channel):
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
17 self.broadcast_channel = broadcast_channel
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
18 self.token = window.ws_token
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
19 self.create_socket()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
20 self.retrying = False
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
21 self.network_error = False
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
22
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
23 @property
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
24 def profile(self):
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
25 return self.broadcast_channel.profile
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
26
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
27 def retry_connect(self) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
28 if self.retrying:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
29 return
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
30 self.retrying = True
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
31 try:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
32 notif = dialog.RetryNotification(self.create_socket)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
33 notif.show(
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
34 "Can't connect to server",
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
35 delay=random.randint(0, 30)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
36 )
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
37 except Exception as e:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
38 # for security reasons, browser don't give the reason of the error with
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
39 # WebSockets, thus we try to detect network error here, as if we can't show
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
40 # the retry dialog, that probably means that it's not reachable
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
41 try:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
42 name = e.name
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
43 except AttributeError:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
44 name = None
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
45 if name == "NetworkError":
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
46 self.network_error = True
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
47 log.warning("network error detected, server may be down")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
48 log.error(f"Can't show retry dialog: {e}")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
49 log.info("retrying in 30s")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
50 timer.set_timeout(self.create_socket, 30000)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
51 else:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
52 raise e
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
53 else:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
54 # if we can show the retry dialog, the network is fine
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
55 self.network_error = False
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
56
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
57 def create_socket(self) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
58 log.debug("creating socket")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
59 self.retrying = False
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
60 self.socket = window.WebSocket.new(window.ws_url, "libervia-page")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
61 self.socket_start = time.time()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
62 self.socket.bind("open", self.on_open)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
63 self.socket.bind("error", self.on_error)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
64 self.socket.bind("close", self.on_close)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
65 self.socket.bind("message", self.on_message)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
66
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
67 def send(self, data_type: str, data: dict) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
68 self.socket.send(json.dumps({
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
69 "type": data_type,
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
70 "data": data
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
71 }))
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
72
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
73 def close(self) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
74 log.debug("closing socket")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
75 self.broadcast_channel.ws = None
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
76 self.socket.close()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
77
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
78 def on_open(self, evt) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
79 log.info("websocket connection opened")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
80 self.send("init", {"profile": self.profile, "token": self.token})
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
81
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
82 def on_error(self, evt) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
83 if not self.network_error and time.time() - self.socket_start < 5:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
84 # disconnection is happening fast, we try to reload
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
85 log.warning("Reloading due to suspected session error")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
86 window.location.reload()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
87 else:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
88 self.retry_connect()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
89
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
90 def on_close(self, evt) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
91 log.warning(f"websocket is closed {evt.code=} {evt.reason=}")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
92 if self.broadcast_channel.ws is None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
93 # this is a close requested locally
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
94 return
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
95 elif evt.code == 4401:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
96 log.info(
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
97 "no authorized, the session is probably not valid anymore, reloading"
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
98 )
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
99 window.location.reload()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
100 else:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
101 # close event may be due to normal tab closing, thus we try to reconnect only
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
102 # after a delay
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
103 timer.set_timeout(self.retry_connect, 5000)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
104
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
105 def on_message(self, message_evt):
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
106 msg_data = json.loads(message_evt.data)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
107 msg_type = msg_data.get("type")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
108 if msg_type == "bridge":
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
109 self.broadcast_channel.post(
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
110 msg_type,
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
111 msg_data["data"]
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
112 )
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
113 elif msg_type == "force_close":
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
114 log.warning(f"force closing connection: {msg_data.get('reason')}")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
115 self.close()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
116 else:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
117 dialog.notification.show(
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
118 f"Unexpected message type {msg_type}"
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
119 "error"
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
120 )
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
121
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
122
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
123 class BroadcastChannel:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
124 handlers = {}
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
125
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
126 def __init__(self):
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
127 log.debug(f"BroadcastChannel init with profile {self.profile!r}")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
128 self.start = time.time()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
129 self.bc = window.BroadcastChannel.new("libervia")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
130 self.bc.bind("message", self.on_message)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
131 # there is no way to check if there is already a connection in BroadcastChannel
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
132 # API, thus we wait a bit to see if somebody is answering. If not, we are probably
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
133 # the first tab.
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
134 self.check_connection_timer = timer.set_timeout(self.establish_connection, 20)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
135 self.ws = None
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
136 # set of all known tab ids
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
137 self.tabs_ids = {tab_id}
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
138 self.post("salut_a_vous", {
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
139 "id": tab_id,
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
140 "profile": self.profile
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
141 })
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
142 window.bind("unload", self.on_unload)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
143
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
144 @property
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
145 def profile(self):
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
146 return window.profile or ""
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
147
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
148 @property
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
149 def connecting_tab(self) -> bool:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
150 """True is this tab is the one establishing the websocket connection"""
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
151 return self.ws is not None
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
152
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
153 @connecting_tab.setter
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
154 def connecting_tab(self, connecting: bool) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
155 if connecting:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
156 if self.ws is None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
157 self.ws = WebSocket(self)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
158 self.post("connection", {
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
159 "tab_id": tab_id
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
160 })
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
161 elif self.ws is not None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
162 self.ws.close()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
163
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
164 def establish_connection(self) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
165 """Called when there is no existing connection"""
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
166 timer.clear_timeout(self.check_connection_timer)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
167 log.debug(f"Establishing connection {tab_id=}")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
168 self.connecting_tab = True
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
169
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
170 def handle_bridge_signal(self, data: dict) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
171 """Forward bridge signals to registered handlers"""
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
172 signal = data["signal"]
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
173 handlers = self.handlers.get(signal, [])
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
174 for handler in handlers:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
175 handler(*data["args"])
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
176
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
177 def on_message(self, evt) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
178 data = json.loads(evt.data)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
179 if data["type"] == "bridge":
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
180 self.handle_bridge_signal(data)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
181 elif data["type"] == "salut_a_toi":
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
182 # this is a response from existing tabs
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
183 other_tab_id = data["id"]
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
184 if other_tab_id == tab_id:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
185 # in the unlikely case that this happens, we simply reload this tab to get
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
186 # a new ID
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
187 log.warning("duplicate tab id, we reload the page: {tab_id=}")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
188 window.location.reload()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
189 return
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
190 self.tabs_ids.add(other_tab_id)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
191 if data["connecting_tab"] and self.check_connection_timer is not None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
192 # this tab has the websocket connection to server
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
193 log.info(f"there is already a connection to server at tab {other_tab_id}")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
194 timer.clear_timeout(self.check_connection_timer)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
195 self.check_connection_timer = None
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
196 elif data["type"] == "salut_a_vous":
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
197 # a new tab has just been created
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
198 if data["profile"] != self.profile:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
199 log.info(
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
200 f"we are now connected with the profile {data['profile']}, "
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
201 "reloading the page"
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
202 )
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
203 window.location.reload()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
204 else:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
205 self.tabs_ids.add(data["id"])
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
206 self.post("salut_a_toi", {
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
207 "id": tab_id,
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
208 "connecting_tab": self.connecting_tab
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
209 })
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
210 elif data["type"] == "connection":
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
211 log.info(f"tab {data['id']} is the new connecting tab")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
212 elif data["type"] == "salut_a_rantanplan":
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
213 # a tab is being closed
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
214 other_tab_id = data["id"]
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
215 # it is unlikely that there is a collision, but just in case we check it
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
216 if other_tab_id != tab_id:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
217 self.tabs_ids.discard(other_tab_id)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
218 if data["connecting_tab"]:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
219 log.info(f"connecting tab with id {other_tab_id} has been closed")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
220 if max(self.tabs_ids) == tab_id:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
221 log.info("this is the new connecting tab, establish_connection")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
222 self.connecting_tab = True
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
223 else:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
224 log.info(f"tab with id {other_tab_id} has been closed")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
225 else:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
226 log.warning(f"unknown message type: {data}")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
227
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
228 def post(self, data_type, data: dict):
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
229 data["type"] = data_type
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
230 data["id"] = tab_id
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
231 self.bc.postMessage(json.dumps(data))
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
232 if data_type == "bridge":
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
233 self.handle_bridge_signal(data)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
234
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
235 def on_unload(self, evt) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
236 """Send a message to indicate that the tab is being closed"""
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
237 self.post("salut_a_rantanplan", {
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
238 "id": tab_id,
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
239 "connecting_tab": self.connecting_tab
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
240 })
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
241
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
242
1297
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
243 class Bridge:
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
244 bc: BroadcastChannel | None = None
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
245
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
246 def __init__(self) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
247 if Bridge.bc is None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
248 Bridge.bc = BroadcastChannel()
1297
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
249
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
250 def __getattr__(self, attr):
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
251 return lambda *args, **kwargs: self.call(attr, *args, **kwargs)
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
252
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
253 def on_load(self, xhr, ev, callback, errback):
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
254 if xhr.status == 200:
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
255 ret = javascript.JSON.parse(xhr.response)
1314
8998f01088ac browser (bridge): handle case where `callback` or `errback` is None
Goffi <goffi@goffi.org>
parents: 1297
diff changeset
256 if callback is not None:
8998f01088ac browser (bridge): handle case where `callback` or `errback` is None
Goffi <goffi@goffi.org>
parents: 1297
diff changeset
257 if ret is None:
8998f01088ac browser (bridge): handle case where `callback` or `errback` is None
Goffi <goffi@goffi.org>
parents: 1297
diff changeset
258 callback()
8998f01088ac browser (bridge): handle case where `callback` or `errback` is None
Goffi <goffi@goffi.org>
parents: 1297
diff changeset
259 else:
8998f01088ac browser (bridge): handle case where `callback` or `errback` is None
Goffi <goffi@goffi.org>
parents: 1297
diff changeset
260 callback(ret)
1297
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
261 elif xhr.status == 502:
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
262 # PROXY_ERROR is used for bridge error
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
263 ret = javascript.JSON.parse(xhr.response)
1314
8998f01088ac browser (bridge): handle case where `callback` or `errback` is None
Goffi <goffi@goffi.org>
parents: 1297
diff changeset
264 if errback is not None:
8998f01088ac browser (bridge): handle case where `callback` or `errback` is None
Goffi <goffi@goffi.org>
parents: 1297
diff changeset
265 errback(ret)
1297
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
266 else:
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
267 log.error(
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
268 f"bridge called failed: code: {xhr.response}, text: {xhr.statusText}"
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
269 )
1314
8998f01088ac browser (bridge): handle case where `callback` or `errback` is None
Goffi <goffi@goffi.org>
parents: 1297
diff changeset
270 if errback is not None:
1477
b28025a7cc28 browser (bride): use `BridgeInternalError` for unmanaged errors:
Goffi <goffi@goffi.org>
parents: 1314
diff changeset
271 errback({"fullname": "BridgeInternalError", "message": xhr.statusText})
1297
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
272
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
273 def call(self, method_name, *args, callback, errback, **kwargs):
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
274 xhr = window.XMLHttpRequest.new()
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
275 xhr.bind('load', lambda ev: self.on_load(xhr, ev, callback, errback))
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
276 xhr.bind('error', lambda ev: errback(
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
277 {"fullname": "ConnectionError", "message": xhr.statusText}))
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
278 xhr.open("POST", f"/_bridge/{method_name}", True)
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
279 data = javascript.JSON.stringify({
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
280 "args": args,
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
281 "kwargs": kwargs,
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
282 })
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
283 xhr.setRequestHeader('X-Csrf-Token', window.csrf_token)
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
284 xhr.send(data)
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
285
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
286 def register_signal(self, signal: str, handler, iface=None) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
287 BroadcastChannel.handlers.setdefault(signal, []).append(handler)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
288 log.debug(f"signal {signal} has been registered")