diff --git a/endpoints/registry.py b/endpoints/registry.py index 8fd54e19c..ded3f8915 100644 --- a/endpoints/registry.py +++ b/endpoints/registry.py @@ -101,6 +101,13 @@ def get_image_layer(namespace, repository, image_id, headers): profile.debug('Looking up repo image') repo_image = model.get_repo_image(namespace, repository, image_id) + # Add the Accept-Ranges header if the storage engine supports resumable + # downloads. + extra_headers = {} + + if store.get_supports_resumable_downloads(): + extra_headers['Accept-Ranges'] = 'bytes'; + profile.debug('Looking up the layer path') try: path = store.image_layer_path(repo_image.storage.uuid) @@ -110,10 +117,12 @@ def get_image_layer(namespace, repository, image_id, headers): if direct_download_url: profile.debug('Returning direct download URL') - return redirect(direct_download_url) + resp = redirect(direct_download_url) + resp.headers = dict(headers, **extra_headers) + return resp profile.debug('Streaming layer data') - return Response(store.stream_read(repo_image.storage.locations, path), headers=headers) + return Response(store.stream_read(repo_image.storage.locations, path), headers=dict(headers, **extra_headers)) except (IOError, AttributeError): profile.debug('Image not found') abort(404, 'Image %(image_id)s not found', issue='unknown-image', diff --git a/storage/basestorage.py b/storage/basestorage.py index b554845b4..94bdec615 100644 --- a/storage/basestorage.py +++ b/storage/basestorage.py @@ -57,6 +57,9 @@ class BaseStorage(StoragePaths): def get_direct_download_url(self, path, expires_in=60): return None + def get_supports_resumable_downloads(self): + return False + def get_content(self, path): raise NotImplementedError diff --git a/storage/s3.py b/storage/s3.py index 1747d34dc..add4ed9dc 100644 --- a/storage/s3.py +++ b/storage/s3.py @@ -83,6 +83,9 @@ class S3Storage(BaseStorage): key.set_contents_from_string(content, encrypt_key=True) return path + def get_supports_resumable_downloads(self): + return True + def get_direct_download_url(self, path, expires_in=60): self._initialize_s3() path = self._init_path(path)