diff --git a/synapse/app/__init__.py b/synapse/app/__init__.py
index c3afcc573b..f56f5fcc13 100644
--- a/synapse/app/__init__.py
+++ b/synapse/app/__init__.py
@@ -12,22 +12,38 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
+import logging
import sys
from synapse import python_dependencies # noqa: E402
sys.dont_write_bytecode = True
+logger = logging.getLogger(__name__)
try:
python_dependencies.check_requirements()
-except python_dependencies.MissingRequirementError as e:
- message = "\n".join([
- "Missing Requirement: %s" % (str(e),),
- "To install run:",
- " pip install --upgrade --force \"%s\"" % (e.dependency,),
- "",
- ])
- sys.stderr.writelines(message)
+except python_dependencies.DependencyException as e:
+ sys.stderr.writelines(e.message)
sys.exit(1)
+
+
+def check_bind_error(e, address, bind_addresses):
+ """
+ This method checks an exception occurred while binding on 0.0.0.0.
+ If :: is specified in the bind addresses a warning is shown.
+ The exception is still raised otherwise.
+
+ Binding on both 0.0.0.0 and :: causes an exception on Linux and macOS
+ because :: binds on both IPv4 and IPv6 (as per RFC 3493).
+ When binding on 0.0.0.0 after :: this can safely be ignored.
+
+ Args:
+ e (Exception): Exception that was caught.
+ address (str): Address on which binding was attempted.
+ bind_addresses (list): Addresses on which the service listens.
+ """
+ if address == '0.0.0.0' and '::' in bind_addresses:
+ logger.warn('Failed to listen on 0.0.0.0, continuing because listening on [::]')
+ else:
+ raise e
diff --git a/synapse/app/_base.py b/synapse/app/_base.py
index 18584226e9..32e8b8a3f5 100644
--- a/synapse/app/_base.py
+++ b/synapse/app/_base.py
@@ -15,18 +15,38 @@
import gc
import logging
+import signal
import sys
+import traceback
import psutil
from daemonize import Daemonize
from twisted.internet import error, reactor
+from twisted.protocols.tls import TLSMemoryBIOFactory
+import synapse
+from synapse.app import check_bind_error
+from synapse.crypto import context_factory
from synapse.util import PreserveLoggingContext
from synapse.util.rlimit import change_resource_limit
+from synapse.util.versionstring import get_version_string
logger = logging.getLogger(__name__)
+_sighup_callbacks = []
+
+
+def register_sighup(func):
+ """
+ Register a function to be called when a SIGHUP occurs.
+
+ Args:
+ func (function): Function to be called when sent a SIGHUP signal.
+ Will be called with a single argument, the homeserver.
+ """
+ _sighup_callbacks.append(func)
+
def start_worker_reactor(appname, config):
""" Run the reactor in the main process
@@ -135,62 +155,154 @@ def listen_metrics(bind_addresses, port):
from prometheus_client import start_http_server
for host in bind_addresses:
- reactor.callInThread(start_http_server, int(port),
- addr=host, registry=RegistryProxy)
- logger.info("Metrics now reporting on %s:%d", host, port)
+ logger.info("Starting metrics listener on %s:%d", host, port)
+ start_http_server(port, addr=host, registry=RegistryProxy)
def listen_tcp(bind_addresses, port, factory, reactor=reactor, backlog=50):
"""
Create a TCP socket for a port and several addresses
+
+ Returns:
+ list[twisted.internet.tcp.Port]: listening for TCP connections
"""
+ r = []
for address in bind_addresses:
try:
- reactor.listenTCP(
- port,
- factory,
- backlog,
- address
+ r.append(
+ reactor.listenTCP(
+ port,
+ factory,
+ backlog,
+ address
+ )
)
except error.CannotListenError as e:
check_bind_error(e, address, bind_addresses)
+ return r
+
def listen_ssl(
bind_addresses, port, factory, context_factory, reactor=reactor, backlog=50
):
"""
- Create an SSL socket for a port and several addresses
+ Create an TLS-over-TCP socket for a port and several addresses
+
+ Returns:
+ list of twisted.internet.tcp.Port listening for TLS connections
"""
+ r = []
for address in bind_addresses:
try:
- reactor.listenSSL(
- port,
- factory,
- context_factory,
- backlog,
- address
+ r.append(
+ reactor.listenSSL(
+ port,
+ factory,
+ context_factory,
+ backlog,
+ address
+ )
)
except error.CannotListenError as e:
check_bind_error(e, address, bind_addresses)
+ return r
-def check_bind_error(e, address, bind_addresses):
+
+def refresh_certificate(hs):
+ """
+ Refresh the TLS certificates that Synapse is using by re-reading them from
+ disk and updating the TLS context factories to use them.
"""
- This method checks an exception occurred while binding on 0.0.0.0.
- If :: is specified in the bind addresses a warning is shown.
- The exception is still raised otherwise.
- Binding on both 0.0.0.0 and :: causes an exception on Linux and macOS
- because :: binds on both IPv4 and IPv6 (as per RFC 3493).
- When binding on 0.0.0.0 after :: this can safely be ignored.
+ if not hs.config.has_tls_listener():
+ # attempt to reload the certs for the good of the tls_fingerprints
+ hs.config.read_certificate_from_disk(require_cert_and_key=False)
+ return
+
+ hs.config.read_certificate_from_disk(require_cert_and_key=True)
+ hs.tls_server_context_factory = context_factory.ServerContextFactory(hs.config)
+
+ if hs._listening_services:
+ logger.info("Updating context factories...")
+ for i in hs._listening_services:
+ # When you listenSSL, it doesn't make an SSL port but a TCP one with
+ # a TLS wrapping factory around the factory you actually want to get
+ # requests. This factory attribute is public but missing from
+ # Twisted's documentation.
+ if isinstance(i.factory, TLSMemoryBIOFactory):
+ addr = i.getHost()
+ logger.info(
+ "Replacing TLS context factory on [%s]:%i", addr.host, addr.port,
+ )
+ # We want to replace TLS factories with a new one, with the new
+ # TLS configuration. We do this by reaching in and pulling out
+ # the wrappedFactory, and then re-wrapping it.
+ i.factory = TLSMemoryBIOFactory(
+ hs.tls_server_context_factory,
+ False,
+ i.factory.wrappedFactory
+ )
+ logger.info("Context factories updated.")
+
+
+def start(hs, listeners=None):
+ """
+ Start a Synapse server or worker.
Args:
- e (Exception): Exception that was caught.
- address (str): Address on which binding was attempted.
- bind_addresses (list): Addresses on which the service listens.
+ hs (synapse.server.HomeServer)
+ listeners (list[dict]): Listener configuration ('listeners' in homeserver.yaml)
"""
- if address == '0.0.0.0' and '::' in bind_addresses:
- logger.warn('Failed to listen on 0.0.0.0, continuing because listening on [::]')
- else:
- raise e
+ try:
+ # Set up the SIGHUP machinery.
+ if hasattr(signal, "SIGHUP"):
+ def handle_sighup(*args, **kwargs):
+ for i in _sighup_callbacks:
+ i(hs)
+
+ signal.signal(signal.SIGHUP, handle_sighup)
+
+ register_sighup(refresh_certificate)
+
+ # Load the certificate from disk.
+ refresh_certificate(hs)
+
+ # It is now safe to start your Synapse.
+ hs.start_listening(listeners)
+ hs.get_datastore().start_profiling()
+
+ setup_sentry(hs)
+ except Exception:
+ traceback.print_exc(file=sys.stderr)
+ reactor = hs.get_reactor()
+ if reactor.running:
+ reactor.stop()
+ sys.exit(1)
+
+
+def setup_sentry(hs):
+ """Enable sentry integration, if enabled in configuration
+
+ Args:
+ hs (synapse.server.HomeServer)
+ """
+
+ if not hs.config.sentry_enabled:
+ return
+
+ import sentry_sdk
+ sentry_sdk.init(
+ dsn=hs.config.sentry_dsn,
+ release=get_version_string(synapse),
+ )
+
+ # We set some default tags that give some context to this instance
+ with sentry_sdk.configure_scope() as scope:
+ scope.set_tag("matrix_server_name", hs.config.server_name)
+
+ app = hs.config.worker_app if hs.config.worker_app else "synapse.app.homeserver"
+ name = hs.config.worker_name if hs.config.worker_name else "master"
+ scope.set_tag("worker_app", app)
+ scope.set_tag("worker_name", name)
diff --git a/synapse/app/appservice.py b/synapse/app/appservice.py
index 8559e141af..33107f56d1 100644
--- a/synapse/app/appservice.py
+++ b/synapse/app/appservice.py
@@ -168,12 +168,7 @@ def start(config_options):
)
ps.setup()
- ps.start_listening(config.worker_listeners)
-
- def start():
- ps.get_datastore().start_profiling()
-
- reactor.callWhenRunning(start)
+ reactor.callWhenRunning(_base.start, ps, config.worker_listeners)
_base.start_worker_reactor("synapse-appservice", config)
diff --git a/synapse/app/client_reader.py b/synapse/app/client_reader.py
index 76aed8c60a..043b48f8f3 100644
--- a/synapse/app/client_reader.py
+++ b/synapse/app/client_reader.py
@@ -25,7 +25,6 @@ from synapse.app import _base
from synapse.config._base import ConfigError
from synapse.config.homeserver import HomeServerConfig
from synapse.config.logger import setup_logging
-from synapse.crypto import context_factory
from synapse.http.server import JsonResource
from synapse.http.site import SynapseSite
from synapse.metrics import RegistryProxy
@@ -41,6 +40,7 @@ from synapse.replication.slave.storage.registration import SlavedRegistrationSto
from synapse.replication.slave.storage.room import RoomStore
from synapse.replication.slave.storage.transactions import SlavedTransactionStore
from synapse.replication.tcp.client import ReplicationClientHandler
+from synapse.rest.client.v1.login import LoginRestServlet
from synapse.rest.client.v1.room import (
JoinedRoomMemberListRestServlet,
PublicRoomListRestServlet,
@@ -48,6 +48,7 @@ from synapse.rest.client.v1.room import (
RoomMemberListRestServlet,
RoomStateRestServlet,
)
+from synapse.rest.client.v2_alpha.register import RegisterRestServlet
from synapse.server import HomeServer
from synapse.storage.engines import create_engine
from synapse.util.httpresourcetree import create_resource_tree
@@ -93,6 +94,8 @@ class ClientReaderServer(HomeServer):
JoinedRoomMemberListRestServlet(self).register(resource)
RoomStateRestServlet(self).register(resource)
RoomEventContextServlet(self).register(resource)
+ RegisterRestServlet(self).register(resource)
+ LoginRestServlet(self).register(resource)
resources.update({
"/_matrix/client/r0": resource,
@@ -164,26 +167,16 @@ def start(config_options):
database_engine = create_engine(config.database_config)
- tls_server_context_factory = context_factory.ServerContextFactory(config)
- tls_client_options_factory = context_factory.ClientTLSOptionsFactory(config)
-
ss = ClientReaderServer(
config.server_name,
db_config=config.database_config,
- tls_server_context_factory=tls_server_context_factory,
- tls_client_options_factory=tls_client_options_factory,
config=config,
version_string="Synapse/" + get_version_string(synapse),
database_engine=database_engine,
)
ss.setup()
- ss.start_listening(config.worker_listeners)
-
- def start():
- ss.get_datastore().start_profiling()
-
- reactor.callWhenRunning(start)
+ reactor.callWhenRunning(_base.start, ss, config.worker_listeners)
_base.start_worker_reactor("synapse-client-reader", config)
diff --git a/synapse/app/event_creator.py b/synapse/app/event_creator.py
index e4a68715aa..b8e5196152 100644
--- a/synapse/app/event_creator.py
+++ b/synapse/app/event_creator.py
@@ -25,7 +25,6 @@ from synapse.app import _base
from synapse.config._base import ConfigError
from synapse.config.homeserver import HomeServerConfig
from synapse.config.logger import setup_logging
-from synapse.crypto import context_factory
from synapse.http.server import JsonResource
from synapse.http.site import SynapseSite
from synapse.metrics import RegistryProxy
@@ -185,26 +184,16 @@ def start(config_options):
database_engine = create_engine(config.database_config)
- tls_server_context_factory = context_factory.ServerContextFactory(config)
- tls_client_options_factory = context_factory.ClientTLSOptionsFactory(config)
-
ss = EventCreatorServer(
config.server_name,
db_config=config.database_config,
- tls_server_context_factory=tls_server_context_factory,
- tls_client_options_factory=tls_client_options_factory,
config=config,
version_string="Synapse/" + get_version_string(synapse),
database_engine=database_engine,
)
ss.setup()
- ss.start_listening(config.worker_listeners)
-
- def start():
- ss.get_datastore().start_profiling()
-
- reactor.callWhenRunning(start)
+ reactor.callWhenRunning(_base.start, ss, config.worker_listeners)
_base.start_worker_reactor("synapse-event-creator", config)
diff --git a/synapse/app/federation_reader.py b/synapse/app/federation_reader.py
index 228a297fb8..b116c17669 100644
--- a/synapse/app/federation_reader.py
+++ b/synapse/app/federation_reader.py
@@ -26,7 +26,6 @@ from synapse.app import _base
from synapse.config._base import ConfigError
from synapse.config.homeserver import HomeServerConfig
from synapse.config.logger import setup_logging
-from synapse.crypto import context_factory
from synapse.federation.transport.server import TransportLayerServer
from synapse.http.site import SynapseSite
from synapse.metrics import RegistryProxy
@@ -41,6 +40,7 @@ from synapse.replication.slave.storage.profile import SlavedProfileStore
from synapse.replication.slave.storage.push_rule import SlavedPushRuleStore
from synapse.replication.slave.storage.pushers import SlavedPusherStore
from synapse.replication.slave.storage.receipts import SlavedReceiptsStore
+from synapse.replication.slave.storage.registration import SlavedRegistrationStore
from synapse.replication.slave.storage.room import RoomStore
from synapse.replication.slave.storage.transactions import SlavedTransactionStore
from synapse.replication.tcp.client import ReplicationClientHandler
@@ -63,6 +63,7 @@ class FederationReaderSlavedStore(
SlavedReceiptsStore,
SlavedEventStore,
SlavedKeyStore,
+ SlavedRegistrationStore,
RoomStore,
DirectoryStore,
SlavedTransactionStore,
@@ -87,6 +88,16 @@ class FederationReaderServer(HomeServer):
resources.update({
FEDERATION_PREFIX: TransportLayerServer(self),
})
+ if name == "openid" and "federation" not in res["names"]:
+ # Only load the openid resource separately if federation resource
+ # is not specified since federation resource includes openid
+ # resource.
+ resources.update({
+ FEDERATION_PREFIX: TransportLayerServer(
+ self,
+ servlet_groups=["openid"],
+ ),
+ })
root_resource = create_resource_tree(resources, NoResource())
@@ -99,7 +110,8 @@ class FederationReaderServer(HomeServer):
listener_config,
root_resource,
self.version_string,
- )
+ ),
+ reactor=self.get_reactor()
)
logger.info("Synapse federation reader now listening on port %d", port)
@@ -151,26 +163,16 @@ def start(config_options):
database_engine = create_engine(config.database_config)
- tls_server_context_factory = context_factory.ServerContextFactory(config)
- tls_client_options_factory = context_factory.ClientTLSOptionsFactory(config)
-
ss = FederationReaderServer(
config.server_name,
db_config=config.database_config,
- tls_server_context_factory=tls_server_context_factory,
- tls_client_options_factory=tls_client_options_factory,
config=config,
version_string="Synapse/" + get_version_string(synapse),
database_engine=database_engine,
)
ss.setup()
- ss.start_listening(config.worker_listeners)
-
- def start():
- ss.get_datastore().start_profiling()
-
- reactor.callWhenRunning(start)
+ reactor.callWhenRunning(_base.start, ss, config.worker_listeners)
_base.start_worker_reactor("synapse-federation-reader", config)
diff --git a/synapse/app/federation_sender.py b/synapse/app/federation_sender.py
index e9a99d76e1..a461442fdc 100644
--- a/synapse/app/federation_sender.py
+++ b/synapse/app/federation_sender.py
@@ -25,7 +25,6 @@ from synapse.app import _base
from synapse.config._base import ConfigError
from synapse.config.homeserver import HomeServerConfig
from synapse.config.logger import setup_logging
-from synapse.crypto import context_factory
from synapse.federation import send_queue
from synapse.http.site import SynapseSite
from synapse.metrics import RegistryProxy
@@ -183,26 +182,17 @@ def start(config_options):
# Force the pushers to start since they will be disabled in the main config
config.send_federation = True
- tls_server_context_factory = context_factory.ServerContextFactory(config)
- tls_client_options_factory = context_factory.ClientTLSOptionsFactory(config)
-
- ps = FederationSenderServer(
+ ss = FederationSenderServer(
config.server_name,
db_config=config.database_config,
- tls_server_context_factory=tls_server_context_factory,
- tls_client_options_factory=tls_client_options_factory,
config=config,
version_string="Synapse/" + get_version_string(synapse),
database_engine=database_engine,
)
- ps.setup()
- ps.start_listening(config.worker_listeners)
-
- def start():
- ps.get_datastore().start_profiling()
+ ss.setup()
+ reactor.callWhenRunning(_base.start, ss, config.worker_listeners)
- reactor.callWhenRunning(start)
_base.start_worker_reactor("synapse-federation-sender", config)
diff --git a/synapse/app/frontend_proxy.py b/synapse/app/frontend_proxy.py
index f5c61dec5b..8479fee738 100644
--- a/synapse/app/frontend_proxy.py
+++ b/synapse/app/frontend_proxy.py
@@ -21,12 +21,11 @@ from twisted.web.resource import NoResource
import synapse
from synapse import events
-from synapse.api.errors import SynapseError
+from synapse.api.errors import HttpResponseException, SynapseError
from synapse.app import _base
from synapse.config._base import ConfigError
from synapse.config.homeserver import HomeServerConfig
from synapse.config.logger import setup_logging
-from synapse.crypto import context_factory
from synapse.http.server import JsonResource
from synapse.http.servlet import RestServlet, parse_json_object_from_request
from synapse.http.site import SynapseSite
@@ -67,10 +66,15 @@ class PresenceStatusStubServlet(ClientV1RestServlet):
headers = {
"Authorization": auth_headers,
}
- result = yield self.http_client.get_json(
- self.main_uri + request.uri.decode('ascii'),
- headers=headers,
- )
+
+ try:
+ result = yield self.http_client.get_json(
+ self.main_uri + request.uri.decode('ascii'),
+ headers=headers,
+ )
+ except HttpResponseException as e:
+ raise e.to_synapse_error()
+
defer.returnValue((200, result))
@defer.inlineCallbacks
@@ -241,26 +245,16 @@ def start(config_options):
database_engine = create_engine(config.database_config)
- tls_server_context_factory = context_factory.ServerContextFactory(config)
- tls_client_options_factory = context_factory.ClientTLSOptionsFactory(config)
-
ss = FrontendProxyServer(
config.server_name,
db_config=config.database_config,
- tls_server_context_factory=tls_server_context_factory,
- tls_client_options_factory=tls_client_options_factory,
config=config,
version_string="Synapse/" + get_version_string(synapse),
database_engine=database_engine,
)
ss.setup()
- ss.start_listening(config.worker_listeners)
-
- def start():
- ss.get_datastore().start_profiling()
-
- reactor.callWhenRunning(start)
+ reactor.callWhenRunning(_base.start, ss, config.worker_listeners)
_base.start_worker_reactor("synapse-frontend-proxy", config)
diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py
index 593e1e75db..05a97979ec 100755
--- a/synapse/app/homeserver.py
+++ b/synapse/app/homeserver.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright 2014-2016 OpenMarket Ltd
+# Copyright 2019 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +14,9 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+
+from __future__ import print_function
+
import gc
import logging
import os
@@ -25,6 +29,7 @@ from prometheus_client import Gauge
from twisted.application import service
from twisted.internet import defer, reactor
+from twisted.python.failure import Failure
from twisted.web.resource import EncodingResourceWrapper, NoResource
from twisted.web.server import GzipEncoderFactory
from twisted.web.static import File
@@ -37,7 +42,6 @@ from synapse.api.urls import (
FEDERATION_PREFIX,
LEGACY_MEDIA_PREFIX,
MEDIA_PREFIX,
- SERVER_KEY_PREFIX,
SERVER_KEY_V2_PREFIX,
STATIC_PREFIX,
WEB_CLIENT_PREFIX,
@@ -46,7 +50,6 @@ from synapse.app import _base
from synapse.app._base import listen_ssl, listen_tcp, quit_with_error
from synapse.config._base import ConfigError
from synapse.config.homeserver import HomeServerConfig
-from synapse.crypto import context_factory
from synapse.federation.transport.server import TransportLayerServer
from synapse.http.additional_resource import AdditionalResource
from synapse.http.server import RootRedirect
@@ -55,13 +58,13 @@ from synapse.metrics import RegistryProxy
from synapse.metrics.background_process_metrics import run_as_background_process
from synapse.metrics.resource import METRICS_PREFIX, MetricsResource
from synapse.module_api import ModuleApi
-from synapse.python_dependencies import CONDITIONAL_REQUIREMENTS, check_requirements
+from synapse.python_dependencies import check_requirements
from synapse.replication.http import REPLICATION_PREFIX, ReplicationRestResource
from synapse.replication.tcp.resource import ReplicationStreamProtocolFactory
from synapse.rest import ClientRestResource
-from synapse.rest.key.v1.server_key_resource import LocalKey
from synapse.rest.key.v2 import KeyApiV2Resource
from synapse.rest.media.v0.content_repository import ContentRepoResource
+from synapse.rest.well_known import WellKnownResource
from synapse.server import HomeServer
from synapse.storage import DataStore, are_all_users_on_domain
from synapse.storage.engines import IncorrectDatabaseSetup, create_engine
@@ -81,36 +84,6 @@ def gz_wrap(r):
return EncodingResourceWrapper(r, [GzipEncoderFactory()])
-def build_resource_for_web_client(hs):
- webclient_path = hs.get_config().web_client_location
- if not webclient_path:
- try:
- import syweb
- except ImportError:
- quit_with_error(
- "Could not find a webclient.\n\n"
- "Please either install the matrix-angular-sdk or configure\n"
- "the location of the source to serve via the configuration\n"
- "option `web_client_location`\n\n"
- "To install the `matrix-angular-sdk` via pip, run:\n\n"
- " pip install '%(dep)s'\n"
- "\n"
- "You can also disable hosting of the webclient via the\n"
- "configuration option `web_client`\n"
- % {"dep": CONDITIONAL_REQUIREMENTS["web_client"].keys()[0]}
- )
- syweb_path = os.path.dirname(syweb.__file__)
- webclient_path = os.path.join(syweb_path, "webclient")
- # GZip is disabled here due to
- # https://twistedmatrix.com/trac/ticket/7678
- # (It can stay enabled for the API resources: they call
- # write() with the whole body and then finish() straight
- # after and so do not trigger the bug.
- # GzipFile was removed in commit 184ba09
- # return GzipFile(webclient_path) # TODO configurable?
- return File(webclient_path) # TODO configurable?
-
-
class SynapseHomeServer(HomeServer):
DATASTORE_CLASS = DataStore
@@ -120,12 +93,13 @@ class SynapseHomeServer(HomeServer):
tls = listener_config.get("tls", False)
site_tag = listener_config.get("tag", port)
- if tls and config.no_tls:
- return
-
resources = {}
for res in listener_config["resources"]:
for name in res["names"]:
+ if name == "openid" and "federation" in res["names"]:
+ # Skip loading openid resource if federation is defined
+ # since federation resource will include openid
+ continue
resources.update(self._configure_named_resource(
name, res.get("compress", False),
))
@@ -139,15 +113,18 @@ class SynapseHomeServer(HomeServer):
handler = handler_cls(config, module_api)
resources[path] = AdditionalResource(self, handler.handle_request)
+ # try to find something useful to redirect '/' to
if WEB_CLIENT_PREFIX in resources:
root_resource = RootRedirect(WEB_CLIENT_PREFIX)
+ elif STATIC_PREFIX in resources:
+ root_resource = RootRedirect(STATIC_PREFIX)
else:
root_resource = NoResource()
root_resource = create_resource_tree(resources, root_resource)
if tls:
- listen_ssl(
+ ports = listen_ssl(
bind_addresses,
port,
SynapseSite(
@@ -158,10 +135,12 @@ class SynapseHomeServer(HomeServer):
self.version_string,
),
self.tls_server_context_factory,
+ reactor=self.get_reactor(),
)
+ logger.info("Synapse now listening on TCP port %d (TLS)", port)
else:
- listen_tcp(
+ ports = listen_tcp(
bind_addresses,
port,
SynapseSite(
@@ -170,9 +149,12 @@ class SynapseHomeServer(HomeServer):
listener_config,
root_resource,
self.version_string,
- )
+ ),
+ reactor=self.get_reactor(),
)
- logger.info("Synapse now listening on port %d", port)
+ logger.info("Synapse now listening on TCP port %d", port)
+
+ return ports
def _configure_named_resource(self, name, compress=False):
"""Build a resource map for a named resource
@@ -197,8 +179,13 @@ class SynapseHomeServer(HomeServer):
"/_matrix/client/unstable": client_resource,
"/_matrix/client/v2_alpha": client_resource,
"/_matrix/client/versions": client_resource,
+ "/.well-known/matrix/client": WellKnownResource(self),
})
+ if self.get_config().saml2_enabled:
+ from synapse.rest.saml2 import SAML2Resource
+ resources["/_matrix/saml2"] = SAML2Resource(self)
+
if name == "consent":
from synapse.rest.consent.consent_resource import ConsentResource
consent_resource = ConsentResource(self)
@@ -213,6 +200,11 @@ class SynapseHomeServer(HomeServer):
FEDERATION_PREFIX: TransportLayerServer(self),
})
+ if name == "openid":
+ resources.update({
+ FEDERATION_PREFIX: TransportLayerServer(self, servlet_groups=["openid"]),
+ })
+
if name in ["static", "client"]:
resources.update({
STATIC_PREFIX: File(
@@ -236,13 +228,19 @@ class SynapseHomeServer(HomeServer):
)
if name in ["keys", "federation"]:
- resources.update({
- SERVER_KEY_PREFIX: LocalKey(self),
- SERVER_KEY_V2_PREFIX: KeyApiV2Resource(self),
- })
+ resources[SERVER_KEY_V2_PREFIX] = KeyApiV2Resource(self)
if name == "webclient":
- resources[WEB_CLIENT_PREFIX] = build_resource_for_web_client(self)
+ webclient_path = self.get_config().web_client_location
+
+ if webclient_path is None:
+ logger.warning(
+ "Not enabling webclient resource, as web_client_location is unset."
+ )
+ else:
+ # GZip is disabled here due to
+ # https://twistedmatrix.com/trac/ticket/7678
+ resources[WEB_CLIENT_PREFIX] = File(webclient_path)
if name == "metrics" and self.get_config().enable_metrics:
resources[METRICS_PREFIX] = MetricsResource(RegistryProxy)
@@ -252,12 +250,14 @@ class SynapseHomeServer(HomeServer):
return resources
- def start_listening(self):
+ def start_listening(self, listeners):
config = self.get_config()
- for listener in config.listeners:
+ for listener in listeners:
if listener["type"] == "http":
- self._listener_http(config, listener)
+ self._listening_services.extend(
+ self._listener_http(config, listener)
+ )
elif listener["type"] == "manhole":
listen_tcp(
listener["bind_addresses"],
@@ -269,14 +269,14 @@ class SynapseHomeServer(HomeServer):
)
)
elif listener["type"] == "replication":
- bind_addresses = listener["bind_addresses"]
- for address in bind_addresses:
- factory = ReplicationStreamProtocolFactory(self)
- server_listener = reactor.listenTCP(
- listener["port"], factory, interface=address
- )
+ services = listen_tcp(
+ listener["bind_addresses"],
+ listener["port"],
+ ReplicationStreamProtocolFactory(self),
+ )
+ for s in services:
reactor.addSystemEventTrigger(
- "before", "shutdown", server_listener.stopListening,
+ "before", "shutdown", s.stopListening,
)
elif listener["type"] == "metrics":
if not self.get_config().enable_metrics:
@@ -337,24 +337,19 @@ def setup(config_options):
# generating config files and shouldn't try to continue.
sys.exit(0)
- synapse.config.logger.setup_logging(config, use_worker_options=False)
-
- # check any extra requirements we have now we have a config
- check_requirements(config)
+ synapse.config.logger.setup_logging(
+ config,
+ use_worker_options=False
+ )
events.USE_FROZEN_DICTS = config.use_frozen_dicts
- tls_server_context_factory = context_factory.ServerContextFactory(config)
- tls_client_options_factory = context_factory.ClientTLSOptionsFactory(config)
-
database_engine = create_engine(config.database_config)
config.database_config["args"]["cp_openfun"] = database_engine.on_new_connection
hs = SynapseHomeServer(
config.server_name,
db_config=config.database_config,
- tls_server_context_factory=tls_server_context_factory,
- tls_client_options_factory=tls_client_options_factory,
config=config,
version_string="Synapse/" + get_version_string(synapse),
database_engine=database_engine,
@@ -381,12 +376,79 @@ def setup(config_options):
logger.info("Database prepared in %s.", config.database_config['name'])
hs.setup()
- hs.start_listening()
+ @defer.inlineCallbacks
+ def do_acme():
+ """
+ Reprovision an ACME certificate, if it's required.
+
+ Returns:
+ Deferred[bool]: Whether the cert has been updated.
+ """
+ acme = hs.get_acme_handler()
+
+ # Check how long the certificate is active for.
+ cert_days_remaining = hs.config.is_disk_cert_valid(
+ allow_self_signed=False
+ )
+
+ # We want to reprovision if cert_days_remaining is None (meaning no
+ # certificate exists), or the days remaining number it returns
+ # is less than our re-registration threshold.
+ provision = False
+
+ if (
+ cert_days_remaining is None or
+ cert_days_remaining < hs.config.acme_reprovision_threshold
+ ):
+ provision = True
+
+ if provision:
+ yield acme.provision_certificate()
+
+ defer.returnValue(provision)
+
+ @defer.inlineCallbacks
+ def reprovision_acme():
+ """
+ Provision a certificate from ACME, if required, and reload the TLS
+ certificate if it's renewed.
+ """
+ reprovisioned = yield do_acme()
+ if reprovisioned:
+ _base.refresh_certificate(hs)
+
+ @defer.inlineCallbacks
def start():
- hs.get_pusherpool().start()
- hs.get_datastore().start_profiling()
- hs.get_datastore().start_doing_background_updates()
+ try:
+ # Run the ACME provisioning code, if it's enabled.
+ if hs.config.acme_enabled:
+ acme = hs.get_acme_handler()
+ # Start up the webservices which we will respond to ACME
+ # challenges with, and then provision.
+ yield acme.start_listening()
+ yield do_acme()
+
+ # Check if it needs to be reprovisioned every day.
+ hs.get_clock().looping_call(
+ reprovision_acme,
+ 24 * 60 * 60 * 1000
+ )
+
+ _base.start(hs, config.listeners)
+
+ hs.get_pusherpool().start()
+ hs.get_datastore().start_doing_background_updates()
+ except Exception:
+ # Print the exception and bail out.
+ print("Error during startup:", file=sys.stderr)
+
+ # this gives better tracebacks than traceback.print_exc()
+ Failure().printTraceback(file=sys.stderr)
+
+ if reactor.running:
+ reactor.stop()
+ sys.exit(1)
reactor.callWhenRunning(start)
@@ -394,7 +456,8 @@ def setup(config_options):
class SynapseService(service.Service):
- """A twisted Service class that will start synapse. Used to run synapse
+ """
+ A twisted Service class that will start synapse. Used to run synapse
via twistd and a .tac.
"""
def __init__(self, config):
@@ -540,7 +603,7 @@ def run(hs):
current_mau_count = 0
reserved_count = 0
store = hs.get_datastore()
- if hs.config.limit_usage_by_mau:
+ if hs.config.limit_usage_by_mau or hs.config.mau_stats_only:
current_mau_count = yield store.get_monthly_active_count()
reserved_count = yield store.get_registered_reserved_users_count()
current_mau_gauge.set(float(current_mau_count))
@@ -554,7 +617,7 @@ def run(hs):
)
start_generate_monthly_active_users()
- if hs.config.limit_usage_by_mau:
+ if hs.config.limit_usage_by_mau or hs.config.mau_stats_only:
clock.looping_call(start_generate_monthly_active_users, 5 * 60 * 1000)
# End of monthly active user settings
diff --git a/synapse/app/media_repository.py b/synapse/app/media_repository.py
index acc0487adc..d4cc4e9443 100644
--- a/synapse/app/media_repository.py
+++ b/synapse/app/media_repository.py
@@ -26,7 +26,6 @@ from synapse.app import _base
from synapse.config._base import ConfigError
from synapse.config.homeserver import HomeServerConfig
from synapse.config.logger import setup_logging
-from synapse.crypto import context_factory
from synapse.http.site import SynapseSite
from synapse.metrics import RegistryProxy
from synapse.metrics.resource import METRICS_PREFIX, MetricsResource
@@ -151,26 +150,16 @@ def start(config_options):
database_engine = create_engine(config.database_config)
- tls_server_context_factory = context_factory.ServerContextFactory(config)
- tls_client_options_factory = context_factory.ClientTLSOptionsFactory(config)
-
ss = MediaRepositoryServer(
config.server_name,
db_config=config.database_config,
- tls_server_context_factory=tls_server_context_factory,
- tls_client_options_factory=tls_client_options_factory,
config=config,
version_string="Synapse/" + get_version_string(synapse),
database_engine=database_engine,
)
ss.setup()
- ss.start_listening(config.worker_listeners)
-
- def start():
- ss.get_datastore().start_profiling()
-
- reactor.callWhenRunning(start)
+ reactor.callWhenRunning(_base.start, ss, config.worker_listeners)
_base.start_worker_reactor("synapse-media-repository", config)
diff --git a/synapse/app/pusher.py b/synapse/app/pusher.py
index 83b0863f00..cbf0d67f51 100644
--- a/synapse/app/pusher.py
+++ b/synapse/app/pusher.py
@@ -224,11 +224,10 @@ def start(config_options):
)
ps.setup()
- ps.start_listening(config.worker_listeners)
def start():
+ _base.start(ps, config.worker_listeners)
ps.get_pusherpool().start()
- ps.get_datastore().start_profiling()
reactor.callWhenRunning(start)
diff --git a/synapse/app/synchrotron.py b/synapse/app/synchrotron.py
index 3926c7f263..9163b56d86 100644
--- a/synapse/app/synchrotron.py
+++ b/synapse/app/synchrotron.py
@@ -226,7 +226,15 @@ class SynchrotronPresence(object):
class SynchrotronTyping(object):
def __init__(self, hs):
self._latest_room_serial = 0
+ self._reset()
+
+ def _reset(self):
+ """
+ Reset the typing handler's data caches.
+ """
+ # map room IDs to serial numbers
self._room_serials = {}
+ # map room IDs to sets of users currently typing
self._room_typing = {}
def stream_positions(self):
@@ -236,6 +244,12 @@ class SynchrotronTyping(object):
return {"typing": self._latest_room_serial}
def process_replication_rows(self, token, rows):
+ if self._latest_room_serial > token:
+ # The master has gone backwards. To prevent inconsistent data, just
+ # clear everything.
+ self._reset()
+
+ # Set the latest serial token to whatever the server gave us.
self._latest_room_serial = token
for row in rows:
@@ -431,12 +445,7 @@ def start(config_options):
)
ss.setup()
- ss.start_listening(config.worker_listeners)
-
- def start():
- ss.get_datastore().start_profiling()
-
- reactor.callWhenRunning(start)
+ reactor.callWhenRunning(_base.start, ss, config.worker_listeners)
_base.start_worker_reactor("synapse-synchrotron", config)
diff --git a/synapse/app/user_dir.py b/synapse/app/user_dir.py
index 0a5f62b509..d1ab9512cd 100644
--- a/synapse/app/user_dir.py
+++ b/synapse/app/user_dir.py
@@ -26,7 +26,6 @@ from synapse.app import _base
from synapse.config._base import ConfigError
from synapse.config.homeserver import HomeServerConfig
from synapse.config.logger import setup_logging
-from synapse.crypto import context_factory
from synapse.http.server import JsonResource
from synapse.http.site import SynapseSite
from synapse.metrics import RegistryProxy
@@ -211,26 +210,16 @@ def start(config_options):
# Force the pushers to start since they will be disabled in the main config
config.update_user_directory = True
- tls_server_context_factory = context_factory.ServerContextFactory(config)
- tls_client_options_factory = context_factory.ClientTLSOptionsFactory(config)
-
- ps = UserDirectoryServer(
+ ss = UserDirectoryServer(
config.server_name,
db_config=config.database_config,
- tls_server_context_factory=tls_server_context_factory,
- tls_client_options_factory=tls_client_options_factory,
config=config,
version_string="Synapse/" + get_version_string(synapse),
database_engine=database_engine,
)
- ps.setup()
- ps.start_listening(config.worker_listeners)
-
- def start():
- ps.get_datastore().start_profiling()
-
- reactor.callWhenRunning(start)
+ ss.setup()
+ reactor.callWhenRunning(_base.start, ss, config.worker_listeners)
_base.start_worker_reactor("synapse-user-dir", config)
|