Apply sanity to the transport client interface. Convert 'make_join' and 'send_join' to accept iterables of destinations

This commit is contained in:
Erik Johnston 2015-02-04 16:28:12 +00:00
parent 95e2d2d36d
commit ae46f10fc5
6 changed files with 151 additions and 92 deletions

View File

@ -39,7 +39,7 @@ class Codes(object):
TOO_LARGE = "M_TOO_LARGE" TOO_LARGE = "M_TOO_LARGE"
class CodeMessageException(Exception): class CodeMessageException(RuntimeError):
"""An exception with integer code and message string attributes.""" """An exception with integer code and message string attributes."""
def __init__(self, code, msg): def __init__(self, code, msg):
@ -227,3 +227,9 @@ class FederationError(RuntimeError):
"affected": self.affected, "affected": self.affected,
"source": self.source if self.source else self.affected, "source": self.source if self.source else self.affected,
} }
class HttpResponseException(CodeMessageException):
def __init__(self, code, msg, response):
self.response = response
super(HttpResponseException, self).__init__(code, msg)

View File

@ -19,6 +19,7 @@ from twisted.internet import defer
from .federation_base import FederationBase from .federation_base import FederationBase
from .units import Edu from .units import Edu
from synapse.api.errors import CodeMessageException
from synapse.util.logutils import log_function from synapse.util.logutils import log_function
from synapse.events import FrozenEvent from synapse.events import FrozenEvent
@ -180,7 +181,8 @@ class FederationClient(FederationBase):
pdu = yield self._check_sigs_and_hash(pdu) pdu = yield self._check_sigs_and_hash(pdu)
break break
except CodeMessageException:
raise
except Exception as e: except Exception as e:
logger.info( logger.info(
"Failed to get PDU %s from %s because %s", "Failed to get PDU %s from %s because %s",
@ -264,45 +266,63 @@ class FederationClient(FederationBase):
defer.returnValue(self.event_from_pdu_json(pdu_dict)) defer.returnValue(self.event_from_pdu_json(pdu_dict))
break break
except Exception as e: except CodeMessageException:
logger.warn("Failed to make_join via %s", destination) raise
except RuntimeError as e:
logger.warn(
"Failed to make_join via %s: %s",
destination, e.message
)
raise RuntimeError("Failed to send to any server.")
@defer.inlineCallbacks @defer.inlineCallbacks
def send_join(self, destination, pdu): def send_join(self, destinations, pdu):
time_now = self._clock.time_msec() for destination in destinations:
_, content = yield self.transport_layer.send_join( try:
destination=destination, time_now = self._clock.time_msec()
room_id=pdu.room_id, _, content = yield self.transport_layer.send_join(
event_id=pdu.event_id, destination=destination,
content=pdu.get_pdu_json(time_now), room_id=pdu.room_id,
) event_id=pdu.event_id,
content=pdu.get_pdu_json(time_now),
)
logger.debug("Got content: %s", content) logger.debug("Got content: %s", content)
state = [ state = [
self.event_from_pdu_json(p, outlier=True) self.event_from_pdu_json(p, outlier=True)
for p in content.get("state", []) for p in content.get("state", [])
] ]
auth_chain = [ auth_chain = [
self.event_from_pdu_json(p, outlier=True) self.event_from_pdu_json(p, outlier=True)
for p in content.get("auth_chain", []) for p in content.get("auth_chain", [])
] ]
signed_state = yield self._check_sigs_and_hash_and_fetch( signed_state = yield self._check_sigs_and_hash_and_fetch(
destination, state, outlier=True destination, state, outlier=True
) )
signed_auth = yield self._check_sigs_and_hash_and_fetch( signed_auth = yield self._check_sigs_and_hash_and_fetch(
destination, auth_chain, outlier=True destination, auth_chain, outlier=True
) )
auth_chain.sort(key=lambda e: e.depth) auth_chain.sort(key=lambda e: e.depth)
defer.returnValue({ defer.returnValue({
"state": signed_state, "state": signed_state,
"auth_chain": signed_auth, "auth_chain": signed_auth,
}) })
except CodeMessageException:
raise
except RuntimeError as e:
logger.warn(
"Failed to send_join via %s: %s",
destination, e.message
)
raise RuntimeError("Failed to send to any server.")
@defer.inlineCallbacks @defer.inlineCallbacks
def send_invite(self, destination, room_id, event_id, pdu): def send_invite(self, destination, room_id, event_id, pdu):

View File

@ -19,6 +19,7 @@ from twisted.internet import defer
from .persistence import TransactionActions from .persistence import TransactionActions
from .units import Transaction from .units import Transaction
from synapse.api.errors import HttpResponseException
from synapse.util.logutils import log_function from synapse.util.logutils import log_function
from synapse.util.logcontext import PreserveLoggingContext from synapse.util.logcontext import PreserveLoggingContext
@ -238,9 +239,14 @@ class TransactionQueue(object):
del p["age_ts"] del p["age_ts"]
return data return data
code, response = yield self.transport_layer.send_transaction( try:
transaction, json_data_cb response = yield self.transport_layer.send_transaction(
) transaction, json_data_cb
)
code = 200
except HttpResponseException as e:
code = e.code
response = e.response
logger.info("TX [%s] got %d response", destination, code) logger.info("TX [%s] got %d response", destination, code)
@ -274,8 +280,7 @@ class TransactionQueue(object):
pass pass
logger.debug("TX [%s] Yielded to callbacks", destination) logger.debug("TX [%s] Yielded to callbacks", destination)
except RuntimeError as e:
except Exception as e:
# We capture this here as there as nothing actually listens # We capture this here as there as nothing actually listens
# for this finishing functions deferred. # for this finishing functions deferred.
logger.warn( logger.warn(
@ -283,6 +288,14 @@ class TransactionQueue(object):
destination, destination,
e, e,
) )
except Exception as e:
# We capture this here as there as nothing actually listens
# for this finishing functions deferred.
logger.exception(
"TX [%s] Problem in _attempt_transaction: %s",
destination,
e,
)
self.set_retrying(destination, retry_interval) self.set_retrying(destination, retry_interval)

View File

@ -19,7 +19,6 @@ from synapse.api.urls import FEDERATION_PREFIX as PREFIX
from synapse.util.logutils import log_function from synapse.util.logutils import log_function
import logging import logging
import json
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -129,7 +128,7 @@ class TransportLayerClient(object):
# generated by the json_data_callback. # generated by the json_data_callback.
json_data = transaction.get_dict() json_data = transaction.get_dict()
code, response = yield self.client.put_json( response = yield self.client.put_json(
transaction.destination, transaction.destination,
path=PREFIX + "/send/%s/" % transaction.transaction_id, path=PREFIX + "/send/%s/" % transaction.transaction_id,
data=json_data, data=json_data,
@ -137,95 +136,86 @@ class TransportLayerClient(object):
) )
logger.debug( logger.debug(
"send_data dest=%s, txid=%s, got response: %d", "send_data dest=%s, txid=%s, got response: 200",
transaction.destination, transaction.transaction_id, code transaction.destination, transaction.transaction_id,
) )
defer.returnValue((code, response)) defer.returnValue(response)
@defer.inlineCallbacks @defer.inlineCallbacks
@log_function @log_function
def make_query(self, destination, query_type, args, retry_on_dns_fail): def make_query(self, destination, query_type, args, retry_on_dns_fail):
path = PREFIX + "/query/%s" % query_type path = PREFIX + "/query/%s" % query_type
response = yield self.client.get_json( content = yield self.client.get_json(
destination=destination, destination=destination,
path=path, path=path,
args=args, args=args,
retry_on_dns_fail=retry_on_dns_fail, retry_on_dns_fail=retry_on_dns_fail,
) )
defer.returnValue(response) defer.returnValue(content)
@defer.inlineCallbacks @defer.inlineCallbacks
@log_function @log_function
def make_join(self, destination, room_id, user_id, retry_on_dns_fail=True): def make_join(self, destination, room_id, user_id, retry_on_dns_fail=True):
path = PREFIX + "/make_join/%s/%s" % (room_id, user_id) path = PREFIX + "/make_join/%s/%s" % (room_id, user_id)
response = yield self.client.get_json( content = yield self.client.get_json(
destination=destination, destination=destination,
path=path, path=path,
retry_on_dns_fail=retry_on_dns_fail, retry_on_dns_fail=retry_on_dns_fail,
) )
defer.returnValue(response) defer.returnValue(content)
@defer.inlineCallbacks @defer.inlineCallbacks
@log_function @log_function
def send_join(self, destination, room_id, event_id, content): def send_join(self, destination, room_id, event_id, content):
path = PREFIX + "/send_join/%s/%s" % (room_id, event_id) path = PREFIX + "/send_join/%s/%s" % (room_id, event_id)
code, content = yield self.client.put_json( response = yield self.client.put_json(
destination=destination, destination=destination,
path=path, path=path,
data=content, data=content,
) )
if not 200 <= code < 300:
raise RuntimeError("Got %d from send_join", code)
defer.returnValue(json.loads(content))
@defer.inlineCallbacks
@log_function
def send_invite(self, destination, room_id, event_id, content):
path = PREFIX + "/invite/%s/%s" % (room_id, event_id)
code, content = yield self.client.put_json(
destination=destination,
path=path,
data=content,
)
if not 200 <= code < 300:
raise RuntimeError("Got %d from send_invite", code)
defer.returnValue(json.loads(content))
@defer.inlineCallbacks
@log_function
def get_event_auth(self, destination, room_id, event_id):
path = PREFIX + "/event_auth/%s/%s" % (room_id, event_id)
response = yield self.client.get_json(
destination=destination,
path=path,
)
defer.returnValue(response) defer.returnValue(response)
@defer.inlineCallbacks @defer.inlineCallbacks
@log_function @log_function
def send_query_auth(self, destination, room_id, event_id, content): def send_invite(self, destination, room_id, event_id, content):
path = PREFIX + "/query_auth/%s/%s" % (room_id, event_id) path = PREFIX + "/invite/%s/%s" % (room_id, event_id)
code, content = yield self.client.post_json( response = yield self.client.put_json(
destination=destination, destination=destination,
path=path, path=path,
data=content, data=content,
) )
if not 200 <= code < 300: defer.returnValue(response)
raise RuntimeError("Got %d from send_invite", code)
defer.returnValue(json.loads(content)) @defer.inlineCallbacks
@log_function
def get_event_auth(self, destination, room_id, event_id):
path = PREFIX + "/event_auth/%s/%s" % (room_id, event_id)
content = yield self.client.get_json(
destination=destination,
path=path,
)
defer.returnValue(content)
@defer.inlineCallbacks
@log_function
def send_query_auth(self, destination, room_id, event_id, content):
path = PREFIX + "/query_auth/%s/%s" % (room_id, event_id)
content = yield self.client.post_json(
destination=destination,
path=path,
data=content,
)
defer.returnValue(content)

View File

@ -288,7 +288,7 @@ class FederationHandler(BaseHandler):
logger.debug("Joining %s to %s", joinee, room_id) logger.debug("Joining %s to %s", joinee, room_id)
pdu = yield self.replication_layer.make_join( pdu = yield self.replication_layer.make_join(
target_host, [target_host],
room_id, room_id,
joinee joinee
) )
@ -331,7 +331,7 @@ class FederationHandler(BaseHandler):
new_event = builder.build() new_event = builder.build()
ret = yield self.replication_layer.send_join( ret = yield self.replication_layer.send_join(
target_host, [target_host],
new_event new_event
) )

View File

@ -27,7 +27,9 @@ from synapse.util.logcontext import PreserveLoggingContext
from syutil.jsonutil import encode_canonical_json from syutil.jsonutil import encode_canonical_json
from synapse.api.errors import CodeMessageException, SynapseError, Codes from synapse.api.errors import (
SynapseError, Codes, HttpResponseException,
)
from syutil.crypto.jsonsign import sign_json from syutil.crypto.jsonsign import sign_json
@ -163,13 +165,12 @@ class MatrixFederationHttpClient(object):
) )
if 200 <= response.code < 300: if 200 <= response.code < 300:
# We need to update the transactions table to say it was sent?
pass pass
else: else:
# :'( # :'(
# Update transactions table? # Update transactions table?
raise CodeMessageException( raise HttpResponseException(
response.code, response.phrase response.code, response.phrase, response
) )
defer.returnValue(response) defer.returnValue(response)
@ -238,11 +239,20 @@ class MatrixFederationHttpClient(object):
headers_dict={"Content-Type": ["application/json"]}, headers_dict={"Content-Type": ["application/json"]},
) )
if 200 <= response.code < 300:
# We need to update the transactions table to say it was sent?
c_type = response.headers.getRawHeaders("Content-Type")
if "application/json" not in c_type:
raise RuntimeError(
"Content-Type not application/json"
)
logger.debug("Getting resp body") logger.debug("Getting resp body")
body = yield readBody(response) body = yield readBody(response)
logger.debug("Got resp body") logger.debug("Got resp body")
defer.returnValue((response.code, body)) defer.returnValue(json.loads(body))
@defer.inlineCallbacks @defer.inlineCallbacks
def post_json(self, destination, path, data={}): def post_json(self, destination, path, data={}):
@ -275,11 +285,20 @@ class MatrixFederationHttpClient(object):
headers_dict={"Content-Type": ["application/json"]}, headers_dict={"Content-Type": ["application/json"]},
) )
if 200 <= response.code < 300:
# We need to update the transactions table to say it was sent?
c_type = response.headers.getRawHeaders("Content-Type")
if "application/json" not in c_type:
raise RuntimeError(
"Content-Type not application/json"
)
logger.debug("Getting resp body") logger.debug("Getting resp body")
body = yield readBody(response) body = yield readBody(response)
logger.debug("Got resp body") logger.debug("Got resp body")
defer.returnValue((response.code, body)) defer.returnValue(json.loads(body))
@defer.inlineCallbacks @defer.inlineCallbacks
def get_json(self, destination, path, args={}, retry_on_dns_fail=True): def get_json(self, destination, path, args={}, retry_on_dns_fail=True):
@ -321,7 +340,18 @@ class MatrixFederationHttpClient(object):
retry_on_dns_fail=retry_on_dns_fail retry_on_dns_fail=retry_on_dns_fail
) )
if 200 <= response.code < 300:
# We need to update the transactions table to say it was sent?
c_type = response.headers.getRawHeaders("Content-Type")
if "application/json" not in c_type:
raise RuntimeError(
"Content-Type not application/json"
)
logger.debug("Getting resp body")
body = yield readBody(response) body = yield readBody(response)
logger.debug("Got resp body")
defer.returnValue(json.loads(body)) defer.returnValue(json.loads(body))