Skip to content

Commit 1724326

Browse files
committed
download_file: do not HEAD pre-signed S3 URIs
Running HEAD on a pre-signed S3 URI gives a 403 response because the URI is signed for GET. Download pre-signed URIs all at once instead. Fixes #939
1 parent b0fa009 commit 1724326

2 files changed

Lines changed: 8 additions & 2 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313

1414
- Convert setup.py to pyproject.toml ([#920](https://github.com/Open-EO/openeo-python-client/issues/920))
1515
- Make `_DerivedFrom._from_url` more resilient against unresolvable/unparsable `derived_from` links ([#928](https://github.com/Open-EO/openeo-python-client/issues/928), eu-cdse/openeo-cdse-infra#1338)
16+
- Download pre-signed S3 URIs all at once ([#939](https://github.com/Open-EO/openeo-python-client/issues/939))
1617

1718
### Removed
1819

‎openeo/rest/_connection.py‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import urllib3.util
1010
from requests import Response
1111
from requests.auth import AuthBase
12+
from urllib.parse import parse_qs, urlparse
1213

1314
import openeo
1415
from openeo.rest import (
@@ -305,8 +306,12 @@ def download_url(
305306
chunk_size: int = DEFAULT_DOWNLOAD_CHUNK_SIZE,
306307
range_size: int = DEFAULT_DOWNLOAD_RANGE_SIZE,
307308
) -> None:
308-
head = self.head(url, stream=True)
309-
if head.ok and head.headers.get("Accept-Ranges") == "bytes" and "Content-Length" in head.headers:
309+
query_params = parse_qs(urlparse(url).query)
310+
# Pre-signed S3 URIs are signed for "GET" and will result in a 403 when running
311+
# HEAD on them, so download them all at once instead.
312+
signed = "X-Amz-Signature" in query_params or "Signature" in query_params
313+
head = None if signed else self.head(url, stream=True)
314+
if head and head.ok and head.headers.get("Accept-Ranges") == "bytes" and "Content-Length" in head.headers:
310315
file_size = int(head.headers["Content-Length"])
311316
self._download_ranged(
312317
url=url, target=target, file_size=file_size, chunk_size=chunk_size, range_size=range_size

0 commit comments

Comments
 (0)