summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--synapse/federation/transport/client.py10
-rw-r--r--synapse/http/matrixfederationclient.py103
-rw-r--r--tests/handlers/test_typing.py4
3 files changed, 78 insertions, 39 deletions
diff --git a/synapse/federation/transport/client.py b/synapse/federation/transport/client.py
index 37f37b3d41..e424c40fdf 100644
--- a/synapse/federation/transport/client.py
+++ b/synapse/federation/transport/client.py
@@ -54,7 +54,7 @@ class TransportLayerClient(object):
         path = _create_v1_path("/state/%s", room_id)
         return self.client.get_json(
             destination, path=path, args={"event_id": event_id},
-            try_trailing_slash_on_404=True,
+            try_trailing_slash_on_400=True,
         )
 
     @log_function
@@ -77,7 +77,7 @@ class TransportLayerClient(object):
         path = _create_v1_path("/state_ids/%s", room_id)
         return self.client.get_json(
             destination, path=path, args={"event_id": event_id},
-            try_trailing_slash_on_404=True,
+            try_trailing_slash_on_400=True,
         )
 
     @log_function
@@ -100,7 +100,7 @@ class TransportLayerClient(object):
         path = _create_v1_path("/event/%s", event_id)
         return self.client.get_json(
             destination, path=path, timeout=timeout,
-            try_trailing_slash_on_404=True,
+            try_trailing_slash_on_400=True,
         )
 
     @log_function
@@ -137,7 +137,7 @@ class TransportLayerClient(object):
             destination,
             path=path,
             args=args,
-            try_trailing_slash_on_404=True,
+            try_trailing_slash_on_400=True,
         )
 
     @defer.inlineCallbacks
@@ -182,7 +182,7 @@ class TransportLayerClient(object):
             json_data_callback=json_data_callback,
             long_retries=True,
             backoff_on_404=True,  # If we get a 404 the other side has gone
-            try_trailing_slash_on_404=True,
+            try_trailing_slash_on_400=True,
         )
 
         defer.returnValue(response)
diff --git a/synapse/http/matrixfederationclient.py b/synapse/http/matrixfederationclient.py
index fc8cf92067..9019c8791a 100644
--- a/synapse/http/matrixfederationclient.py
+++ b/synapse/http/matrixfederationclient.py
@@ -189,6 +189,55 @@ class MatrixFederationHttpClient(object):
         self._cooperator = Cooperator(scheduler=schedule)
 
     @defer.inlineCallbacks
+    def _send_request_with_optional_trailing_slash(
+        request,
+        try_trailing_slash_on_400=False,
+        backoff_on_404=False,
+        **kwargs,
+    ):
+        """Wrapper for _send_request which can optionally retry the request
+        upon receiving a combination of a 400 HTTP response code and a
+        'M_UNRECOGNIZED' errcode. This is a workaround for Synapse <=v0.99.2
+        due to #3622.
+
+        Args:
+            request
+            try_trailing_slash_on_400 (bool): Whether on receiving a 400
+                'M_UNRECOGNIZED' from the server to retry the request with a
+                trailing slash appended to the request path.
+            backoff_on_404 (bool): Whether to backoff on 404 when making a
+                request with a trailing slash (only affects request if
+                try_trailing_slash_on_400 is True).
+            kwargs (Dict): A dictionary of arguments to pass to
+                `_send_request()`.
+
+        Returns:
+            Deferred[twisted.web.client.Response]: resolves with the HTTP
+            response object on success.
+        """
+        response = self._send_request(**kwargs)
+
+        if not try_trailing_slash_on_400:
+            defer.returnValue(response)
+
+        # Check if it's necessary to retry with a trailing slash
+        body = yield _handle_json_response(
+            self.hs.get_reactor(), self.default_timeout, request, response,
+        )
+
+        # Retry with a trailing slash if we received a 400 with
+        # 'M_UNRECOGNIZED' which some endpoints can return when omitting a
+        # trailing slash on Synapse <=v0.99.2.
+        if (response.code == 400 and body.get("errcode") == "M_UNRECOGNIZED"):
+            # Enable backoff if initially disabled
+            kwargs["backoff_on_404"] = backoff_on_404
+
+            kwargs["path"] += "/"
+            response = yield self._send_request(**kwargs)
+
+        defer.returnValue(response)
+
+    @defer.inlineCallbacks
     def _send_request(
         self,
         request,
@@ -474,7 +523,7 @@ class MatrixFederationHttpClient(object):
                  long_retries=False, timeout=None,
                  ignore_backoff=False,
                  backoff_on_404=False,
-                 try_trailing_slash_on_404=False):
+                 try_trailing_slash_on_400=False):
         """ Sends the specifed json data using PUT
 
         Args:
@@ -495,10 +544,11 @@ class MatrixFederationHttpClient(object):
             backoff_on_404 (bool): True if we should count a 404 response as
                 a failure of the server (and should therefore back off future
                 requests).
-            try_trailing_slash_on_404 (bool): True if on a 404 response we
-                should try appending a trailing slash to the end of the request.
-                Workaround for #3622 in Synapse <0.99.2. This will be attempted
-                before backing off if backing off has been enabled.
+            try_trailing_slash_on_400 (bool): True if on a 400 M_UNRECOGNIZED
+                response we should try appending a trailing slash to the end
+                of the request. Workaround for #3622 in Synapse <=v0.99.2. This
+                will be attempted before backing off if backing off has been
+                enabled.
 
         Returns:
             Deferred[dict|list]: Succeeds when we get a 2xx HTTP response. The
@@ -528,21 +578,21 @@ class MatrixFederationHttpClient(object):
             "long_retries": long_retries,
             "timeout": timeout,
             "ignore_backoff": ignore_backoff,
-            # Do not backoff on the initial request if we're trying with trailing slashes
-            # Otherwise we may end up waiting to contact a server that is actually up
-            "backoff_on_404": False if try_trailing_slash_on_404 else backoff_on_404,
+            # Do not backoff on the initial request if we're trying again with
+            # trailing slashes Otherwise we may end up waiting to contact a
+            # server that is actually up
+            "backoff_on_404": False if try_trailing_slash_on_400 else backoff_on_404,
         }
 
-        response = yield self._send_request(**send_request_args)
+        response = yield self._send_request_with_optional_trailing_slash(
+            request, try_trailing_slash_on_400, backoff_on_404, **send_request_args,
+        )
 
-        # If enabled, retry with a trailing slash if we received a 404
-        if try_trailing_slash_on_404 and response.code == 404:
+        # If enabled, retry with a trailing slash if we received a 400
+        if try_trailing_slash_on_400 and response.code == 400:
             args["path"] += "/"
 
-            # Re-enable backoff if enabled
-            send_request_args["backoff_on_404"] = backoff_on_404
-
-            response = yield self.get_json(**send_request_args)
+            response = yield self._send_request(**send_request_args)
 
         body = yield _handle_json_response(
             self.hs.get_reactor(), self.default_timeout, request, response,
@@ -610,7 +660,7 @@ class MatrixFederationHttpClient(object):
     @defer.inlineCallbacks
     def get_json(self, destination, path, args=None, retry_on_dns_fail=True,
                  timeout=None, ignore_backoff=False,
-                 try_trailing_slash_on_404=False):
+                 try_trailing_slash_on_400=False):
         """ GETs some json from the given host homeserver and path
 
         Args:
