From 4f11518934c3e032a763f115a73261414d67f87b Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Thu, 2 Oct 2014 13:57:48 +0100 Subject: Split PlainHttpClient into separate clients for talking to Identity servers and talking to Capatcha servers --- synapse/http/client.py | 290 +++++++++++++++++++++++++------------------------ 1 file changed, 146 insertions(+), 144 deletions(-) (limited to 'synapse/http/client.py') diff --git a/synapse/http/client.py b/synapse/http/client.py index 822afeec1d..e02cce5642 100644 --- a/synapse/http/client.py +++ b/synapse/http/client.py @@ -36,49 +36,6 @@ import urllib logger = logging.getLogger(__name__) -class HttpClient(object): - """ Interface for talking json over http - """ - RETRY_DNS_LOOKUP_FAILURES = "__retry_dns" - - def put_json(self, destination, path, data): - """ Sends the specifed json data using PUT - - Args: - destination (str): The remote server to send the HTTP request - to. - path (str): The HTTP path. - data (dict): A dict containing the data that will be used as - the request body. This will be encoded as JSON. - - Returns: - Deferred: Succeeds when we get a 2xx HTTP response. The result - will be the decoded JSON body. On a 4xx or 5xx error response a - CodeMessageException is raised. - """ - pass - - def get_json(self, destination, path, args=None): - """ Get's some json from the given host homeserver and path - - Args: - destination (str): The remote server to send the HTTP request - to. - path (str): The HTTP path. - args (dict): A dictionary used to create query strings, defaults to - None. - **Note**: The value of each key is assumed to be an iterable - and *not* a string. - - Returns: - Deferred: Succeeds when we get *any* HTTP response. - - The result of the deferred is a tuple of `(code, response)`, - where `response` is a dict representing the decoded JSON body. - """ - pass - - class MatrixHttpAgent(_AgentBase): def __init__(self, reactor, pool=None): @@ -102,23 +59,114 @@ class MatrixHttpAgent(_AgentBase): parsed_URI.originForm) -class TwistedHttpClient(HttpClient): - """ Wrapper around the twisted HTTP client api. +class BaseHttpClient(object): + """Base class for HTTP clients using twisted. + """ + + def __init__(self, hs): + self.agent = MatrixHttpAgent(reactor) + self.hs = hs + + @defer.inlineCallbacks + def _create_request(self, destination, method, path_bytes, param_bytes=b"", + query_bytes=b"", producer=None, headers_dict={}, + retry_on_dns_fail=True, on_send_callback=None): + """ Creates and sends a request to the given url + """ + headers_dict[b"User-Agent"] = [b"Synapse"] + headers_dict[b"Host"] = [destination] + + logger.debug("Sending request to %s: %s %s;%s?%s", + destination, method, path_bytes, param_bytes, query_bytes) + + logger.debug( + "Types: %s", + [ + type(destination), type(method), type(path_bytes), + type(param_bytes), + type(query_bytes) + ] + ) + + retries_left = 5 + + endpoint = self._getEndpoint(reactor, destination); + + while True: + if on_send_callback: + on_send_callback(destination, method, path_bytes, producer) + + try: + response = yield self.agent.request( + destination, + endpoint, + method, + path_bytes, + param_bytes, + query_bytes, + Headers(headers_dict), + producer + ) + + logger.debug("Got response to %s", method) + break + except Exception as e: + if not retry_on_dns_fail and isinstance(e, DNSLookupError): + logger.warn("DNS Lookup failed to %s with %s", destination, + e) + raise SynapseError(400, "Domain specified not found.") + + logger.exception("Got error in _create_request") + _print_ex(e) + + if retries_left: + yield sleep(2 ** (5 - retries_left)) + retries_left -= 1 + else: + raise + + if 200 <= response.code < 300: + # We need to update the transactions table to say it was sent? + pass + else: + # :'( + # Update transactions table? + logger.error( + "Got response %d %s", response.code, response.phrase + ) + raise CodeMessageException( + response.code, response.phrase + ) + + defer.returnValue(response) + + +class MatrixHttpClient(BaseHttpClient): + """ Wrapper around the twisted HTTP client api. Implements Attributes: agent (twisted.web.client.Agent): The twisted Agent used to send the requests. """ - def __init__(self, hs): - self.agent = MatrixHttpAgent(reactor) - self.hs = hs + RETRY_DNS_LOOKUP_FAILURES = "__retry_dns" @defer.inlineCallbacks def put_json(self, destination, path, data, on_send_callback=None): - if destination in _destination_mappings: - destination = _destination_mappings[destination] + """ Sends the specifed json data using PUT + Args: + destination (str): The remote server to send the HTTP request + to. + path (str): The HTTP path. + data (dict): A dict containing the data that will be used as + the request body. This will be encoded as JSON. + + Returns: + Deferred: Succeeds when we get a 2xx HTTP response. The result + will be the decoded JSON body. On a 4xx or 5xx error response a + CodeMessageException is raised. + """ response = yield self._create_request( destination.encode("ascii"), "PUT", @@ -136,9 +184,23 @@ class TwistedHttpClient(HttpClient): @defer.inlineCallbacks def get_json(self, destination, path, args={}): - if destination in _destination_mappings: - destination = _destination_mappings[destination] + """ Get's some json from the given host homeserver and path + + Args: + destination (str): The remote server to send the HTTP request + to. + path (str): The HTTP path. + args (dict): A dictionary used to create query strings, defaults to + None. + **Note**: The value of each key is assumed to be an iterable + and *not* a string. + + Returns: + Deferred: Succeeds when we get *any* HTTP response. + The result of the deferred is a tuple of `(code, response)`, + where `response` is a dict representing the decoded JSON body. + """ logger.debug("get_json args: %s", args) retry_on_dns_fail = True @@ -163,6 +225,22 @@ class TwistedHttpClient(HttpClient): defer.returnValue(json.loads(body)) + + def _getEndpoint(self, reactor, destination): + return matrix_endpoint( + reactor, destination, timeout=10, + ssl_context_factory=self.hs.tls_context_factory + ) + + +class IdentityServerHttpClient(BaseHttpClient): + """Separate HTTP client for talking to the Identity servers since they + don't use SRV records and talk x-www-form-urlencoded rather than JSON. + """ + def _getEndpoint(self, reactor, destination): + #TODO: This should be talking TLS + return matrix_endpoint(reactor, destination, timeout=10) + @defer.inlineCallbacks def post_urlencoded_get_json(self, destination, path, args={}): if destination in _destination_mappings: @@ -176,16 +254,25 @@ class TwistedHttpClient(HttpClient): "POST", path.encode("ascii"), producer=FileBodyProducer(StringIO(urllib.urlencode(args))), - headers_dict={"Content-Type": ["application/x-www-form-urlencoded"]} + headers_dict={ + "Content-Type": ["application/x-www-form-urlencoded"] + } ) body = yield readBody(response) defer.returnValue(json.loads(body)) - - # XXX FIXME : I'm so sorry. + + +class CaptchaServerHttpClient(MatrixHttpClient): + """Separate HTTP client for talking to google's captcha servers""" + + def _getEndpoint(self, reactor, destination): + return matrix_endpoint(reactor, destination, timeout=10) + @defer.inlineCallbacks - def post_urlencoded_get_raw(self, destination, path, accept_partial=False, args={}): + def post_urlencoded_get_raw(self, destination, path, accept_partial=False, + args={}): if destination in _destination_mappings: destination = _destination_mappings[destination] @@ -196,7 +283,9 @@ class TwistedHttpClient(HttpClient): "POST", path.encode("ascii"), producer=FileBodyProducer(StringIO(urllib.urlencode(args))), - headers_dict={"Content-Type": ["application/x-www-form-urlencoded"]} + headers_dict={ + "Content-Type": ["application/x-www-form-urlencoded"] + } ) try: @@ -207,93 +296,6 @@ class TwistedHttpClient(HttpClient): defer.returnValue(e.response) else: raise e - - - @defer.inlineCallbacks - def _create_request(self, destination, method, path_bytes, param_bytes=b"", - query_bytes=b"", producer=None, headers_dict={}, - retry_on_dns_fail=True, on_send_callback=None): - """ Creates and sends a request to the given url - """ - headers_dict[b"User-Agent"] = [b"Synapse"] - headers_dict[b"Host"] = [destination] - - logger.debug("Sending request to %s: %s %s;%s?%s", - destination, method, path_bytes, param_bytes, query_bytes) - - logger.debug( - "Types: %s", - [ - type(destination), type(method), type(path_bytes), - type(param_bytes), - type(query_bytes) - ] - ) - - retries_left = 5 - - # TODO: setup and pass in an ssl_context to enable TLS - endpoint = self._getEndpoint(reactor, destination); - - while True: - if on_send_callback: - on_send_callback(destination, method, path_bytes, producer) - - try: - response = yield self.agent.request( - destination, - endpoint, - method, - path_bytes, - param_bytes, - query_bytes, - Headers(headers_dict), - producer - ) - - logger.debug("Got response to %s", method) - break - except Exception as e: - if not retry_on_dns_fail and isinstance(e, DNSLookupError): - logger.warn("DNS Lookup failed to %s with %s", destination, - e) - raise SynapseError(400, "Domain specified not found.") - - logger.exception("Got error in _create_request") - _print_ex(e) - - if retries_left: - yield sleep(2 ** (5 - retries_left)) - retries_left -= 1 - else: - raise - - if 200 <= response.code < 300: - # We need to update the transactions table to say it was sent? - pass - else: - # :'( - # Update transactions table? - logger.error( - "Got response %d %s", response.code, response.phrase - ) - raise CodeMessageException( - response.code, response.phrase - ) - - defer.returnValue(response) - - def _getEndpoint(self, reactor, destination): - return matrix_endpoint( - reactor, destination, timeout=10, - ssl_context_factory=self.hs.tls_context_factory - ) - - -class PlainHttpClient(TwistedHttpClient): - def _getEndpoint(self, reactor, destination): - return matrix_endpoint(reactor, destination, timeout=10) - def _print_ex(e): if hasattr(e, "reasons") and e.reasons: -- cgit 1.4.1