aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Matteo Bertucci <[email protected]>2021-10-05 18:49:27 +0200
committerGravatar GitHub <[email protected]>2021-10-05 18:49:27 +0200
commitce8246f01dce4d443d7f6d5805d34c592c12c4fb (patch)
treebaec503eb0911df5f0bf2b76c3d13fcc06b888d4
parentFix linting (diff)
parentMerge pull request #1824 from Numerlor/doc-fetch_inventory-error-badarg (diff)
Merge branch 'main' into antispam-log-improvements
-rw-r--r--bot/converters.py15
-rw-r--r--bot/exts/info/doc/_cog.py9
-rw-r--r--bot/exts/info/doc/_inventory_parser.py22
3 files changed, 34 insertions, 12 deletions
diff --git a/bot/converters.py b/bot/converters.py
index 8b0890830..c96e2c984 100644
--- a/bot/converters.py
+++ b/bot/converters.py
@@ -235,11 +235,16 @@ class Inventory(Converter):
async def convert(ctx: Context, url: str) -> t.Tuple[str, _inventory_parser.InventoryDict]:
"""Convert url to Intersphinx inventory URL."""
await ctx.trigger_typing()
- if (inventory := await _inventory_parser.fetch_inventory(url)) is None:
- raise BadArgument(
- f"Failed to fetch inventory file after {_inventory_parser.FAILED_REQUEST_ATTEMPTS} attempts."
- )
- return url, inventory
+ try:
+ inventory = await _inventory_parser.fetch_inventory(url)
+ except _inventory_parser.InvalidHeaderError:
+ raise BadArgument("Unable to parse inventory because of invalid header, check if URL is correct.")
+ else:
+ if inventory is None:
+ raise BadArgument(
+ f"Failed to fetch inventory file after {_inventory_parser.FAILED_REQUEST_ATTEMPTS} attempts."
+ )
+ return url, inventory
class Snowflake(IDConverter):
diff --git a/bot/exts/info/doc/_cog.py b/bot/exts/info/doc/_cog.py
index 1624c50f6..e7710db24 100644
--- a/bot/exts/info/doc/_cog.py
+++ b/bot/exts/info/doc/_cog.py
@@ -22,7 +22,7 @@ from bot.utils.lock import SharedEvent, lock
from bot.utils.messages import send_denial, wait_for_deletion
from bot.utils.scheduling import Scheduler
from . import NAMESPACE, PRIORITY_PACKAGES, _batch_parser, doc_cache
-from ._inventory_parser import InventoryDict, fetch_inventory
+from ._inventory_parser import InvalidHeaderError, InventoryDict, fetch_inventory
log = logging.getLogger(__name__)
@@ -137,7 +137,12 @@ class DocCog(commands.Cog):
The first attempt is rescheduled to execute in `FETCH_RESCHEDULE_DELAY.first` minutes, the subsequent attempts
in `FETCH_RESCHEDULE_DELAY.repeated` minutes.
"""
- package = await fetch_inventory(inventory_url)
+ try:
+ package = await fetch_inventory(inventory_url)
+ except InvalidHeaderError as e:
+ # Do not reschedule if the header is invalid, as the request went through but the contents are invalid.
+ log.warning(f"Invalid inventory header at {inventory_url}. Reason: {e}")
+ return
if not package:
if api_package_name in self.inventory_scheduler:
diff --git a/bot/exts/info/doc/_inventory_parser.py b/bot/exts/info/doc/_inventory_parser.py
index 80d5841a0..61924d070 100644
--- a/bot/exts/info/doc/_inventory_parser.py
+++ b/bot/exts/info/doc/_inventory_parser.py
@@ -16,6 +16,10 @@ _V2_LINE_RE = re.compile(r'(?x)(.+?)\s+(\S*:\S*)\s+(-?\d+)\s+?(\S*)\s+(.*)')
InventoryDict = DefaultDict[str, List[Tuple[str, str]]]
+class InvalidHeaderError(Exception):
+ """Raised when an inventory file has an invalid header."""
+
+
class ZlibStreamReader:
"""Class used for decoding zlib data of a stream line by line."""
@@ -80,19 +84,25 @@ async def _fetch_inventory(url: str) -> InventoryDict:
stream = response.content
inventory_header = (await stream.readline()).decode().rstrip()
- inventory_version = int(inventory_header[-1:])
- await stream.readline() # skip project name
- await stream.readline() # skip project version
+ try:
+ inventory_version = int(inventory_header[-1:])
+ except ValueError:
+ raise InvalidHeaderError("Unable to convert inventory version header.")
+
+ has_project_header = (await stream.readline()).startswith(b"# Project")
+ has_version_header = (await stream.readline()).startswith(b"# Version")
+ if not (has_project_header and has_version_header):
+ raise InvalidHeaderError("Inventory missing project or version header.")
if inventory_version == 1:
return await _load_v1(stream)
elif inventory_version == 2:
if b"zlib" not in await stream.readline():
- raise ValueError(f"Invalid inventory file at url {url}.")
+ raise InvalidHeaderError("'zlib' not found in header of compressed inventory.")
return await _load_v2(stream)
- raise ValueError(f"Invalid inventory file at url {url}.")
+ raise InvalidHeaderError("Incompatible inventory version.")
async def fetch_inventory(url: str) -> Optional[InventoryDict]:
@@ -115,6 +125,8 @@ async def fetch_inventory(url: str) -> Optional[InventoryDict]:
f"Failed to get inventory from {url}; "
f"trying again ({attempt}/{FAILED_REQUEST_ATTEMPTS})."
)
+ except InvalidHeaderError:
+ raise
except Exception:
log.exception(
f"An unexpected error has occurred during fetching of {url}; "