# 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)