Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 26 additions & 12 deletions config/patch/s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ def get_bucket_url(self, tile):
# AWS S3: always HTTPS
return f"https://{self.bucket_name}.s3.{self.region_name}.amazonaws.com/{key}"

def _presigned_url(self, client_method, key):
# Built locally by botocore (SigV4 signature over Bucket/Key) — no
# network round-trip — so unsigned HEAD/GET requests against a
# private bucket are authenticated the same as the boto3 write path.
return self.conn().generate_presigned_url(
client_method, Params={'Bucket': self.bucket_name, 'Key': key})

def tile_key(self, tile):
return self._tile_location(tile, self.base_path, self.file_ext).lstrip('/')

Expand Down Expand Up @@ -150,18 +157,22 @@ def _set_metadata(self, response, tile):
def is_cached(self, tile, dimensions=None):
if tile.is_missing():
if self.use_http_get:
url = self.get_bucket_url(tile)
key = self.tile_key(tile)
url = self._presigned_url('head_object', key)
try:
response = _http.request('HEAD', url)
if response.status in (403, 404):
log.debug('S3: is_cached HTTP %s, url: %s' % (response.status, url))
if response.status == 404:
log.debug('S3: is_cached HTTP 404, key: %s' % key)
return False
if response.status == 403:
log.error('S3: is_cached HTTP 403 (access denied), key: %s' % key)
raise S3ConnectionError('S3 HTTP 403 (access denied) for key: %s' % key)
if response.status != 200:
log.error('S3: is_cached HTTP error, url: %s, status: %s' % (url, response.status))
raise S3ConnectionError('S3 HTTP error %s for url: %s' % (response.status, url))
log.error('S3: is_cached HTTP error, key: %s, status: %s' % (key, response.status))
raise S3ConnectionError('S3 HTTP error %s for key: %s' % (response.status, key))
self._set_metadata(response.headers, tile)
except urllib3.exceptions.HTTPError as e:
log.error('S3: is_cached request error, url: %s, error: %s' % (url, e))
log.error('S3: is_cached request error, key: %s, error: %s' % (key, e))
raise
else:
key = self.tile_key(tile)
Expand All @@ -187,18 +198,21 @@ def load_tile(self, tile, with_metadata=True, dimensions=None):
log.debug('S3:load_tile, key: %s' % key)

if self.use_http_get:
url = self.get_bucket_url(tile)
url = self._presigned_url('get_object', key)
try:
response = _http.request('GET', url)
if response.status in (403, 404):
log.debug('S3: load_tile HTTP %s, url: %s' % (response.status, url))
if response.status == 404:
log.debug('S3: load_tile HTTP 404, key: %s' % key)
return False
if response.status == 403:
log.error('S3: load_tile HTTP 403 (access denied), key: %s' % key)
raise S3ConnectionError('S3 HTTP 403 (access denied) for key: %s' % key)
if response.status != 200:
log.error('S3: load_tile HTTP error, url: %s, status: %s' % (url, response.status))
raise S3ConnectionError('S3 HTTP error %s for url: %s' % (response.status, url))
log.error('S3: load_tile HTTP error, key: %s, status: %s' % (key, response.status))
raise S3ConnectionError('S3 HTTP error %s for key: %s' % (response.status, key))
tile.source = ImageSource(io.BytesIO(response.data))
except urllib3.exceptions.HTTPError as e:
log.error('S3: load_tile request error, url: %s, error: %s' % (url, e))
log.error('S3: load_tile request error, key: %s, error: %s' % (key, e))
raise
else:
try:
Expand Down
Loading