blob: 9f116ac86dd5cf84ca20e41755255ecf8d151fe1 [file] [log] [blame]
# Copyright 2012 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
"""Google Cloud Storage specific Files API calls."""
__all__ = ['AuthorizationError',
'check_status',
'Error',
'FatalError',
'FileClosedError',
'ForbiddenError',
'NotFoundError',
'ServerError',
'TimeoutError',
'TransientError',
]
import httplib
class Error(Exception):
"""Base error for all gcs operations.
Error can happen on GAE side or GCS server side.
For details on a particular GCS HTTP response code, see
https://developers.google.com/storage/docs/reference-status#standardcodes
"""
class TransientError(Error):
"""TransientError could be retried."""
class TimeoutError(TransientError):
"""HTTP 408 timeout."""
class FatalError(Error):
"""FatalError shouldn't be retried."""
class FileClosedError(FatalError):
"""File is already closed.
This can happen when the upload has finished but 'write' is called on
a stale upload handle.
"""
class NotFoundError(FatalError):
"""HTTP 404 resource not found."""
class ForbiddenError(FatalError):
"""HTTP 403 Forbidden.
While GCS replies with a 403 error for many reasons, the most common one
is due to bucket permission not correctly setup for your app to access.
"""
class AuthorizationError(FatalError):
"""HTTP 401 authentication required.
Unauthorized request has been received by GCS.
This error is mostly handled by GCS client. GCS client will request
a new access token and retry the request.
"""
class InvalidRange(FatalError):
"""HTTP 416 RequestRangeNotSatifiable."""
class ServerError(TransientError):
"""HTTP >= 500 server side error."""
def check_status(status, expected, path, headers=None,
resp_headers=None, extras=None):
"""Check HTTP response status is expected.
Args:
status: HTTP response status. int.
expected: a list of expected statuses. A list of ints.
path: filename or a path prefix.
headers: HTTP request headers.
resp_headers: HTTP response headers.
extras: extra info to be logged verbatim if error occurs.
Raises:
AuthorizationError: if authorization failed.
NotFoundError: if an object that's expected to exist doesn't.
TimeoutError: if HTTP request timed out.
ServerError: if server experienced some errors.
FatalError: if any other unexpected errors occurred.
"""
if status in expected:
return
msg = ('Expect status %r from Google Storage. But got status %d.\n'
'Path: %r.\n'
'Request headers: %r.\n'
'Response headers: %r.\n'
'Extra info: %r.\n' %
(expected, status, path, headers, resp_headers, extras))
if status == httplib.UNAUTHORIZED:
raise AuthorizationError(msg)
elif status == httplib.FORBIDDEN:
raise ForbiddenError(msg)
elif status == httplib.NOT_FOUND:
raise NotFoundError(msg)
elif status == httplib.REQUEST_TIMEOUT:
raise TimeoutError(msg)
elif status == httplib.REQUESTED_RANGE_NOT_SATISFIABLE:
raise InvalidRange(msg)
elif (status == httplib.OK and 308 in expected and
httplib.OK not in expected):
raise FileClosedError(msg)
elif status >= 500:
raise ServerError(msg)
else:
raise FatalError(msg)