comparison src/server/server.py @ 914:0c0551967bdf

server, browser: partial Libervia fix Libervia was broken following the refactorings. This commit partially fixes it : Libervia is starting, avatar, blog and message are working again, but not everything is restablished yet. following things have been fixed/changed: - new dependency: shortuuid - D-Bus bridge is working again - fixed naming in several bridge methods - register method changed to register_signal - fixed Chat widget, which was not working anymore since the refactoring - avatar now use avatarGet. Cache dir is accessible using a session specific uuid, to avoid cache leak (i.e. accessing cache of other profiles) - server: new uuid attribute in session data Browser code is not fully working yet, notably OTR and contact list are not fully fixed.
author Goffi <goffi@goffi.org>
date Sun, 26 Feb 2017 18:32:47 +0100
parents 58f611481e6d
children e9e9d9d893a8
comparison
equal deleted inserted replaced
913:58f611481e6d 914:0c0551967bdf
31 from txjsonrpc.web import jsonrpc 31 from txjsonrpc.web import jsonrpc
32 from txjsonrpc import jsonrpclib 32 from txjsonrpc import jsonrpclib
33 33
34 from sat.core.log import getLogger 34 from sat.core.log import getLogger
35 log = getLogger(__name__) 35 log = getLogger(__name__)
36 from sat_frontends.bridge.DBus import DBusBridgeFrontend, BridgeExceptionNoService, const_TIMEOUT as BRIDGE_TIMEOUT 36 from sat_frontends.bridge.dbus_bridge import Bridge, BridgeExceptionNoService, const_TIMEOUT as BRIDGE_TIMEOUT
37 from sat.core.i18n import _, D_ 37 from sat.core.i18n import _, D_
38 from sat.core import exceptions 38 from sat.core import exceptions
39 from sat.tools import utils 39 from sat.tools import utils
40 from sat.tools.common import regex
40 41
41 import re 42 import re
42 import glob 43 import glob
43 import os.path 44 import os.path
44 import sys 45 import sys
45 import tempfile 46 import tempfile
46 import shutil 47 import shutil
47 import uuid 48 import uuid
48 import urlparse 49 import urlparse
49 import urllib 50 import urllib
51 import shortuuid
50 from zope.interface import Interface, Attribute, implements 52 from zope.interface import Interface, Attribute, implements
51 from httplib import HTTPS_PORT 53 from httplib import HTTPS_PORT
52 import libervia 54 import libervia
53 55
54 try: 56 try:
66 68
67 69
68 class ISATSession(Interface): 70 class ISATSession(Interface):
69 profile = Attribute("Sat profile") 71 profile = Attribute("Sat profile")
70 jid = Attribute("JID associated with the profile") 72 jid = Attribute("JID associated with the profile")
73 uuid = Attribute("uuid associated with the profile session")
71 74
72 75
73 class SATSession(object): 76 class SATSession(object):
74 implements(ISATSession) 77 implements(ISATSession)
75 78
76 def __init__(self, session): 79 def __init__(self, session):
77 self.profile = None 80 self.profile = None
78 self.jid = None 81 self.jid = None
82 self.uuid = unicode(shortuuid.uuid())
79 83
80 84
81 class LiberviaSession(server.Session): 85 class LiberviaSession(server.Session):
82 sessionTimeout = C.SESSION_TIMEOUT 86 sessionTimeout = C.SESSION_TIMEOUT
83 87
648 def jsonrpc_getPresenceStatuses(self): 652 def jsonrpc_getPresenceStatuses(self):
649 """Get Presence information for connected contacts""" 653 """Get Presence information for connected contacts"""
650 profile = ISATSession(self.session).profile 654 profile = ISATSession(self.session).profile
651 return self.sat_host.bridge.getPresenceStatuses(profile) 655 return self.sat_host.bridge.getPresenceStatuses(profile)
652 656
653 def jsonrpc_getHistory(self, from_jid, to_jid, size, between, search=''): 657 def jsonrpc_historyGet(self, from_jid, to_jid, size, between, search=''):
654 """Return history for the from_jid/to_jid couple""" 658 """Return history for the from_jid/to_jid couple"""
655 sat_session = ISATSession(self.session) 659 sat_session = ISATSession(self.session)
656 profile = sat_session.profile 660 profile = sat_session.profile
657 sat_jid = sat_session.jid 661 sat_jid = sat_session.jid
658 if not sat_jid: 662 if not sat_jid:
659 # we keep a session cache for jid to avoir jid spoofing 663 # we keep a session cache for jid to avoir jid spoofing
660 sat_jid = sat_session.jid = jid.JID(self.sat_host.bridge.getParamA("JabberID", "Connection", profile_key=profile)) 664 sat_jid = sat_session.jid = jid.JID(self.sat_host.bridge.getParamA("JabberID", "Connection", profile_key=profile))
661 if jid.JID(from_jid).userhost() != sat_jid.userhost() and jid.JID(to_jid).userhost() != sat_jid.userhost(): 665 if jid.JID(from_jid).userhost() != sat_jid.userhost() and jid.JID(to_jid).userhost() != sat_jid.userhost():
662 log.error(u"Trying to get history from a different jid (given (browser): {}, real (backend): {}), maybe a hack attempt ?".format(from_jid, sat_jid)) 666 log.error(u"Trying to get history from a different jid (given (browser): {}, real (backend): {}), maybe a hack attempt ?".format(from_jid, sat_jid))
663 return {} 667 return {}
664 d = self.asyncBridgeCall("getHistory", from_jid, to_jid, size, between, search, profile) 668 d = self.asyncBridgeCall("historyGet", from_jid, to_jid, size, between, search, profile)
665 669
666 def show(result_dbus): 670 def show(result_dbus):
667 result = [] 671 result = []
668 for line in result_dbus: 672 for line in result_dbus:
669 #XXX: we have to do this stupid thing because Python D-Bus use its own types instead of standard types 673 #XXX: we have to do this stupid thing because Python D-Bus use its own types instead of standard types
670 # and txJsonRPC doesn't accept D-Bus types, resulting in a empty query 674 # and txJsonRPC doesn't accept D-Bus types, resulting in a empty query
671 timestamp, from_jid, to_jid, message, mess_type, extra = line 675 uuid, timestamp, from_jid, to_jid, message, subject, mess_type, extra = line
672 result.append((float(timestamp), unicode(from_jid), unicode(to_jid), unicode(message), unicode(mess_type), dict(extra))) 676 result.append((unicode(uuid), float(timestamp), unicode(from_jid), unicode(to_jid), dict(message), dict(subject), unicode(mess_type), dict(extra)))
673 return result 677 return result
674 d.addCallback(show) 678 d.addCallback(show)
675 return d 679 return d
676 680
677 def jsonrpc_mucJoin(self, room_jid, nick): 681 def jsonrpc_mucJoin(self, room_jid, nick):
781 """Get VCard for entiry 785 """Get VCard for entiry
782 @param jid_: jid of contact from who we want data 786 @param jid_: jid of contact from who we want data
783 @return: id to retrieve the profile""" 787 @return: id to retrieve the profile"""
784 profile = ISATSession(self.session).profile 788 profile = ISATSession(self.session).profile
785 return self.sat_host.bridge.getCard(jid_, profile) 789 return self.sat_host.bridge.getCard(jid_, profile)
790
791 @defer.inlineCallbacks
792 def jsonrpc_avatarGet(self, entity, cache_only, hash_only):
793 session_data = ISATSession(self.session)
794 profile = session_data.profile
795 # profile_uuid = session_data.uuid
796 avatar = yield self.asyncBridgeCall("avatarGet", entity, cache_only, hash_only, profile)
797 if hash_only:
798 defer.returnValue(avatar)
799 else:
800 filename = os.path.basename(avatar)
801 avatar_url = os.path.join(C.CACHE_DIR, session_data.uuid, filename)
802 defer.returnValue(avatar_url)
786 803
787 def jsonrpc_getAccountDialogUI(self): 804 def jsonrpc_getAccountDialogUI(self):
788 """Get the dialog for managing user account 805 """Get the dialog for managing user account
789 @return: XML string of the XMLUI""" 806 @return: XML string of the XMLUI"""
790 profile = ISATSession(self.session).profile 807 profile = ISATSession(self.session).profile
918 if request.postpath == ['login']: 935 if request.postpath == ['login']:
919 return self.loginOrRegister(request) 936 return self.loginOrRegister(request)
920 _session = request.getSession() 937 _session = request.getSession()
921 parsed = jsonrpclib.loads(request.content.read()) 938 parsed = jsonrpclib.loads(request.content.read())
922 method = parsed.get("method") # pylint: disable=E1103 939 method = parsed.get("method") # pylint: disable=E1103
923 if method not in ['getSessionMetadata', 'registerParams', 'getMenus']: 940 if method not in ['getSessionMetadata', 'registerParams', 'menusGet']:
924 #if we don't call these methods, we need to be identified 941 #if we don't call these methods, we need to be identified
925 profile = ISATSession(_session).profile 942 profile = ISATSession(_session).profile
926 if not profile: 943 if not profile:
927 #user is not identified, we return a jsonrpc fault 944 #user is not identified, we return a jsonrpc fault
928 fault = jsonrpclib.Fault(C.ERRNUM_LIBERVIA, C.NOT_ALLOWED) # FIXME: define some standard error codes for libervia 945 fault = jsonrpclib.Fault(C.ERRNUM_LIBERVIA, C.NOT_ALLOWED) # FIXME: define some standard error codes for libervia
1018 request.write(C.PROFILE_AUTH_ERROR) 1035 request.write(C.PROFILE_AUTH_ERROR)
1019 request.finish() 1036 request.finish()
1020 return 1037 return
1021 register_with_ext_jid = False 1038 register_with_ext_jid = False
1022 1039
1023 connect_method = "asyncConnect" 1040 connect_method = "connect"
1024 1041
1025 if self.waiting_profiles.getRequest(profile): 1042 if self.waiting_profiles.getRequest(profile):
1026 request.write(C.ALREADY_WAITING) 1043 request.write(C.ALREADY_WAITING)
1027 request.finish() 1044 request.finish()
1028 return 1045 return
1103 register_with_ext_jid = self.waiting_profiles.getRegisterWithExtJid(profile) 1120 register_with_ext_jid = self.waiting_profiles.getRegisterWithExtJid(profile)
1104 self.waiting_profiles.purgeRequest(profile) 1121 self.waiting_profiles.purgeRequest(profile)
1105 _session = request.getSession() 1122 _session = request.getSession()
1106 sat_session = ISATSession(_session) 1123 sat_session = ISATSession(_session)
1107 if sat_session.profile: 1124 if sat_session.profile:
1108 log.error(('/!\\ Session has already a profile, this should NEVER happen!')) 1125 log.error(_(u'/!\\ Session has already a profile, this should NEVER happen!'))
1109 request.write(C.SESSION_ACTIVE) 1126 request.write(C.SESSION_ACTIVE)
1110 request.finish() 1127 request.finish()
1111 return 1128 return
1112 # we manage profile server side to avoid profile spoofing 1129 # we manage profile server side to avoid profile spoofing
1113 sat_session.profile = profile 1130 sat_session.profile = profile
1114 self.sat_host.prof_connected.add(profile) 1131 self.sat_host.prof_connected.add(profile)
1132 cache_dir = os.path.join(self.sat_host.cache_root_dir, regex.pathEscape(profile))
1133 # FIXME: would be better to have a global /cache URL which redirect to profile's cache directory, without uuid
1134 self.sat_host.cache_resource.putChild(sat_session.uuid, ProtectedFile(cache_dir))
1135 log.debug(_(u"profile cache resource added from {uuid} to {path}").format(uuid=sat_session.uuid, path=cache_dir))
1115 1136
1116 def onExpire(): 1137 def onExpire():
1117 log.info(u"Session expired (profile=%s)" % (profile,)) 1138 log.info(u"Session expired (profile=%s)" % (profile,))
1139 self.sat_host.cache_resource.delEntity(sat_session.uuid)
1140 log.debug(_(u"profile cache resource {uuid} deleted").format(uuid = sat_session.uuid))
1118 try: 1141 try:
1119 #We purge the queue 1142 #We purge the queue
1120 del self.sat_host.signal_handler.queue[profile] 1143 del self.sat_host.signal_handler.queue[profile]
1121 except KeyError: 1144 except KeyError:
1122 pass 1145 pass
1131 def jsonrpc_isConnected(self): 1154 def jsonrpc_isConnected(self):
1132 _session = self.request.getSession() 1155 _session = self.request.getSession()
1133 profile = ISATSession(_session).profile 1156 profile = ISATSession(_session).profile
1134 return self.sat_host.bridge.isConnected(profile) 1157 return self.sat_host.bridge.isConnected(profile)
1135 1158
1136 def jsonrpc_asyncConnect(self): 1159 def jsonrpc_connect(self):
1137 _session = self.request.getSession() 1160 _session = self.request.getSession()
1138 profile = ISATSession(_session).profile 1161 profile = ISATSession(_session).profile
1139 if self.waiting_profiles.getRequest(profile): 1162 if self.waiting_profiles.getRequest(profile):
1140 raise jsonrpclib.Fault(1, C.ALREADY_WAITING) # FIXME: define some standard error codes for libervia 1163 raise jsonrpclib.Fault(1, C.ALREADY_WAITING) # FIXME: define some standard error codes for libervia
1141 self.waiting_profiles.setRequest(self.request, profile) 1164 self.waiting_profiles.setRequest(self.request, profile)
1142 self.sat_host.bridge.asyncConnect(profile) 1165 self.sat_host.bridge.connect(profile)
1143 return server.NOT_DONE_YET 1166 return server.NOT_DONE_YET
1144 1167
1145 def jsonrpc_getSessionMetadata(self): 1168 def jsonrpc_getSessionMetadata(self):
1146 """Return metadata useful on session start 1169 """Return metadata useful on session start
1147 1170
1169 def jsonrpc_registerParams(self): 1192 def jsonrpc_registerParams(self):
1170 """Register the frontend specific parameters""" 1193 """Register the frontend specific parameters"""
1171 # params = """<params><individual>...</category></individual>""" 1194 # params = """<params><individual>...</category></individual>"""
1172 # self.sat_host.bridge.paramsRegisterApp(params, C.SECURITY_LIMIT, C.APP_NAME) 1195 # self.sat_host.bridge.paramsRegisterApp(params, C.SECURITY_LIMIT, C.APP_NAME)
1173 1196
1174 def jsonrpc_getMenus(self): 1197 def jsonrpc_menusGet(self):
1175 """Return the parameters XML for profile""" 1198 """Return the parameters XML for profile"""
1176 # XXX: we put this method in Register because we get menus before being logged 1199 # XXX: we put this method in Register because we get menus before being logged
1177 return self.sat_host.bridge.getMenus('', C.SECURITY_LIMIT) 1200 return self.sat_host.bridge.menusGet('', C.SECURITY_LIMIT)
1178 1201
1179 def _getSecurityWarning(self): 1202 def _getSecurityWarning(self):
1180 """@return: a security warning message, or None if the connection is secure""" 1203 """@return: a security warning message, or None if the connection is secure"""
1181 if self.request.URLPath().scheme == 'https' or not self.sat_host.options['security_warning']: 1204 if self.request.URLPath().scheme == 'https' or not self.sat_host.options['security_warning']:
1182 return None 1205 return None
1435 self.html_dir = os.path.join(self.options['data_dir'], C.HTML_DIR) 1458 self.html_dir = os.path.join(self.options['data_dir'], C.HTML_DIR)
1436 self.themes_dir = os.path.join(self.options['data_dir'], C.THEMES_DIR) 1459 self.themes_dir = os.path.join(self.options['data_dir'], C.THEMES_DIR)
1437 1460
1438 self._cleanup = [] 1461 self._cleanup = []
1439 1462
1440 root = LiberviaRootResource(self.options, self.html_dir)
1441
1442 self.signal_handler = SignalHandler(self) 1463 self.signal_handler = SignalHandler(self)
1464 self.sessions = {} # key = session value = user
1465 self.prof_connected = set() # Profiles connected
1466
1467 ## bridge ##
1468 try:
1469 self.bridge = Bridge()
1470 except BridgeExceptionNoService:
1471 print(u"Can't connect to SàT backend, are you sure it's launched ?")
1472 sys.exit(1)
1473 self.bridge.bridgeConnect(callback=self._bridgeCb, errback=self._bridgeEb)
1474
1475 def backendReady(self, dummy):
1476 self.root = root = LiberviaRootResource(self.options, self.html_dir)
1443 _register = Register(self) 1477 _register = Register(self)
1444 _upload_radiocol = UploadManagerRadioCol(self) 1478 _upload_radiocol = UploadManagerRadioCol(self)
1445 _upload_avatar = UploadManagerAvatar(self) 1479 _upload_avatar = UploadManagerAvatar(self)
1446 self.signal_handler.plugRegister(_register) 1480 self.signal_handler.plugRegister(_register)
1447 self.sessions = {} # key = session value = user 1481 self.bridge.register_signal("connected", self.signal_handler.connected)
1448 self.prof_connected = set() # Profiles connected 1482 self.bridge.register_signal("disconnected", self.signal_handler.disconnected)
1449 1483 #core
1450 ## bridge ## 1484 for signal_name in ['presenceUpdate', 'messageNew', 'subscribe', 'contactDeleted',
1451 try: 1485 'newContact', 'entityDataUpdated', 'paramUpdate']:
1452 self.bridge = DBusBridgeFrontend() 1486 self.bridge.register_signal(signal_name, self.signal_handler.getGenericCb(signal_name))
1453 except BridgeExceptionNoService: 1487 # XXX: actionNew is handled separately because the handler must manage security_limit
1454 print(u"Can't connect to SàT backend, are you sure it's launched ?") 1488 self.bridge.register_signal('actionNew', self.signal_handler.actionNewHandler)
1455 sys.exit(1) 1489 #plugins
1456 1490 for signal_name in ['psEvent', 'mucRoomJoined', 'tarotGameStarted', 'tarotGameNew', 'tarotGameChooseContrat',
1457 def backendReady(dummy): 1491 'tarotGameShowCards', 'tarotGameInvalidCards', 'tarotGameCardsPlayed', 'tarotGameYourTurn', 'tarotGameScore', 'tarotGamePlayers',
1458 self.bridge.register("connected", self.signal_handler.connected) 1492 'radiocolStarted', 'radiocolPreload', 'radiocolPlay', 'radiocolNoUpload', 'radiocolUploadOk', 'radiocolSongRejected', 'radiocolPlayers',
1459 self.bridge.register("disconnected", self.signal_handler.disconnected) 1493 'mucRoomLeft', 'mucRoomUserChangedNick', 'chatStateReceived']:
1460 #core 1494 self.bridge.register_signal(signal_name, self.signal_handler.getGenericCb(signal_name), "plugin")
1461 for signal_name in ['presenceUpdate', 'messageNew', 'subscribe', 'contactDeleted', 1495 self.media_dir = self.bridge.getConfig('', 'media_dir')
1462 'newContact', 'entityDataUpdated', 'paramUpdate']: 1496 self.local_dir = self.bridge.getConfig('', 'local_dir')
1463 self.bridge.register(signal_name, self.signal_handler.getGenericCb(signal_name)) 1497 self.cache_root_dir = os.path.join(
1464 # XXX: actionNew is handled separately because the handler must manage security_limit 1498 self.local_dir,
1465 self.bridge.register('actionNew', self.signal_handler.actionNewHandler) 1499 C.CACHE_DIR)
1466 #plugins 1500
1467 for signal_name in ['psEvent', 'mucRoomJoined', 'tarotGameStarted', 'tarotGameNew', 'tarotGameChooseContrat', 1501 # JSON APIs
1468 'tarotGameShowCards', 'tarotGameInvalidCards', 'tarotGameCardsPlayed', 'tarotGameYourTurn', 'tarotGameScore', 'tarotGamePlayers', 1502 self.putChild('json_signal_api', self.signal_handler)
1469 'radiocolStarted', 'radiocolPreload', 'radiocolPlay', 'radiocolNoUpload', 'radiocolUploadOk', 'radiocolSongRejected', 'radiocolPlayers', 1503 self.putChild('json_api', MethodHandler(self))
1470 'mucRoomLeft', 'mucRoomUserChangedNick', 'chatStateReceived']: 1504 self.putChild('register_api', _register)
1471 self.bridge.register(signal_name, self.signal_handler.getGenericCb(signal_name), "plugin") 1505
1472 self.media_dir = self.bridge.getConfig('', 'media_dir') 1506 # files upload
1473 self.local_dir = self.bridge.getConfig('', 'local_dir') 1507 self.putChild('upload_radiocol', _upload_radiocol)
1474 1508 self.putChild('upload_avatar', _upload_avatar)
1475 ## URLs ## 1509
1476 def putChild(path, resource): 1510 # static pages
1477 """Add a child to the root resource""" 1511 self.putChild('blog', MicroBlog(self))
1478 # FIXME: check that no information is leaked (c.f. https://twistedmatrix.com/documents/current/web/howto/using-twistedweb.html#request-encoders) 1512 self.putChild(C.THEMES_URL, ProtectedFile(self.themes_dir))
1479 root.putChild(path, web_resource.EncodingResourceWrapper(resource, [server.GzipEncoderFactory()])) 1513
1480 1514 # media dirs
1481 # JSON APIs 1515 # FIXME: get rid of dirname and "/" in C.XXX_DIR
1482 putChild('json_signal_api', self.signal_handler) 1516 self.putChild(os.path.dirname(C.MEDIA_DIR), ProtectedFile(self.media_dir))
1483 putChild('json_api', MethodHandler(self)) 1517 self.cache_resource = web_resource.NoResource()
1484 putChild('register_api', _register) 1518 self.putChild(C.CACHE_DIR, self.cache_resource)
1485 1519
1486 # files upload 1520 # special
1487 putChild('upload_radiocol', _upload_radiocol) 1521 self.putChild('radiocol', ProtectedFile(_upload_radiocol.getTmpDir(), defaultType="audio/ogg")) # FIXME: We cheat for PoC because we know we are on the same host, so we use directly upload dir
1488 putChild('upload_avatar', _upload_avatar) 1522 # pyjamas tests, redirected only for dev versions
1489 1523 if self.version[-1] == 'D':
1490 # static pages 1524 self.putChild('test', web_util.Redirect('/libervia_test.html'))
1491 putChild('blog', MicroBlog(self)) 1525
1492 putChild(C.THEMES_URL, ProtectedFile(self.themes_dir)) 1526
1493 1527 wrapped = web_resource.EncodingResourceWrapper(root, [server.GzipEncoderFactory()])
1494 # media dirs 1528 self.site = server.Site(wrapped)
1495 putChild(os.path.dirname(C.MEDIA_DIR), ProtectedFile(self.media_dir)) 1529 self.site.sessionFactory = LiberviaSession
1496 putChild(os.path.dirname(C.AVATARS_DIR), ProtectedFile(os.path.join(self.local_dir, C.AVATARS_DIR))) 1530
1497 1531
1498 # special 1532 def _bridgeCb(self):
1499 putChild('radiocol', ProtectedFile(_upload_radiocol.getTmpDir(), defaultType="audio/ogg")) # FIXME: We cheat for PoC because we know we are on the same host, so we use directly upload dir
1500 # pyjamas tests, redirected only for dev versions
1501 if self.version[-1] == 'D':
1502 putChild('test', web_util.Redirect('/libervia_test.html'))
1503
1504
1505 wrapped = web_resource.EncodingResourceWrapper(root, [server.GzipEncoderFactory()])
1506 self.site = server.Site(wrapped)
1507 self.site.sessionFactory = LiberviaSession
1508
1509 self.bridge.getReady(lambda: self.initialised.callback(None), 1533 self.bridge.getReady(lambda: self.initialised.callback(None),
1510 lambda failure: self.initialised.errback(Exception(failure))) 1534 lambda failure: self.initialised.errback(Exception(failure)))
1511 self.initialised.addCallback(backendReady) 1535 self.initialised.addCallback(self.backendReady)
1512 self.initialised.addErrback(lambda failure: log.error(u"Init error: %s" % failure)) 1536 self.initialised.addErrback(lambda failure: log.error(u"Init error: %s" % failure))
1537
1538 def _bridgeEb(self, failure):
1539 log.error(u"Can't connect to bridge: {}".format(failure))
1513 1540
1514 @property 1541 @property
1515 def version(self): 1542 def version(self):
1516 """Return the short version of Libervia""" 1543 """Return the short version of Libervia"""
1517 return C.APP_VERSION 1544 return C.APP_VERSION
1553 log.error(u"Can't check service profile ({profile}), are you sure it exists ?\n{error}".format( 1580 log.error(u"Can't check service profile ({profile}), are you sure it exists ?\n{error}".format(
1554 profile=C.SERVICE_PROFILE, error=msg)) 1581 profile=C.SERVICE_PROFILE, error=msg))
1555 self.stop() 1582 self.stop()
1556 return 1583 return
1557 if not connected: 1584 if not connected:
1558 self.bridge.asyncConnect(C.SERVICE_PROFILE, self.options['passphrase'], 1585 self.bridge.connect(C.SERVICE_PROFILE, self.options['passphrase'],
1559 callback=self._startService, errback=eb) 1586 {}, callback=self._startService, errback=eb)
1560 else: 1587 else:
1561 self._startService() 1588 self._startService()
1562 1589
1563 self.initialised.addCallback(initOk) 1590 self.initialised.addCallback(initOk)
1591
1592 ## URLs ##
1593 def putChild(self, path, resource):
1594 """Add a child to the root resource"""
1595 # FIXME: check that no information is leaked (c.f. https://twistedmatrix.com/documents/current/web/howto/using-twistedweb.html#request-encoders)
1596 self.root.putChild(path, web_resource.EncodingResourceWrapper(resource, [server.GzipEncoderFactory()]))
1564 1597
1565 ## TLS related methods ## 1598 ## TLS related methods ##
1566 1599
1567 def _TLSOptionsCheck(self): 1600 def _TLSOptionsCheck(self):
1568 """Check options coherence if TLS is activated, and update missing values 1601 """Check options coherence if TLS is activated, and update missing values