|
@@ -15,17 +15,19 @@
|
|
|
import logging
|
|
|
import random
|
|
|
import time
|
|
|
+from io import BytesIO
|
|
|
from typing import Callable, Dict, Optional, Tuple
|
|
|
|
|
|
import attr
|
|
|
|
|
|
from twisted.internet import defer
|
|
|
from twisted.internet.interfaces import IReactorTime
|
|
|
-from twisted.web.client import RedirectAgent, readBody
|
|
|
+from twisted.web.client import RedirectAgent
|
|
|
from twisted.web.http import stringToDatetime
|
|
|
from twisted.web.http_headers import Headers
|
|
|
from twisted.web.iweb import IAgent, IResponse
|
|
|
|
|
|
+from synapse.http.client import BodyExceededMaxSize, read_body_with_max_size
|
|
|
from synapse.logging.context import make_deferred_yieldable
|
|
|
from synapse.util import Clock, json_decoder
|
|
|
from synapse.util.caches.ttlcache import TTLCache
|
|
@@ -53,6 +55,9 @@ WELL_KNOWN_MAX_CACHE_PERIOD = 48 * 3600
|
|
|
# lower bound for .well-known cache period
|
|
|
WELL_KNOWN_MIN_CACHE_PERIOD = 5 * 60
|
|
|
|
|
|
+# The maximum size (in bytes) to allow a well-known file to be.
|
|
|
+WELL_KNOWN_MAX_SIZE = 50 * 1024 # 50 KiB
|
|
|
+
|
|
|
# Attempt to refetch a cached well-known N% of the TTL before it expires.
|
|
|
# e.g. if set to 0.2 and we have a cached entry with a TTL of 5mins, then
|
|
|
# we'll start trying to refetch 1 minute before it expires.
|
|
@@ -229,6 +234,9 @@ class WellKnownResolver:
|
|
|
server_name: name of the server, from the requested url
|
|
|
retry: Whether to retry the request if it fails.
|
|
|
|
|
|
+ Raises:
|
|
|
+ _FetchWellKnownFailure if we fail to lookup a result
|
|
|
+
|
|
|
Returns:
|
|
|
Returns the response object and body. Response may be a non-200 response.
|
|
|
"""
|
|
@@ -250,7 +258,11 @@ class WellKnownResolver:
|
|
|
b"GET", uri, headers=Headers(headers)
|
|
|
)
|
|
|
)
|
|
|
- body = await make_deferred_yieldable(readBody(response))
|
|
|
+ body_stream = BytesIO()
|
|
|
+ await make_deferred_yieldable(
|
|
|
+ read_body_with_max_size(response, body_stream, WELL_KNOWN_MAX_SIZE)
|
|
|
+ )
|
|
|
+ body = body_stream.getvalue()
|
|
|
|
|
|
if 500 <= response.code < 600:
|
|
|
raise Exception("Non-200 response %s" % (response.code,))
|
|
@@ -259,6 +271,15 @@ class WellKnownResolver:
|
|
|
except defer.CancelledError:
|
|
|
# Bail if we've been cancelled
|
|
|
raise
|
|
|
+ except BodyExceededMaxSize:
|
|
|
+ # If the well-known file was too large, do not keep attempting
|
|
|
+ # to download it, but consider it a temporary error.
|
|
|
+ logger.warning(
|
|
|
+ "Requested .well-known file for %s is too large > %r bytes",
|
|
|
+ server_name.decode("ascii"),
|
|
|
+ WELL_KNOWN_MAX_SIZE,
|
|
|
+ )
|
|
|
+ raise _FetchWellKnownFailure(temporary=True)
|
|
|
except Exception as e:
|
|
|
if not retry or i >= WELL_KNOWN_RETRY_ATTEMPTS:
|
|
|
logger.info("Error fetching %s: %s", uri_str, e)
|