Silence a few common errors when fetching (#404)

Downgrade nodeinfo json error to a captured message
This commit is contained in:
Michael Manfre 2023-01-13 12:53:02 -05:00 committed by GitHub
parent fa688a5a73
commit 18b50ce0e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 34 additions and 22 deletions

View File

@ -1,4 +1,5 @@
import hashlib import hashlib
import json
import mimetypes import mimetypes
import re import re
from collections.abc import Iterable from collections.abc import Iterable
@ -872,12 +873,15 @@ class Post(StatorModel):
f"Error fetching post from {object_uri}: {response.status_code}", f"Error fetching post from {object_uri}: {response.status_code}",
{response.content}, {response.content},
) )
post = cls.by_ap( try:
canonicalise(response.json(), include_security=True), post = cls.by_ap(
create=True, canonicalise(response.json(), include_security=True),
update=True, create=True,
fetch_author=True, update=True,
) fetch_author=True,
)
except (json.JSONDecodeError, ValueError):
raise cls.DoesNotExist(f"Invalid ld+json response for {object_uri}")
# We may need to fetch the author too # We may need to fetch the author too
if post.author.state == IdentityStates.outdated: if post.author.state == IdentityStates.outdated:
async_to_sync(post.author.fetch_actor)() async_to_sync(post.author.fetch_actor)()

View File

@ -1,4 +1,5 @@
import json import json
import ssl
from typing import Optional from typing import Optional
import httpx import httpx
@ -7,6 +8,7 @@ from asgiref.sync import sync_to_async
from django.conf import settings from django.conf import settings
from django.db import models from django.db import models
from core.exceptions import capture_message
from stator.models import State, StateField, StateGraph, StatorModel from stator.models import State, StateField, StateGraph, StatorModel
from users.schemas import NodeInfo from users.schemas import NodeInfo
@ -164,6 +166,8 @@ class Domain(StatorModel):
) )
except httpx.HTTPError: except httpx.HTTPError:
pass pass
except ssl.SSLCertVerificationError:
return None
else: else:
try: try:
for link in response.json().get("links", []): for link in response.json().get("links", []):
@ -183,7 +187,7 @@ class Domain(StatorModel):
headers={"Accept": "application/json"}, headers={"Accept": "application/json"},
) )
response.raise_for_status() response.raise_for_status()
except httpx.HTTPError as ex: except (httpx.HTTPError, ssl.SSLCertVerificationError) as ex:
response = getattr(ex, "response", None) response = getattr(ex, "response", None)
if ( if (
response response
@ -199,9 +203,10 @@ class Domain(StatorModel):
try: try:
info = NodeInfo(**response.json()) info = NodeInfo(**response.json())
except json.JSONDecodeError as ex: except json.JSONDecodeError as ex:
raise ValueError( capture_message(
f"Client error decoding nodeinfo: domain={self.domain}, error={str(ex)}" f"Client error decoding nodeinfo: domain={self.domain}, error={str(ex)}"
) )
return None
return info return info
@property @property

View File

@ -1,3 +1,4 @@
import ssl
from functools import cached_property, partial from functools import cached_property, partial
from typing import Literal from typing import Literal
from urllib.parse import urlparse from urllib.parse import urlparse
@ -642,7 +643,10 @@ class Identity(StatorModel):
(actor uri, canonical handle) or None, None if it does not resolve. (actor uri, canonical handle) or None, None if it does not resolve.
""" """
domain = handle.split("@")[1].lower() domain = handle.split("@")[1].lower()
webfinger_url = await cls.fetch_webfinger_url(domain) try:
webfinger_url = await cls.fetch_webfinger_url(domain)
except ssl.SSLCertVerificationError:
return None, None
# Go make a Webfinger request # Go make a Webfinger request
async with httpx.AsyncClient( async with httpx.AsyncClient(
@ -656,7 +660,7 @@ class Identity(StatorModel):
headers={"Accept": "application/json"}, headers={"Accept": "application/json"},
) )
response.raise_for_status() response.raise_for_status()
except httpx.RequestError as ex: except (httpx.HTTPError, ssl.SSLCertVerificationError) as ex:
response = getattr(ex, "response", None) response = getattr(ex, "response", None)
if ( if (
response response
@ -703,25 +707,24 @@ class Identity(StatorModel):
method="get", method="get",
uri=self.actor_uri, uri=self.actor_uri,
) )
except httpx.RequestError: except (httpx.RequestError, ssl.SSLCertVerificationError):
return False return False
content_type = response.headers.get("content-type") content_type = response.headers.get("content-type")
if content_type and "html" in content_type: if content_type and "html" in content_type:
# Some servers don't properly handle "application/activity+json" # Some servers don't properly handle "application/activity+json"
return False return False
if response.status_code == 410: status_code = response.status_code
# Their account got deleted, so let's do the same. if status_code >= 400:
if self.pk: if status_code == 410 and self.pk:
# Their account got deleted, so let's do the same.
await Identity.objects.filter(pk=self.pk).adelete() await Identity.objects.filter(pk=self.pk).adelete()
return False
if response.status_code == 404: if status_code >= 500 or status_code in [403, 404, 410]:
# We don't trust this as much as 410 Gone, but skip for now # Common errors with other server, not worth reporting
return False return False
if response.status_code >= 500:
return False
if response.status_code >= 400:
raise ValueError( raise ValueError(
f"Client error fetching actor at {self.actor_uri}: {response.status_code}", f"Client error fetching actor at {self.actor_uri}: {status_code}",
response.content, response.content,
) )
document = canonicalise(response.json(), include_security=True) document = canonicalise(response.json(), include_security=True)