annotate libervia/web/pages/_browser/bridge.py @ 1579:5db55d01ce05

browser (bridge): workaround to fix unicode dumping of JSON: in Brython 3.11, emoji are broken when dumped/parsed with `ensure_ascii`, and using `ensure_ascii=False` is not working (see https://github.com/brython-dev/brython/issues/2331). This work around this and https://github.com/brython-dev/brython/issues/2332 to make sure that emoji are transmitted correctly to other tabs.
author Goffi <goffi@goffi.org>
date Wed, 22 Nov 2023 16:31:36 +0100
parents be20e6ac9f22
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
1510
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
1 from browser import window, aio, timer, console as log
1504
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
1510
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
14 class BridgeException(Exception):
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
15 """An exception which has been raised from the backend and arrived to the frontend."""
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
16
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
17 def __init__(self, name, message="", condition=""):
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
18 """
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
19
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
20 @param name (str): full exception class name (with module)
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
21 @param message (str): error message
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
22 @param condition (str) : error condition
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
23 """
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
24 Exception.__init__(self)
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
25 self.fullname = str(name)
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
26 self.message = str(message)
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
27 self.condition = str(condition) if condition else ""
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
28 self.module, __, self.classname = str(self.fullname).rpartition(".")
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
29
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
30 def __str__(self):
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
31 return f"{self.classname}: {self.message or ''}"
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
32
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
33 def __eq__(self, other):
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
34 return self.classname == other
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
35
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
36
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
37 class WebSocket:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
38
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
39 def __init__(self, broadcast_channel):
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
40 self.broadcast_channel = broadcast_channel
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
41 self.token = window.ws_token
1545
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
42 self.socket = None
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
43 self.create_socket()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
44 self.retrying = False
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
45 self.network_error = False
1545
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
46 self._ready_fut = aio.Future()
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
47
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
48 @property
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
49 def profile(self):
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
50 return self.broadcast_channel.profile
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
51
1545
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
52 @property
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
53 def is_ready(self):
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
54 return False if self.socket is None else self.socket.readyState == "OPEN"
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
55
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
56 @property
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
57 def connection_ready_fut(self):
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
58 """Future resolved when connection is ready"""
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
59 return self._ready_fut
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
60
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
61 def retry_connect(self) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
62 if self.retrying:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
63 return
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
64 self.retrying = True
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
65 try:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
66 notif = dialog.RetryNotification(self.create_socket)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
67 notif.show(
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
68 "Can't connect to server",
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
69 delay=random.randint(0, 30)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
70 )
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
71 except Exception as e:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
72 # 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
73 # 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
74 # 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
75 try:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
76 name = e.name
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
77 except AttributeError:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
78 name = None
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
79 if name == "NetworkError":
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
80 self.network_error = True
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
81 log.warning("network error detected, server may be down")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
82 log.error(f"Can't show retry dialog: {e}")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
83 log.info("retrying in 30s")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
84 timer.set_timeout(self.create_socket, 30000)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
85 else:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
86 raise e
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 # 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
89 self.network_error = False
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
90
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
91 def create_socket(self) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
92 log.debug("creating socket")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
93 self.retrying = False
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
94 self.socket = window.WebSocket.new(window.ws_url, "libervia-page")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
95 self.socket_start = time.time()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
96 self.socket.bind("open", self.on_open)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
97 self.socket.bind("error", self.on_error)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
98 self.socket.bind("close", self.on_close)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
99 self.socket.bind("message", self.on_message)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
100
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
101 def send(self, data_type: str, data: dict) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
102 self.socket.send(json.dumps({
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
103 "type": data_type,
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
104 "data": data
1579
5db55d01ce05 browser (bridge): workaround to fix unicode dumping of JSON:
Goffi <goffi@goffi.org>
parents: 1545
diff changeset
105 }, ensure_ascii=False))
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
106
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
107 def close(self) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
108 log.debug("closing socket")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
109 self.broadcast_channel.ws = None
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
110 self.socket.close()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
111
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
112 def on_open(self, evt) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
113 log.info("websocket connection opened")
1545
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
114 self._ready_fut.set_result(None)
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
115 self.send("init", {"profile": self.profile, "token": self.token})
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
116
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
117 def on_error(self, evt) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
118 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
119 # disconnection is happening fast, we try to reload
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
120 log.warning("Reloading due to suspected session error")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
121 window.location.reload()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
122 else:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
123 self.retry_connect()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
124
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
125 def on_close(self, evt) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
126 log.warning(f"websocket is closed {evt.code=} {evt.reason=}")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
127 if self.broadcast_channel.ws is None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
128 # this is a close requested locally
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
129 return
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
130 elif evt.code == 4401:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
131 log.info(
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
132 "no authorized, the session is probably not valid anymore, reloading"
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
133 )
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
134 window.location.reload()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
135 else:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
136 # 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
137 # after a delay
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
138 timer.set_timeout(self.retry_connect, 5000)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
139
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
140 def on_message(self, message_evt):
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
141 msg_data = json.loads(message_evt.data)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
142 msg_type = msg_data.get("type")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
143 if msg_type == "bridge":
1506
ce879da7fcf7 server: fix `on_signal` callback
Goffi <goffi@goffi.org>
parents: 1504
diff changeset
144 log.debug(
ce879da7fcf7 server: fix `on_signal` callback
Goffi <goffi@goffi.org>
parents: 1504
diff changeset
145 f"==> bridge message: {msg_data=}"
ce879da7fcf7 server: fix `on_signal` callback
Goffi <goffi@goffi.org>
parents: 1504
diff changeset
146 )
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
147 self.broadcast_channel.post(
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
148 msg_type,
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
149 msg_data["data"]
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
150 )
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
151 elif msg_type == "force_close":
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
152 log.warning(f"force closing connection: {msg_data.get('reason')}")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
153 self.close()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
154 else:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
155 dialog.notification.show(
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
156 f"Unexpected message type {msg_type}"
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
157 "error"
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
158 )
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
159
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 class BroadcastChannel:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
162 handlers = {}
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 __init__(self):
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
165 log.debug(f"BroadcastChannel init with profile {self.profile!r}")
1545
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
166 self.ws = None
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
167 self._connected = False
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
168 self.start = time.time()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
169 self.bc = window.BroadcastChannel.new("libervia")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
170 self.bc.bind("message", self.on_message)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
171 # 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
172 # 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
173 # the first tab.
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
174 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
175 # set of all known tab ids
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
176 self.tabs_ids = {tab_id}
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
177 self.post("salut_a_vous", {
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
178 "profile": self.profile
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
179 })
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
180 window.bind("unload", self.on_unload)
1545
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
181 self._wait_connection_fut = aio.Future()
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
182
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
183 @property
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
184 def profile(self):
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
185 return window.profile or ""
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
186
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
187 @property
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
188 def connecting_tab(self) -> bool:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
189 """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
190 return self.ws is not None
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
191
1545
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
192 async def _wait_for_ws(self):
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
193 assert self.ws is not None
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
194 await self.ws.connection_ready_fut
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
195 self._wait_connection_fut.set_result(None)
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
196 self._connected = True
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
197
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
198 @connecting_tab.setter
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
199 def connecting_tab(self, connecting: bool) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
200 if connecting:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
201 if self.ws is None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
202 self.ws = WebSocket(self)
1579
5db55d01ce05 browser (bridge): workaround to fix unicode dumping of JSON:
Goffi <goffi@goffi.org>
parents: 1545
diff changeset
203 self.post("connection", {})
1545
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
204 aio.run(self._wait_for_ws())
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
205
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
206 elif self.ws is not None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
207 self.ws.close()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
208
1545
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
209 @property
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
210 def connected(self):
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
211 return self._connected
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
212
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
213 async def wait_for_connection(self):
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
214 if self._connected:
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
215 return
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
216 await self._wait_connection_fut
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
217
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
218 def establish_connection(self) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
219 """Called when there is no existing connection"""
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
220 timer.clear_timeout(self.check_connection_timer)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
221 log.debug(f"Establishing connection {tab_id=}")
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
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
224 def handle_bridge_signal(self, data: dict) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
225 """Forward bridge signals to registered handlers"""
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
226 signal = data["signal"]
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
227 handlers = self.handlers.get(signal, [])
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
228 for handler in handlers:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
229 handler(*data["args"])
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
230
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
231 def on_message(self, evt) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
232 data = json.loads(evt.data)
1579
5db55d01ce05 browser (bridge): workaround to fix unicode dumping of JSON:
Goffi <goffi@goffi.org>
parents: 1545
diff changeset
233 # FIXME: we convert back to int, see FIXME in [post] for details
5db55d01ce05 browser (bridge): workaround to fix unicode dumping of JSON:
Goffi <goffi@goffi.org>
parents: 1545
diff changeset
234 data["id"] = int(data["id"])
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
235 if data["type"] == "bridge":
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
236 self.handle_bridge_signal(data)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
237 elif data["type"] == "salut_a_toi":
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
238 # this is a response from existing tabs
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
239 other_tab_id = data["id"]
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
240 if other_tab_id == tab_id:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
241 # 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
242 # a new ID
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
243 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
244 window.location.reload()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
245 return
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
246 self.tabs_ids.add(other_tab_id)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
247 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
248 # this tab has the websocket connection to server
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
249 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
250 timer.clear_timeout(self.check_connection_timer)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
251 self.check_connection_timer = None
1545
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
252 self._wait_connection_fut.set_result(None)
be20e6ac9f22 browser (bridge): add methods/properties to wait for Websocket/BroadcastChannel to be ready
Goffi <goffi@goffi.org>
parents: 1518
diff changeset
253 self._connected = True
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
254 elif data["type"] == "salut_a_vous":
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
255 # a new tab has just been created
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
256 if data["profile"] != self.profile:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
257 log.info(
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
258 f"we are now connected with the profile {data['profile']}, "
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
259 "reloading the page"
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
260 )
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
261 window.location.reload()
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
262 else:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
263 self.tabs_ids.add(data["id"])
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
264 self.post("salut_a_toi", {
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
265 "id": tab_id,
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
266 "connecting_tab": self.connecting_tab
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
267 })
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
268 elif data["type"] == "connection":
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
269 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
270 elif data["type"] == "salut_a_rantanplan":
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
271 # a tab is being closed
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
272 other_tab_id = data["id"]
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
273 # 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
274 if other_tab_id != tab_id:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
275 self.tabs_ids.discard(other_tab_id)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
276 if data["connecting_tab"]:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
277 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
278 if max(self.tabs_ids) == tab_id:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
279 log.info("this is the new connecting tab, establish_connection")
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
280 self.connecting_tab = True
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
281 else:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
282 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
283 else:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
284 log.warning(f"unknown message type: {data}")
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 post(self, data_type, data: dict):
1579
5db55d01ce05 browser (bridge): workaround to fix unicode dumping of JSON:
Goffi <goffi@goffi.org>
parents: 1545
diff changeset
287 data["type"] = str(data_type)
5db55d01ce05 browser (bridge): workaround to fix unicode dumping of JSON:
Goffi <goffi@goffi.org>
parents: 1545
diff changeset
288 # FIXME: for some reason, JSON.stringify fail when a random.randint is used with
5db55d01ce05 browser (bridge): workaround to fix unicode dumping of JSON:
Goffi <goffi@goffi.org>
parents: 1545
diff changeset
289 # Brython 3.11 . See https://github.com/brython-dev/brython/issues/2332,
5db55d01ce05 browser (bridge): workaround to fix unicode dumping of JSON:
Goffi <goffi@goffi.org>
parents: 1545
diff changeset
290 # workaround may be removed once fixed version is used.
5db55d01ce05 browser (bridge): workaround to fix unicode dumping of JSON:
Goffi <goffi@goffi.org>
parents: 1545
diff changeset
291 data["id"] = str(tab_id)
5db55d01ce05 browser (bridge): workaround to fix unicode dumping of JSON:
Goffi <goffi@goffi.org>
parents: 1545
diff changeset
292 # FIXME: json.dumps doesn't support "ensure_ascii=False" and fails to correctly
5db55d01ce05 browser (bridge): workaround to fix unicode dumping of JSON:
Goffi <goffi@goffi.org>
parents: 1545
diff changeset
293 # dump emoji. See https://github.com/brython-dev/brython/issues/2331, workaround
5db55d01ce05 browser (bridge): workaround to fix unicode dumping of JSON:
Goffi <goffi@goffi.org>
parents: 1545
diff changeset
294 # may be removed once fixed version is used.
5db55d01ce05 browser (bridge): workaround to fix unicode dumping of JSON:
Goffi <goffi@goffi.org>
parents: 1545
diff changeset
295 dumped = javascript.JSON.stringify(data)
5db55d01ce05 browser (bridge): workaround to fix unicode dumping of JSON:
Goffi <goffi@goffi.org>
parents: 1545
diff changeset
296 self.bc.postMessage(dumped)
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
297 if data_type == "bridge":
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
298 self.handle_bridge_signal(data)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
299
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
300 def on_unload(self, evt) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
301 """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
302 self.post("salut_a_rantanplan", {
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
303 "id": tab_id,
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
304 "connecting_tab": self.connecting_tab
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
305 })
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
306
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
307
1510
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
308 class Bridge:
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
309 bc: BroadcastChannel | None = None
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
310
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
311 def __init__(self) -> None:
1510
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
312 if Bridge.bc is None:
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
313 Bridge.bc = BroadcastChannel()
1297
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
314
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
315 def __getattr__(self, attr):
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
316 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
317
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
318 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
319 if xhr.status == 200:
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
320 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
321 if callback is not None:
8998f01088ac browser (bridge): handle case where `callback` or `errback` is None
Goffi <goffi@goffi.org>
parents: 1297
diff changeset
322 if ret is None:
8998f01088ac browser (bridge): handle case where `callback` or `errback` is None
Goffi <goffi@goffi.org>
parents: 1297
diff changeset
323 callback()
8998f01088ac browser (bridge): handle case where `callback` or `errback` is None
Goffi <goffi@goffi.org>
parents: 1297
diff changeset
324 else:
8998f01088ac browser (bridge): handle case where `callback` or `errback` is None
Goffi <goffi@goffi.org>
parents: 1297
diff changeset
325 callback(ret)
1297
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
326 elif xhr.status == 502:
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
327 # 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
328 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
329 if errback is not None:
8998f01088ac browser (bridge): handle case where `callback` or `errback` is None
Goffi <goffi@goffi.org>
parents: 1297
diff changeset
330 errback(ret)
1297
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
331 else:
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
332 log.error(
1510
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
333 f"bridge call failed: code: {xhr.response}, text: {xhr.statusText}"
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
334 )
1314
8998f01088ac browser (bridge): handle case where `callback` or `errback` is None
Goffi <goffi@goffi.org>
parents: 1297
diff changeset
335 if errback is not None:
1477
b28025a7cc28 browser (bride): use `BridgeInternalError` for unmanaged errors:
Goffi <goffi@goffi.org>
parents: 1314
diff changeset
336 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
337
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
338 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
339 xhr = window.XMLHttpRequest.new()
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
340 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
341 xhr.bind('error', lambda ev: errback(
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
342 {"fullname": "ConnectionError", "message": xhr.statusText}))
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
343 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
344 data = javascript.JSON.stringify({
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
345 "args": args,
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
346 "kwargs": kwargs,
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
347 })
999dccf0093e browser: new bridge module to access restricted bridge from browser
Goffi <goffi@goffi.org>
parents:
diff changeset
348 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
349 xhr.send(data)
1504
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
350
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
351 def register_signal(self, signal: str, handler, iface=None) -> None:
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
352 BroadcastChannel.handlers.setdefault(signal, []).append(handler)
409d10211b20 server, browser: dynamic pages refactoring:
Goffi <goffi@goffi.org>
parents: 1477
diff changeset
353 log.debug(f"signal {signal} has been registered")
1510
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
354
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
355
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
356 class AsyncBridge:
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
357
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
358 def __getattr__(self, attr):
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
359 return lambda *args, **kwargs: self.call(attr, *args, **kwargs)
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
360
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
361 async def call(self, method_name, *args, **kwargs):
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
362 print(f"calling {method_name}")
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
363 data = javascript.JSON.stringify({
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
364 "args": args,
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
365 "kwargs": kwargs,
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
366 })
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
367 url = f"/_bridge/{method_name}"
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
368 r = await aio.post(
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
369 url,
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
370 headers={
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
371 'X-Csrf-Token': window.csrf_token,
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
372 },
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
373 data=data,
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
374 )
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
375
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
376 if r.status == 200:
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
377 return javascript.JSON.parse(r.data)
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
378 elif r.status == 502:
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
379 ret = javascript.JSON.parse(r.data)
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
380 raise BridgeException(ret['fullname'], ret['message'], ret['condition'])
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
381 else:
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
382 print(f"bridge called failed: code: {r.status}, text: {r.statusText}")
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
383 raise BridgeException("InternalError", r.statusText)
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
384
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
385 def register_signal(self, signal: str, handler, iface=None) -> None:
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
386 BroadcastChannel.handlers.setdefault(signal, []).append(handler)
5ea06e8b06ed browser: make bridge API closer to the one use with other frontends:
Goffi <goffi@goffi.org>
parents: 1509
diff changeset
387 log.debug(f"signal {signal} has been registered")