Merge branch 'develop' of github.com:matrix-org/synapse into matrix-org-hotfixes
commit
75c924430e
|
@ -199,6 +199,8 @@ by installing the ``libjemalloc1`` package and adding this line to
|
||||||
|
|
||||||
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.1
|
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.1
|
||||||
|
|
||||||
|
This can make a significant difference on Python 2.7 - it's unclear how
|
||||||
|
much of an improvement it provides on Python 3.x.
|
||||||
|
|
||||||
Upgrading an existing Synapse
|
Upgrading an existing Synapse
|
||||||
=============================
|
=============================
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Cleanup request exception logging
|
Cleanup request exception logging.
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fix ACME config for python 2.
|
|
@ -0,0 +1 @@
|
||||||
|
Fix paginating over federation persisting incorrect state.
|
|
@ -0,0 +1 @@
|
||||||
|
Return correct error code when inviting a remote user to a room whose homeserver does not support the room version.
|
|
@ -0,0 +1 @@
|
||||||
|
Don't log exceptions when failing to fetch remote server keys
|
|
@ -0,0 +1 @@
|
||||||
|
Correctly proxy exception in frontend_proxy worker
|
|
@ -0,0 +1 @@
|
||||||
|
Cleanup request exception logging.
|
|
@ -0,0 +1 @@
|
||||||
|
Cleanup request exception logging.
|
|
@ -21,7 +21,7 @@ from twisted.web.resource import NoResource
|
||||||
|
|
||||||
import synapse
|
import synapse
|
||||||
from synapse import events
|
from synapse import events
|
||||||
from synapse.api.errors import SynapseError
|
from synapse.api.errors import HttpResponseException, SynapseError
|
||||||
from synapse.app import _base
|
from synapse.app import _base
|
||||||
from synapse.config._base import ConfigError
|
from synapse.config._base import ConfigError
|
||||||
from synapse.config.homeserver import HomeServerConfig
|
from synapse.config.homeserver import HomeServerConfig
|
||||||
|
@ -66,10 +66,15 @@ class PresenceStatusStubServlet(ClientV1RestServlet):
|
||||||
headers = {
|
headers = {
|
||||||
"Authorization": auth_headers,
|
"Authorization": auth_headers,
|
||||||
}
|
}
|
||||||
result = yield self.http_client.get_json(
|
|
||||||
self.main_uri + request.uri.decode('ascii'),
|
try:
|
||||||
headers=headers,
|
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.returnValue((200, result))
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
|
|
|
@ -47,5 +47,5 @@ class CaptchaConfig(Config):
|
||||||
#captcha_bypass_secret: "YOUR_SECRET_HERE"
|
#captcha_bypass_secret: "YOUR_SECRET_HERE"
|
||||||
|
|
||||||
# The API endpoint to use for verifying m.login.recaptcha responses.
|
# The API endpoint to use for verifying m.login.recaptcha responses.
|
||||||
recaptcha_siteverify_api: "https://www.google.com/recaptcha/api/siteverify"
|
recaptcha_siteverify_api: "https://www.recaptcha.net/recaptcha/api/siteverify"
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -19,6 +19,8 @@ import warnings
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
from unpaddedbase64 import encode_base64
|
from unpaddedbase64 import encode_base64
|
||||||
|
|
||||||
from OpenSSL import crypto
|
from OpenSSL import crypto
|
||||||
|
@ -36,9 +38,11 @@ class TlsConfig(Config):
|
||||||
acme_config = {}
|
acme_config = {}
|
||||||
|
|
||||||
self.acme_enabled = acme_config.get("enabled", False)
|
self.acme_enabled = acme_config.get("enabled", False)
|
||||||
self.acme_url = acme_config.get(
|
|
||||||
|
# hyperlink complains on py2 if this is not a Unicode
|
||||||
|
self.acme_url = six.text_type(acme_config.get(
|
||||||
"url", u"https://acme-v01.api.letsencrypt.org/directory"
|
"url", u"https://acme-v01.api.letsencrypt.org/directory"
|
||||||
)
|
))
|
||||||
self.acme_port = acme_config.get("port", 80)
|
self.acme_port = acme_config.get("port", 80)
|
||||||
self.acme_bind_addresses = acme_config.get("bind_addresses", ['::', '0.0.0.0'])
|
self.acme_bind_addresses = acme_config.get("bind_addresses", ['::', '0.0.0.0'])
|
||||||
self.acme_reprovision_threshold = acme_config.get("reprovision_threshold", 30)
|
self.acme_reprovision_threshold = acme_config.get("reprovision_threshold", 30)
|
||||||
|
@ -55,7 +59,7 @@ class TlsConfig(Config):
|
||||||
)
|
)
|
||||||
if not self.tls_private_key_file:
|
if not self.tls_private_key_file:
|
||||||
raise ConfigError(
|
raise ConfigError(
|
||||||
"tls_certificate_path must be specified if TLS-enabled listeners are "
|
"tls_private_key_path must be specified if TLS-enabled listeners are "
|
||||||
"configured."
|
"configured."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
import logging
|
import logging
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
|
from six import raise_from
|
||||||
from six.moves import urllib
|
from six.moves import urllib
|
||||||
|
|
||||||
from signedjson.key import (
|
from signedjson.key import (
|
||||||
|
@ -35,7 +36,12 @@ from unpaddedbase64 import decode_base64
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
from synapse.api.errors import Codes, RequestSendFailed, SynapseError
|
from synapse.api.errors import (
|
||||||
|
Codes,
|
||||||
|
HttpResponseException,
|
||||||
|
RequestSendFailed,
|
||||||
|
SynapseError,
|
||||||
|
)
|
||||||
from synapse.util import logcontext, unwrapFirstError
|
from synapse.util import logcontext, unwrapFirstError
|
||||||
from synapse.util.logcontext import (
|
from synapse.util.logcontext import (
|
||||||
LoggingContext,
|
LoggingContext,
|
||||||
|
@ -44,6 +50,7 @@ from synapse.util.logcontext import (
|
||||||
run_in_background,
|
run_in_background,
|
||||||
)
|
)
|
||||||
from synapse.util.metrics import Measure
|
from synapse.util.metrics import Measure
|
||||||
|
from synapse.util.retryutils import NotRetryingDestination
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -367,13 +374,18 @@ class Keyring(object):
|
||||||
server_name_and_key_ids, perspective_name, perspective_keys
|
server_name_and_key_ids, perspective_name, perspective_keys
|
||||||
)
|
)
|
||||||
defer.returnValue(result)
|
defer.returnValue(result)
|
||||||
|
except KeyLookupError as e:
|
||||||
|
logger.warning(
|
||||||
|
"Key lookup failed from %r: %s", perspective_name, e,
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(
|
logger.exception(
|
||||||
"Unable to get key from %r: %s %s",
|
"Unable to get key from %r: %s %s",
|
||||||
perspective_name,
|
perspective_name,
|
||||||
type(e).__name__, str(e),
|
type(e).__name__, str(e),
|
||||||
)
|
)
|
||||||
defer.returnValue({})
|
|
||||||
|
defer.returnValue({})
|
||||||
|
|
||||||
results = yield logcontext.make_deferred_yieldable(defer.gatherResults(
|
results = yield logcontext.make_deferred_yieldable(defer.gatherResults(
|
||||||
[
|
[
|
||||||
|
@ -421,21 +433,30 @@ class Keyring(object):
|
||||||
# TODO(mark): Set the minimum_valid_until_ts to that needed by
|
# TODO(mark): Set the minimum_valid_until_ts to that needed by
|
||||||
# the events being validated or the current time if validating
|
# the events being validated or the current time if validating
|
||||||
# an incoming request.
|
# an incoming request.
|
||||||
query_response = yield self.client.post_json(
|
try:
|
||||||
destination=perspective_name,
|
query_response = yield self.client.post_json(
|
||||||
path="/_matrix/key/v2/query",
|
destination=perspective_name,
|
||||||
data={
|
path="/_matrix/key/v2/query",
|
||||||
u"server_keys": {
|
data={
|
||||||
server_name: {
|
u"server_keys": {
|
||||||
key_id: {
|
server_name: {
|
||||||
u"minimum_valid_until_ts": 0
|
key_id: {
|
||||||
} for key_id in key_ids
|
u"minimum_valid_until_ts": 0
|
||||||
|
} for key_id in key_ids
|
||||||
|
}
|
||||||
|
for server_name, key_ids in server_names_and_key_ids
|
||||||
}
|
}
|
||||||
for server_name, key_ids in server_names_and_key_ids
|
},
|
||||||
}
|
long_retries=True,
|
||||||
},
|
)
|
||||||
long_retries=True,
|
except (NotRetryingDestination, RequestSendFailed) as e:
|
||||||
)
|
raise_from(
|
||||||
|
KeyLookupError("Failed to connect to remote server"), e,
|
||||||
|
)
|
||||||
|
except HttpResponseException as e:
|
||||||
|
raise_from(
|
||||||
|
KeyLookupError("Remote server returned an error"), e,
|
||||||
|
)
|
||||||
|
|
||||||
keys = {}
|
keys = {}
|
||||||
|
|
||||||
|
@ -502,11 +523,20 @@ class Keyring(object):
|
||||||
if requested_key_id in keys:
|
if requested_key_id in keys:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
response = yield self.client.get_json(
|
try:
|
||||||
destination=server_name,
|
response = yield self.client.get_json(
|
||||||
path="/_matrix/key/v2/server/" + urllib.parse.quote(requested_key_id),
|
destination=server_name,
|
||||||
ignore_backoff=True,
|
path="/_matrix/key/v2/server/" + urllib.parse.quote(requested_key_id),
|
||||||
)
|
ignore_backoff=True,
|
||||||
|
)
|
||||||
|
except (NotRetryingDestination, RequestSendFailed) as e:
|
||||||
|
raise_from(
|
||||||
|
KeyLookupError("Failed to connect to remote server"), e,
|
||||||
|
)
|
||||||
|
except HttpResponseException as e:
|
||||||
|
raise_from(
|
||||||
|
KeyLookupError("Remote server returned an error"), e,
|
||||||
|
)
|
||||||
|
|
||||||
if (u"signatures" not in response
|
if (u"signatures" not in response
|
||||||
or server_name not in response[u"signatures"]):
|
or server_name not in response[u"signatures"]):
|
||||||
|
|
|
@ -33,6 +33,7 @@ from synapse.api.constants import (
|
||||||
)
|
)
|
||||||
from synapse.api.errors import (
|
from synapse.api.errors import (
|
||||||
CodeMessageException,
|
CodeMessageException,
|
||||||
|
Codes,
|
||||||
FederationDeniedError,
|
FederationDeniedError,
|
||||||
HttpResponseException,
|
HttpResponseException,
|
||||||
SynapseError,
|
SynapseError,
|
||||||
|
@ -792,10 +793,25 @@ class FederationClient(FederationBase):
|
||||||
defer.returnValue(content)
|
defer.returnValue(content)
|
||||||
except HttpResponseException as e:
|
except HttpResponseException as e:
|
||||||
if e.code in [400, 404]:
|
if e.code in [400, 404]:
|
||||||
|
err = e.to_synapse_error()
|
||||||
|
|
||||||
|
# If we receive an error response that isn't a generic error, we
|
||||||
|
# assume that the remote understands the v2 invite API and this
|
||||||
|
# is a legitimate error.
|
||||||
|
if err.errcode != Codes.UNKNOWN:
|
||||||
|
raise err
|
||||||
|
|
||||||
|
# Otherwise, we assume that the remote server doesn't understand
|
||||||
|
# the v2 invite API.
|
||||||
|
|
||||||
if room_version in (RoomVersions.V1, RoomVersions.V2):
|
if room_version in (RoomVersions.V1, RoomVersions.V2):
|
||||||
pass # We'll fall through
|
pass # We'll fall through
|
||||||
else:
|
else:
|
||||||
raise Exception("Remote server is too old")
|
raise SynapseError(
|
||||||
|
400,
|
||||||
|
"User's homeserver does not support this room version",
|
||||||
|
Codes.UNSUPPORTED_ROOM_VERSION,
|
||||||
|
)
|
||||||
elif e.code == 403:
|
elif e.code == 403:
|
||||||
raise e.to_synapse_error()
|
raise e.to_synapse_error()
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -25,9 +25,10 @@ from twisted.internet import defer
|
||||||
from twisted.internet.abstract import isIPAddress
|
from twisted.internet.abstract import isIPAddress
|
||||||
from twisted.python import failure
|
from twisted.python import failure
|
||||||
|
|
||||||
from synapse.api.constants import EventTypes, Membership
|
from synapse.api.constants import KNOWN_ROOM_VERSIONS, EventTypes, Membership
|
||||||
from synapse.api.errors import (
|
from synapse.api.errors import (
|
||||||
AuthError,
|
AuthError,
|
||||||
|
Codes,
|
||||||
FederationError,
|
FederationError,
|
||||||
IncompatibleRoomVersionError,
|
IncompatibleRoomVersionError,
|
||||||
NotFoundError,
|
NotFoundError,
|
||||||
|
@ -239,8 +240,9 @@ class FederationServer(FederationBase):
|
||||||
f = failure.Failure()
|
f = failure.Failure()
|
||||||
pdu_results[event_id] = {"error": str(e)}
|
pdu_results[event_id] = {"error": str(e)}
|
||||||
logger.error(
|
logger.error(
|
||||||
"Failed to handle PDU %s: %s",
|
"Failed to handle PDU %s",
|
||||||
event_id, f.getTraceback().rstrip(),
|
event_id,
|
||||||
|
exc_info=(f.type, f.value, f.getTracebackObject()),
|
||||||
)
|
)
|
||||||
|
|
||||||
yield concurrently_execute(
|
yield concurrently_execute(
|
||||||
|
@ -386,6 +388,13 @@ class FederationServer(FederationBase):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_invite_request(self, origin, content, room_version):
|
def on_invite_request(self, origin, content, room_version):
|
||||||
|
if room_version not in KNOWN_ROOM_VERSIONS:
|
||||||
|
raise SynapseError(
|
||||||
|
400,
|
||||||
|
"Homeserver does not support this room version",
|
||||||
|
Codes.UNSUPPORTED_ROOM_VERSION,
|
||||||
|
)
|
||||||
|
|
||||||
format_ver = room_version_to_event_format(room_version)
|
format_ver = room_version_to_event_format(room_version)
|
||||||
|
|
||||||
pdu = event_from_pdu_json(content, format_ver)
|
pdu = event_from_pdu_json(content, format_ver)
|
||||||
|
|
|
@ -770,10 +770,26 @@ class FederationHandler(BaseHandler):
|
||||||
set(auth_events.keys()) | set(state_events.keys())
|
set(auth_events.keys()) | set(state_events.keys())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# We now have a chunk of events plus associated state and auth chain to
|
||||||
|
# persist. We do the persistence in two steps:
|
||||||
|
# 1. Auth events and state get persisted as outliers, plus the
|
||||||
|
# backward extremities get persisted (as non-outliers).
|
||||||
|
# 2. The rest of the events in the chunk get persisted one by one, as
|
||||||
|
# each one depends on the previous event for its state.
|
||||||
|
#
|
||||||
|
# The important thing is that events in the chunk get persisted as
|
||||||
|
# non-outliers, including when those events are also in the state or
|
||||||
|
# auth chain. Caution must therefore be taken to ensure that they are
|
||||||
|
# not accidentally marked as outliers.
|
||||||
|
|
||||||
|
# Step 1a: persist auth events that *don't* appear in the chunk
|
||||||
ev_infos = []
|
ev_infos = []
|
||||||
for a in auth_events.values():
|
for a in auth_events.values():
|
||||||
if a.event_id in seen_events:
|
# We only want to persist auth events as outliers that we haven't
|
||||||
|
# seen and aren't about to persist as part of the backfilled chunk.
|
||||||
|
if a.event_id in seen_events or a.event_id in event_map:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
a.internal_metadata.outlier = True
|
a.internal_metadata.outlier = True
|
||||||
ev_infos.append({
|
ev_infos.append({
|
||||||
"event": a,
|
"event": a,
|
||||||
|
@ -785,14 +801,21 @@ class FederationHandler(BaseHandler):
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# Step 1b: persist the events in the chunk we fetched state for (i.e.
|
||||||
|
# the backwards extremities) as non-outliers.
|
||||||
for e_id in events_to_state:
|
for e_id in events_to_state:
|
||||||
|
# For paranoia we ensure that these events are marked as
|
||||||
|
# non-outliers
|
||||||
|
ev = event_map[e_id]
|
||||||
|
assert(not ev.internal_metadata.is_outlier())
|
||||||
|
|
||||||
ev_infos.append({
|
ev_infos.append({
|
||||||
"event": event_map[e_id],
|
"event": ev,
|
||||||
"state": events_to_state[e_id],
|
"state": events_to_state[e_id],
|
||||||
"auth_events": {
|
"auth_events": {
|
||||||
(auth_events[a_id].type, auth_events[a_id].state_key):
|
(auth_events[a_id].type, auth_events[a_id].state_key):
|
||||||
auth_events[a_id]
|
auth_events[a_id]
|
||||||
for a_id in event_map[e_id].auth_event_ids()
|
for a_id in ev.auth_event_ids()
|
||||||
if a_id in auth_events
|
if a_id in auth_events
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -802,12 +825,17 @@ class FederationHandler(BaseHandler):
|
||||||
backfilled=True,
|
backfilled=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Step 2: Persist the rest of the events in the chunk one by one
|
||||||
events.sort(key=lambda e: e.depth)
|
events.sort(key=lambda e: e.depth)
|
||||||
|
|
||||||
for event in events:
|
for event in events:
|
||||||
if event in events_to_state:
|
if event in events_to_state:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# For paranoia we ensure that these events are marked as
|
||||||
|
# non-outliers
|
||||||
|
assert(not event.internal_metadata.is_outlier())
|
||||||
|
|
||||||
# We store these one at a time since each event depends on the
|
# We store these one at a time since each event depends on the
|
||||||
# previous to work out the state.
|
# previous to work out the state.
|
||||||
# TODO: We can probably do something more clever here.
|
# TODO: We can probably do something more clever here.
|
||||||
|
|
|
@ -136,7 +136,11 @@ class PaginationHandler(object):
|
||||||
logger.info("[purge] complete")
|
logger.info("[purge] complete")
|
||||||
self._purges_by_id[purge_id].status = PurgeStatus.STATUS_COMPLETE
|
self._purges_by_id[purge_id].status = PurgeStatus.STATUS_COMPLETE
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error("[purge] failed: %s", Failure().getTraceback().rstrip())
|
f = Failure()
|
||||||
|
logger.error(
|
||||||
|
"[purge] failed",
|
||||||
|
exc_info=(f.type, f.value, f.getTracebackObject()),
|
||||||
|
)
|
||||||
self._purges_by_id[purge_id].status = PurgeStatus.STATUS_FAILED
|
self._purges_by_id[purge_id].status = PurgeStatus.STATUS_FAILED
|
||||||
finally:
|
finally:
|
||||||
self._purges_in_progress_by_room.discard(room_id)
|
self._purges_in_progress_by_room.discard(room_id)
|
||||||
|
|
|
@ -460,7 +460,7 @@ class RegistrationHandler(BaseHandler):
|
||||||
lines = response.split('\n')
|
lines = response.split('\n')
|
||||||
json = {
|
json = {
|
||||||
"valid": lines[0] == 'true',
|
"valid": lines[0] == 'true',
|
||||||
"error_url": "http://www.google.com/recaptcha/api/challenge?" +
|
"error_url": "http://www.recaptcha.net/recaptcha/api/challenge?" +
|
||||||
"error=%s" % lines[1]
|
"error=%s" % lines[1]
|
||||||
}
|
}
|
||||||
defer.returnValue(json)
|
defer.returnValue(json)
|
||||||
|
@ -471,7 +471,7 @@ class RegistrationHandler(BaseHandler):
|
||||||
Used only by c/s api v1
|
Used only by c/s api v1
|
||||||
"""
|
"""
|
||||||
data = yield self.captcha_client.post_urlencoded_get_raw(
|
data = yield self.captcha_client.post_urlencoded_get_raw(
|
||||||
"http://www.google.com:80/recaptcha/api/verify",
|
"http://www.recaptcha.net:80/recaptcha/api/verify",
|
||||||
args={
|
args={
|
||||||
'privatekey': private_key,
|
'privatekey': private_key,
|
||||||
'remoteip': ip_addr,
|
'remoteip': ip_addr,
|
||||||
|
|
|
@ -169,18 +169,18 @@ def _return_html_error(f, request):
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.error(
|
logger.error(
|
||||||
"Failed handle request %r: %s",
|
"Failed handle request %r",
|
||||||
request,
|
request,
|
||||||
f.getTraceback().rstrip(),
|
exc_info=(f.type, f.value, f.getTracebackObject()),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
code = http_client.INTERNAL_SERVER_ERROR
|
code = http_client.INTERNAL_SERVER_ERROR
|
||||||
msg = "Internal server error"
|
msg = "Internal server error"
|
||||||
|
|
||||||
logger.error(
|
logger.error(
|
||||||
"Failed handle request %r: %s",
|
"Failed handle request %r",
|
||||||
request,
|
request,
|
||||||
f.getTraceback().rstrip(),
|
exc_info=(f.type, f.value, f.getTracebackObject()),
|
||||||
)
|
)
|
||||||
|
|
||||||
body = HTML_ERROR_TEMPLATE.format(
|
body = HTML_ERROR_TEMPLATE.format(
|
||||||
|
|
|
@ -33,7 +33,7 @@ RECAPTCHA_TEMPLATE = """
|
||||||
<title>Authentication</title>
|
<title>Authentication</title>
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1,
|
<meta name='viewport' content='width=device-width, initial-scale=1,
|
||||||
user-scalable=no, minimum-scale=1.0, maximum-scale=1.0'>
|
user-scalable=no, minimum-scale=1.0, maximum-scale=1.0'>
|
||||||
<script src="https://www.google.com/recaptcha/api.js"
|
<script src="https://www.recaptcha.net/recaptcha/api.js"
|
||||||
async defer></script>
|
async defer></script>
|
||||||
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
|
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
|
||||||
<link rel="stylesheet" href="/_matrix/static/client/register/style.css">
|
<link rel="stylesheet" href="/_matrix/static/client/register/style.css">
|
||||||
|
|
|
@ -133,8 +133,15 @@ def respond_with_responder(request, responder, media_type, file_size, upload_nam
|
||||||
|
|
||||||
logger.debug("Responding to media request with responder %s")
|
logger.debug("Responding to media request with responder %s")
|
||||||
add_file_headers(request, media_type, file_size, upload_name)
|
add_file_headers(request, media_type, file_size, upload_name)
|
||||||
with responder:
|
try:
|
||||||
yield responder.write_to_consumer(request)
|
with responder:
|
||||||
|
yield responder.write_to_consumer(request)
|
||||||
|
except Exception as e:
|
||||||
|
# The majority of the time this will be due to the client having gone
|
||||||
|
# away. Unfortunately, Twisted simply throws a generic exception at us
|
||||||
|
# in that case.
|
||||||
|
logger.warning("Failed to write to consumer: %s %s", type(e), e)
|
||||||
|
|
||||||
finish_request(request)
|
finish_request(request)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0'>
|
<meta name='viewport' content='width=device-width, initial-scale=1, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0'>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
<script src="js/jquery-2.1.3.min.js"></script>
|
<script src="js/jquery-2.1.3.min.js"></script>
|
||||||
<script src="https://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script>
|
<script src="https://www.recaptcha.net/recaptcha/api/js/recaptcha_ajax.js"></script>
|
||||||
<script src="register_config.js"></script>
|
<script src="register_config.js"></script>
|
||||||
<script src="js/register.js"></script>
|
<script src="js/register.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
Loading…
Reference in New Issue