Skip to content

Commit

Permalink
Merge pull request matrix-org#3639 from matrix-org/rav/refactor_error…
Browse files Browse the repository at this point in the history
…_handling

Clean up handling of errors from outbound requests
  • Loading branch information
richvdh authored Aug 2, 2018
2 parents bdae8f2 + 908be65 commit 1fa9849
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 142 deletions.
1 change: 1 addition & 0 deletions changelog.d/3639.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
When we fail to join a room over federation, pass the error code back to the client.
106 changes: 53 additions & 53 deletions synapse/api/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,6 @@ def __init__(self, code, msg):
self.code = code
self.msg = msg

def error_dict(self):
return cs_error(self.msg)


class MatrixCodeMessageException(CodeMessageException):
"""An error from a general matrix endpoint, eg. from a proxied Matrix API call.
Attributes:
errcode (str): Matrix error code e.g 'M_FORBIDDEN'
"""
def __init__(self, code, msg, errcode=Codes.UNKNOWN):
super(MatrixCodeMessageException, self).__init__(code, msg)
self.errcode = errcode


class SynapseError(CodeMessageException):
"""A base exception type for matrix errors which have an errcode and error
Expand All @@ -109,38 +95,28 @@ def error_dict(self):
self.errcode,
)

@classmethod
def from_http_response_exception(cls, err):
"""Make a SynapseError based on an HTTPResponseException
This is useful when a proxied request has failed, and we need to
decide how to map the failure onto a matrix error to send back to the
client.
An attempt is made to parse the body of the http response as a matrix
error. If that succeeds, the errcode and error message from the body
are used as the errcode and error message in the new synapse error.
Otherwise, the errcode is set to M_UNKNOWN, and the error message is
set to the reason code from the HTTP response.

