diff --git a/README.rst b/README.rst index a20ae64..21e6c17 100644 --- a/README.rst +++ b/README.rst @@ -113,6 +113,22 @@ setting:: AWS_REGION = 'us-east-1' +S3 supports multiple calling formats for bucket URLs. For example, the bucket +name may be called as a subdomain, or it may be called as a subdirectory. The +issue with a subdomain call, which is the default in ``boto.s3``, is that SSL +certificates of wildcarded subdomains are being deprecated for security, so +HTTPS URLs with bucket subdomains may trigger browser warnings, or, in +in some cases with Internet Explorer 10, hide the issue behind a connection +error. Therefore, the default is to use the bucket name as a subdirectory, +which is done by ``boto.s3.connection.OrdinaryCallingFormat``. To change this, +set ``URL_CALLING_FORMAT`` to a string describing either a calling format in +the ``boto.s3.connection`` module, or to a string of a full absolute import +path (similar to Django middleware). For example, either of the following +would suffice to use the default subdomain calling format, instead:: + + URL_CALLING_FORMAT = 'SubdomainCallingFormat' + URL_CALLING_FORMAT = 'boto.s3.connection.SubdomainCallingFormat' + Using in models --------------- diff --git a/athumb/backends/s3boto.py b/athumb/backends/s3boto.py index 935f1aa..250ea93 100644 --- a/athumb/backends/s3boto.py +++ b/athumb/backends/s3boto.py @@ -16,8 +16,10 @@ from django.core.files.base import File from django.core.files.storage import Storage from django.core.exceptions import ImproperlyConfigured +from django.utils.importlib import import_module try: + import boto.s3.connection from boto.s3.connection import S3Connection from boto.exception import S3ResponseError from boto.s3.key import Key @@ -54,6 +56,7 @@ 'application/javascript', 'application/x-javascript' )) +URL_CALLING_FORMAT = getattr(settings, 'URL_CALLING_FORMAT', None) if IS_GZIPPED: from gzip import GzipFile @@ -69,7 +72,8 @@ def __init__(self, bucket=STORAGE_BUCKET_NAME, headers=HEADERS, gzip=IS_GZIPPED, gzip_content_types=GZIP_CONTENT_TYPES, querystring_auth=QUERYSTRING_AUTH, - force_no_ssl=False): + force_no_ssl=False, + calling_format=URL_CALLING_FORMAT): self.bucket_name = bucket self.bucket_cname = bucket_cname self.host = self._get_host(region) @@ -86,8 +90,30 @@ def __init__(self, bucket=STORAGE_BUCKET_NAME, if not access_key and not secret_key: access_key, secret_key = self._get_access_keys() + if calling_format: + try: + if '.' in calling_format: + mod, klass = calling_format.rsplit('.', 1) + mod = import_module(mod) + calling_format = getattr(mod, klass) + else: + calling_format = getattr( + boto.s3.connection, + calling_format + ) + except (AttributeError, ImportError): + raise ImproperlyConfigured( + 'CALLBACK_FORMAT_URL does not point to a correct import ' + 'path or a class in s3.boto.connection.S3Connection.' + ) + else: + calling_format = boto.s3.connection.OrdinaryCallingFormat + self.connection = S3Connection( - access_key, secret_key, host=self.host, + access_key, + secret_key, + host=self.host, + calling_format=calling_format() ) @property