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