Args:
err (HttpResponseException):
class ProxiedRequestError(SynapseError):
"""An error from a general matrix endpoint, eg. from a proxied Matrix API call.
Returns:
SynapseError:
"""
# try to parse the body as json, to get better errcode/msg, but
# default to M_UNKNOWN with the HTTP status as the error text
try:
j = json.loads(err.response)
except ValueError:
j = {}
errcode = j.get('errcode', Codes.UNKNOWN)
errmsg = j.get('error', err.msg)
Attributes:
errcode (str): Matrix error code e.g 'M_FORBIDDEN'
"""
def __init__(self, code, msg, errcode=Codes.UNKNOWN, additional_fields=None):
super(ProxiedRequestError, self).__init__(
code, msg, errcode
)
if additional_fields is None:
self._additional_fields = {}
else:
self._additional_fields = dict(additional_fields)

res = SynapseError(err.code, errmsg, errcode)
return res
def error_dict(self):
return cs_error(
self.msg,
self.errcode,
**self._additional_fields
)


class ConsentNotGivenError(SynapseError):
Expand Down Expand Up @@ -309,14 +285,6 @@ def error_dict(self):
)


def cs_exception(exception):
if isinstance(exception, CodeMessageException):
return exception.error_dict()
else:
logger.error("Unknown exception type: %s", type(exception))
return {}


def cs_error(msg, code=Codes.UNKNOWN, **kwargs):
""" Utility method for constructing an error response for client-server
interactions.
Expand Down Expand Up @@ -373,15 +341,47 @@ class HttpResponseException(CodeMessageException):
Represents an HTTP-level failure of an outbound request
Attributes:
response (str): body of response
response (bytes): body of response
"""
def __init__(self, code, msg, response):
"""
Args:
code (int): HTTP status code
msg (str): reason phrase from HTTP response status line
response (str): body of response
response (bytes): body of response
"""
super(HttpResponseException, self).__init__(code, msg)
self.response = response

def to_synapse_error(self):
"""Make a SynapseError based on an HTTPResponseException
This is useful when a proxied request has failed, and we need to
decide how to map the failure onto a matrix error to send back to the
client.
An attempt is made to parse the body of the http response as a matrix
error. If that succeeds, the errcode and error message from the body
are used as the errcode and error message in the new synapse error.
Otherwise, the errcode is set to M_UNKNOWN, and the error message is
set to the reason code from the HTTP response.
Returns:
SynapseError:
"""
# try to parse the body as json, to get better errcode/msg, but
# default to M_UNKNOWN with the HTTP status as the error text
try:
j = json.loads(self.response)
except ValueError:
j = {}

if not isinstance(j, dict):
j = {}

errcode = j.pop('errcode', Codes.UNKNOWN)
errmsg = j.pop('error', self.msg)

return ProxiedRequestError(self.code, errmsg, errcode, j)
29 changes: 17 additions & 12 deletions synapse/federation/federation_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ def _try_destination_list(self, description, destinations, callback):
The [Deferred] result of callback, if it succeeds
Raises:
CodeMessageException if the chosen remote server returns a 300/400 code.
SynapseError if the chosen remote server returns a 300/400 code.
RuntimeError if no servers were reachable.
"""
Expand All @@ -504,9 +504,9 @@ def _try_destination_list(self, description, destinations, callback):
"Failed to %s via %s: %s",
description, destination, e,
)
except CodeMessageException as e:
except HttpResponseException as e:
if not 500 <= e.code < 600:
raise
raise e.to_synapse_error()
else:
logger.warn(
"Failed to %s via %s: %i %s",
Expand Down Expand Up @@ -543,7 +543,7 @@ def make_membership_event(self, destinations, room_id, user_id, membership,
Deferred: resolves to a tuple of (origin (str), event (object))
where origin is the remote homeserver which generated the event.
Fails with a ``CodeMessageException`` if the chosen remote server
Fails with a ``SynapseError`` if the chosen remote server
returns a 300/400 code.
Fails with a ``RuntimeError`` if no servers were reachable.
Expand Down Expand Up @@ -599,7 +599,7 @@ def send_join(self, destinations, pdu):
giving the serer the event was sent to, ``state`` (?) and
``auth_chain``.
Fails with a ``CodeMessageException`` if the chosen remote server
Fails with a ``SynapseError`` if the chosen remote server
returns a 300/400 code.
Fails with a ``RuntimeError`` if no servers were reachable.
Expand Down Expand Up @@ -673,12 +673,17 @@ def send_request(destination):
@defer.inlineCallbacks
def send_invite(self, destination, room_id, event_id, pdu):
time_now = self._clock.time_msec()
code, content = yield self.transport_layer.send_invite(
destination=destination,
room_id=room_id,
event_id=event_id,
content=pdu.get_pdu_json(time_now),
)
try:
code, content = yield self.transport_layer.send_invite(
destination=destination,
room_id=room_id,
event_id=event_id,
content=pdu.get_pdu_json(time_now),
)
except HttpResponseException as e:
if e.code == 403:
raise e.to_synapse_error()
raise

pdu_dict = content["event"]

Expand Down Expand Up @@ -709,7 +714,7 @@ def send_leave(self, destinations, pdu):
Return:
Deferred: resolves to None.
Fails with a ``CodeMessageException`` if the chosen remote server
Fails with a ``SynapseError`` if the chosen remote server
returns a 300/400 code.
Fails with a ``RuntimeError`` if no servers were reachable.
Expand Down
25 changes: 8 additions & 17 deletions synapse/handlers/identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from synapse.api.errors import (
CodeMessageException,
Codes,
MatrixCodeMessageException,
HttpResponseException,
SynapseError,
)

Expand Down Expand Up @@ -85,7 +85,6 @@ def threepid_from_creds(self, creds):
)
defer.returnValue(None)

data = {}
try:
data = yield self.http_client.get_json(
"https://%s%s" % (
Expand All @@ -94,11 +93,9 @@ def threepid_from_creds(self, creds):
),
{'sid': creds['sid'], 'client_secret': client_secret}
)
except MatrixCodeMessageException as e:
except HttpResponseException as e:
logger.info("getValidated3pid failed with Matrix error: %r", e)
raise SynapseError(e.code, e.msg, e.errcode)
except CodeMessageException as e:
data = json.loads(e.msg)
raise e.to_synapse_error()

if 'medium' in data:
defer.returnValue(data)
Expand Down Expand Up @@ -136,7 +133,7 @@ def bind_threepid(self, creds, mxid):
)
logger.debug("bound threepid %r to %s", creds, mxid)
except CodeMessageException as e:
data = json.loads(e.msg)
data = json.loads(e.msg) # XXX WAT?
defer.returnValue(data)

@defer.inlineCallbacks
Expand Down Expand Up @@ -209,12 +206,9 @@ def requestEmailToken(self, id_server, email, client_secret, send_attempt, **kwa
params
)
defer.returnValue(data)
except MatrixCodeMessageException as e:
logger.info("Proxied requestToken failed with Matrix error: %r", e)
raise SynapseError(e.code, e.msg, e.errcode)
except CodeMessageException as e:
except HttpResponseException as e:
logger.info("Proxied requestToken failed: %r", e)
raise e
raise e.to_synapse_error()

@defer.inlineCallbacks
def requestMsisdnToken(
Expand Down Expand Up @@ -244,9 +238,6 @@ def requestMsisdnToken(
params
)
defer.returnValue(data)
except MatrixCodeMessageException as e:
logger.info("Proxied requestToken failed with Matrix error: %r", e)
raise SynapseError(e.code, e.msg, e.errcode)
except CodeMessageException as e:
except HttpResponseException as e:
logger.info("Proxied requestToken failed: %r", e)
raise e
raise e.to_synapse_error()
Loading

0 comments on commit 1fa9849

Please sign in to comment.