@@ -624,9 +674,9 @@ class MatrixFederationHttpClient(object):
                 be retried.
             ignore_backoff (bool): true to ignore the historical backoff data
                 and try the request anyway.
-            try_trailing_slash_on_404 (bool): True if on a 404 response we
-                should try appending a trailing slash to the end of the
-                request. Workaround for #3622 in Synapse <0.99.2.
+            try_trailing_slash_on_400 (bool): True if on a 400 M_UNRECOGNIZED
+                response we should try appending a trailing slash to the end of
+                the request. Workaround for #3622 in Synapse <=v0.99.2.
         Returns:
             Deferred[dict|list]: Succeeds when we get a 2xx HTTP response. The
             result will be the decoded JSON body.
@@ -659,21 +709,10 @@ class MatrixFederationHttpClient(object):
             "ignore_backoff": ignore_backoff,
         }
 
-        response = yield self._send_request(**send_request_args)
-
-        body = yield _handle_json_response(
-            self.hs.get_reactor(), self.default_timeout, request, response,
+        response = yield self._send_request_with_optional_trailing_slash(
+            request, try_trailing_slash_on_400, False, **send_request_args,
         )
 
-        # If enabled, retry with a trailing slash if we received a 404
-        # or if a 400 with "M_UNRECOGNIZED" which some endpoints return
-        if (try_trailing_slash_on_404 and
-           (response.code == 404
-            or (response.code == 400
-                and body.get("errcode") == "M_UNRECOGNIZED"))):
-            args["path"] += "/"
-            response = yield self._send_request(**send_request_args)
-
         defer.returnValue(body)
 
     @defer.inlineCallbacks
diff --git a/tests/handlers/test_typing.py b/tests/handlers/test_typing.py
index 244a0bc80c..6460cbc708 100644
--- a/tests/handlers/test_typing.py
+++ b/tests/handlers/test_typing.py
@@ -192,7 +192,7 @@ class TypingNotificationsTestCase(unittest.HomeserverTestCase):
             json_data_callback=ANY,
             long_retries=True,
             backoff_on_404=True,
-            try_trailing_slash_on_404=True,
+            try_trailing_slash_on_400=True,
         )
 
     def test_started_typing_remote_recv(self):
@@ -270,7 +270,7 @@ class TypingNotificationsTestCase(unittest.HomeserverTestCase):
             json_data_callback=ANY,
             long_retries=True,
             backoff_on_404=True,
-            try_trailing_slash_on_404=True,
+            try_trailing_slash_on_400=True,
         )
 
         self.assertEquals(self.event_source.get_current_key(), 1)