702d080a1e
Google BK
114 lines
4.7 KiB
Python
114 lines
4.7 KiB
Python
from aiohttp import ClientSession, ContentTypeError, ClientConnectorError, ClientTimeout, ClientResponse
|
|
from aiohttp.client_exceptions import ServerTimeoutError, ServerDisconnectedError, ClientOSError
|
|
from backup.exceptions import GoogleUnexpectedError, GoogleInternalError, GoogleRateLimitError, GoogleCredentialsExpired, CredRefreshGoogleError, DriveQuotaExceeded, GoogleDrivePermissionDenied, GoogleDnsFailure, GoogleCantConnect, GoogleTimeoutError
|
|
from backup.util import Resolver
|
|
from backup.logger import getLogger
|
|
from backup.config import Config, Setting
|
|
from injector import singleton, inject
|
|
from dns.exception import DNSException
|
|
|
|
RATE_LIMIT_EXCEEDED = [403]
|
|
TOO_MANY_REQUESTS = [429]
|
|
INTERNAL_ERROR = [500, 503]
|
|
PERMISSION_DENIED = [401]
|
|
REQUEST_TIMEOUT = [408]
|
|
|
|
logger = getLogger(__name__)
|
|
|
|
|
|
@singleton
|
|
class DriveRequester():
|
|
@inject
|
|
def __init__(self, config: Config, session: ClientSession, resolver: Resolver):
|
|
self.session = session
|
|
self.resolver = resolver
|
|
self.config = config
|
|
|
|
async def request(self, method, url, headers={}, json=None, data=None) -> ClientResponse:
|
|
try:
|
|
response = await self.session.request(method, url, headers=headers, json=json, timeout=self.buildTimeout(), data=data)
|
|
if response.status < 400:
|
|
return response
|
|
await self.raiseForKnownErrors(response)
|
|
if response.status in PERMISSION_DENIED:
|
|
response.release()
|
|
raise GoogleCredentialsExpired()
|
|
elif response.status in INTERNAL_ERROR:
|
|
response.release()
|
|
raise GoogleInternalError()
|
|
elif response.status in RATE_LIMIT_EXCEEDED or response.status in TOO_MANY_REQUESTS:
|
|
response.release()
|
|
raise GoogleRateLimitError()
|
|
elif response.status in REQUEST_TIMEOUT:
|
|
response.release()
|
|
raise GoogleTimeoutError()
|
|
response.raise_for_status()
|
|
return response
|
|
except ClientConnectorError as e:
|
|
logger.debug(
|
|
"Ran into trouble reaching Google Drive's servers. We'll use alternate DNS servers on the next attempt.")
|
|
self.resolver.toggle()
|
|
if "Cannot connect to host" in str(e) or "Connection reset by peer" in str(e):
|
|
raise GoogleCantConnect()
|
|
if e.os_error.errno == -2:
|
|
# -2 means dns lookup failed.
|
|
raise GoogleDnsFailure()
|
|
elif str(e.os_error) == "Domain name not found":
|
|
raise GoogleDnsFailure()
|
|
elif e.os_error.errno in [99, 111, 10061, 104]:
|
|
# 111 means connection refused
|
|
# Can't connect
|
|
raise GoogleCantConnect()
|
|
elif "Could not contact DNS serve" in str(e.os_error):
|
|
# Wish there was a better way to identify this exception
|
|
raise GoogleDnsFailure()
|
|
raise
|
|
except ClientOSError as e:
|
|
if e.errno == 1:
|
|
raise GoogleUnexpectedError()
|
|
raise
|
|
except ServerTimeoutError:
|
|
raise GoogleTimeoutError()
|
|
except ServerDisconnectedError:
|
|
raise GoogleUnexpectedError()
|
|
except DNSException:
|
|
logger.debug(
|
|
"Ran into trouble resolving Google Drive's servers. We'll use normal DNS servers on the next attempt.")
|
|
self.resolver.toggle()
|
|
raise GoogleDnsFailure()
|
|
|
|
def buildTimeout(self):
|
|
return ClientTimeout(
|
|
sock_connect=self.config.get(
|
|
Setting.GOOGLE_DRIVE_TIMEOUT_SECONDS),
|
|
sock_read=self.config.get(Setting.GOOGLE_DRIVE_TIMEOUT_SECONDS))
|
|
|
|
async def raiseForKnownErrors(self, response):
|
|
try:
|
|
message = await response.json()
|
|
except ContentTypeError:
|
|
return
|
|
except ValueError:
|
|
# parsing json failed, just give up
|
|
return
|
|
except TypeError:
|
|
# Same
|
|
return
|
|
if "error" not in message:
|
|
return
|
|
error_obj = message["error"]
|
|
if isinstance(error_obj, str):
|
|
if error_obj == "expired":
|
|
raise GoogleCredentialsExpired()
|
|
else:
|
|
raise CredRefreshGoogleError(error_obj)
|
|
if "errors" not in error_obj:
|
|
return
|
|
for error in error_obj["errors"]:
|
|
if "reason" not in error:
|
|
continue
|
|
if error["reason"] == "storageQuotaExceeded":
|
|
raise DriveQuotaExceeded()
|
|
elif error["reason"] in ["forbidden", "insufficientFilePermissions"]:
|
|
raise GoogleDrivePermissionDenied()
|