annotate libervia/server/websockets.py @ 1131:9cf592d1e6aa

server: better error handling when a named page mentioned in menu_json is missing
author Goffi <goffi@goffi.org>
date Tue, 18 Sep 2018 21:16:51 +0200
parents 28e3eb3bb217
children 2af117bfe6cc
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1 #!/usr/bin/python
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
2 # -*- coding: utf-8 -*-
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
3
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
4 # Libervia: a Salut à Toi frontend
1054
f2170536ba23 date update
Goffi <goffi@goffi.org>
parents: 995
diff changeset
5 # Copyright (C) 2011-2018 Jérôme Poisson <goffi@goffi.org>
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
6
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
7 # This program is free software: you can redistribute it and/or modify
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
8 # it under the terms of the GNU Affero General Public License as published by
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
9 # the Free Software Foundation, either version 3 of the License, or
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
10 # (at your option) any later version.
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
11
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
12 # This program is distributed in the hope that it will be useful,
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
15 # GNU Affero General Public License for more details.
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
16
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
17 # You should have received a copy of the GNU Affero General Public License
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
19
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
20 from sat.core.i18n import _
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
21 from sat.core.log import getLogger
1113
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
22
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
23 log = getLogger(__name__)
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
24 from sat.core import exceptions
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
25
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
26 from autobahn.twisted import websocket
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
27 from autobahn.twisted import resource as resource
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
28 from autobahn.websocket import types
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
29
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
30 import json
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
31
1113
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
32 LIBERVIA_PROTOCOL = "libervia_page"
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
33
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
34
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
35 class LiberviaPageWSProtocol(websocket.WebSocketServerProtocol):
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
36 host = None
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
37 tokens_map = {}
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
38
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
39 def onConnect(self, request):
1113
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
40 prefix = LIBERVIA_PROTOCOL + u"_"
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
41 for protocol in request.protocols:
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
42 if protocol.startswith(prefix):
1113
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
43 token = protocol[len(prefix) :].strip()
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
44 if token:
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
45 break
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
46 else:
1113
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
47 raise types.ConnectionDeny(
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
48 types.ConnectionDeny.NOT_IMPLEMENTED, u"Can't use this subprotocol"
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
49 )
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
50
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
51 if token not in self.tokens_map:
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
52 log.warning(_(u"Can't activate page socket: unknown token"))
1113
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
53 raise types.ConnectionDeny(
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
54 types.ConnectionDeny.FORBIDDEN, u"Bad token, please reload page"
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
55 )
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
56 self.token = token
1113
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
57 self.page = self.tokens_map[token]["page"]
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
58 self.request = self.tokens_map[token]["request"]
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
59 return protocol
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
60
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
61 def onOpen(self):
1113
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
62 log.debug(
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
63 _(
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
64 u"Websocket opened for {page} (token: {token})".format(
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
65 page=self.page, token=self.token
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
66 )
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
67 )
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
68 )
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
69 self.request.sendData = self.sendJSONData
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
70 self.page.onSocketOpen(self.request)
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
71
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
72 def onMessage(self, payload, isBinary):
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
73 try:
1113
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
74 data_json = json.loads(payload.decode("utf8"))
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
75 except ValueError as e:
1113
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
76 log.warning(
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
77 _(u"Not valid JSON, ignoring data: {msg}\n{data}").format(
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
78 msg=e, data=payload
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
79 )
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
80 )
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
81 return
1113
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
82 #  we request page first, to raise an AttributeError
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
83 # if it is not set (which should never happen)
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
84 page = self.page
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
85 try:
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
86 cb = page.on_data
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
87 except AttributeError:
1113
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
88 log.warning(
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
89 _(
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
90 u'No "on_data" method set on dynamic page, ignoring data:\n{data}'
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
91 ).format(data=data_json)
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
92 )
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
93 else:
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
94 cb(page, self.request, data_json)
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
95
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
96 def onClose(self, wasClean, code, reason):
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
97 try:
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
98 token = self.token
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
99 except AttributeError:
1113
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
100 log.warning(_(u"Websocket closed but no token is associated"))
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
101 return
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
102
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
103 self.page.onSocketClose(self.request)
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
104
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
105 try:
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
106 del self.tokens_map[token]
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
107 del self.request.sendData
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
108 except (KeyError, AttributeError):
1113
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
109 raise exceptions.InternalError(
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
110 _(u"Token or sendData doesn't exist, this should never happen!")
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
111 )
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
112 log.debug(
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
113 _(
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
114 u"Websocket closed for {page} (token: {token}). {reason}".format(
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
115 page=self.page,
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
116 token=self.token,
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
117 reason=u""
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
118 if wasClean
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
119 else _(u"Reason: {reason}").format(reason=reason),
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
120 )
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
121 )
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
122 )
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
123
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
124 def sendJSONData(self, type_, **data):
1113
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
125 assert "type" not in data
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
126 data["type"] = type_
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
127 self.sendMessage(json.dumps(data, ensure_ascii=False).encode("utf8"))
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
128
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
129 @classmethod
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
130 def getBaseURL(cls, host, secure):
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
131 return u"ws{sec}://localhost:{port}".format(
1113
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
132 sec="s" if secure else "",
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
133 port=cls.host.options["port_https" if secure else "port"],
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
134 )
995
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
135
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
136 @classmethod
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
137 def getResource(cls, host, secure):
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
138 if cls.host is None:
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
139 cls.host = host
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
140 factory = websocket.WebSocketServerFactory(cls.getBaseURL(host, secure))
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
141 factory.protocol = cls
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
142 return resource.WebSocketResource(factory)
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
143
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
144 @classmethod
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
145 def registerToken(cls, token, page, request):
f88325b56a6a server: dynamic pages first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
146 if token in cls.tokens_map:
1113
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
147 raise exceptions.ConflictError(_(u"This token is already registered"))
cdd389ef97bc server: code style reformatting using black
Goffi <goffi@goffi.org>
parents: 1054
diff changeset
148 cls.tokens_map[token] = {"page": page, "request": request}