Skip to content

Commit

Permalink
improve error handling and pass raw error messages via exceptions dow…
Browse files Browse the repository at this point in the history
…nstream, related to #34
  • Loading branch information
meeb committed Jul 28, 2024
1 parent d6aea5c commit 333448b
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 17 deletions.
16 changes: 13 additions & 3 deletions whoisit/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ class QueryError(WhoisItError):
Raised when there are any issues with queries.
"""

def __init__(self, message, response=''):
super().__init__(message)
self.response = response


class UnsupportedError(WhoisItError):
"""
Expand All @@ -28,19 +32,25 @@ class ParseError(WhoisItError):
"""


class ResourceDoesNotExist(WhoisItError):
class ResourceDoesNotExist(QueryError):
"""
Raised when querying a resource which doesn't exist.
"""


class RateLimitedError(WhoisItError):
class ResourceAccessDeniedError(QueryError):
"""
Raised when querying a resource returns an access denied response.
"""


class RateLimitedError(QueryError):
"""
Raised when querying a resource and getting a rate limited response.
"""


class RemoteServerError(WhoisItError):
class RemoteServerError(QueryError):
"""
Raised when querying a resource and getting a remote server error response.
"""
67 changes: 53 additions & 14 deletions whoisit/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

from .errors import (
QueryError,
ResourceAccessDeniedError,
RateLimitedError,
RemoteServerError,
ResourceDoesNotExist,
Expand Down Expand Up @@ -201,26 +202,64 @@ def add_url_params(self, url, extra_params):
qs_str = urlencode(qs)
return urlunsplit((parts.scheme, parts.netloc, parts.path, qs_str, ''))


def _decode_content(self, response):
"""
Decode the content of the response and attempt to force it into a
unicode string. This is used as part of the query failure exceptions
to allow downstream clients to decide how to behave when errors occur.
"""
try:
return response.text.strip()
except (ValueError, UnicodeDecodeError) as e:
return response.content.decode('utf-8', errors='ignore').strip()

def _process_response(self, response):
if response.status_code == 404:
raise ResourceDoesNotExist(f'RDAP {self.method} request to {self.url} '
f'returned a 404 error, the resource does '
f'not exist')
elif response.status_code == 429:
raise RateLimitedError(f'RDAP {self.method} request to {self.url} '
f'returned a 429 error, the resource has been '
f'rate limited')
elif response.status_code == 500:
raise RemoteServerError(f'RDAP {self.method} request to {self.url} '
f'returned a 500 error, the resource returned '
f'a remote server error')
status_code_map = {
401: (
ResourceAccessDeniedError,
f'RDAP {self.method} request to {self.url} returned a '
f'401 error, unauthorized',
),
403: (
ResourceAccessDeniedError,
f'RDAP {self.method} request to {self.url} returned a '
f'403 error, forbidden',
),
404: (
ResourceDoesNotExist,
f'RDAP {self.method} request to {self.url} returned a '
f'404 error, the resource does not exist',
),
422: (
ResourceAccessDeniedError,
f'RDAP {self.method} request to {self.url} returned a '
f'422 error, the request was unprocessable',
),
429: (
RateLimitedError,
f'RDAP {self.method} request to {self.url} returned a '
f'429 error, the resource has been rate limited',
),
500: (
RemoteServerError,
f'RDAP {self.method} request to {self.url} returned a '
f'500 error, the resource returned a remote server error',
),
}
if response.status_code in status_code_map:
error_class, error_message = status_code_map[response.status_code]
error_content = self._decode_content(response)
raise error_class(error_message, response=error_content)
elif response.status_code != 200:
error_content = self._decode_content(response)
raise QueryError(f'RDAP {self.method} request to {self.url} returned a '
f'non-200 status code of {response.status_code}')
f'non-200 status code of {response.status_code}',
response=error_content)
try:
return response.json()
except (TypeError, ValueError) as e:
raise QueryError(f'Failed to parse RDAP Query response as JSON: {e}') from e
raise QueryError(f'Failed to parse RDAP Query response as JSON: {e}', response=error_content) from e


class Query(BaseQuery):
Expand Down

0 comments on commit 333448b

Please sign in to comment.