From 3fadb20cab43ac8c36d5a96edafa3ec023c443a2 Mon Sep 17 00:00:00 2001 From: twilligon Date: Sat, 8 Nov 2025 19:21:38 -0800 Subject: [PATCH] Use service=s3 by default for DO Spaces and Linode A few services expect the `service` parameter to be set to `s3` where this is not currently inferred, such that "default" aws4fetch use fails with authentication errors. We already handle this case for R2 and B2, but we were missing Linode Object Storage and DigitalOcean Spaces: https://github.com/mhart/aws4fetch/issues/15 https://github.com/twilligon/git-lfs-s3-proxy/issues/10 Handle `service` as we do for R2 and B2 so aws4fetch works by default. Also, add virtual-host and path-style URL tests for both providers. --- dist/aws4fetch.cjs.js | 8 ++++++++ dist/aws4fetch.esm.js | 8 ++++++++ dist/aws4fetch.esm.mjs | 8 ++++++++ dist/aws4fetch.umd.js | 8 ++++++++ src/main.js | 8 ++++++++ test/serviceTests.js | 4 ++++ 6 files changed, 44 insertions(+) diff --git a/dist/aws4fetch.cjs.js b/dist/aws4fetch.cjs.js index b827565..cb9b6a1 100644 --- a/dist/aws4fetch.cjs.js +++ b/dist/aws4fetch.cjs.js @@ -259,6 +259,14 @@ function guessServiceRegion(url, headers) { const match = hostname.match(/^(?:[^.]{1,63}\.)?s3\.([^.]{1,63})\.backblazeb2\.com$/); return match != null ? ['s3', match[1] || ''] : ['', ''] } + if (hostname.endsWith('.linodeobjects.com')) { + const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.linodeobjects\.com$/); + return match != null ? ['s3', match[1] || ''] : ['', ''] + } + if (hostname.endsWith('.digitaloceanspaces.com')) { + const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.digitaloceanspaces\.com$/); + return match != null ? ['s3', match[1] || ''] : ['', ''] + } const match = hostname.replace('dualstack.', '').match(/([^.]{1,63})\.(?:([^.]{0,63})\.)?amazonaws\.com(?:\.cn)?$/); let service = (match && match[1]) || ''; let region = match && match[2]; diff --git a/dist/aws4fetch.esm.js b/dist/aws4fetch.esm.js index 9c27de4..598d291 100644 --- a/dist/aws4fetch.esm.js +++ b/dist/aws4fetch.esm.js @@ -255,6 +255,14 @@ function guessServiceRegion(url, headers) { const match = hostname.match(/^(?:[^.]{1,63}\.)?s3\.([^.]{1,63})\.backblazeb2\.com$/); return match != null ? ['s3', match[1] || ''] : ['', ''] } + if (hostname.endsWith('.linodeobjects.com')) { + const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.linodeobjects\.com$/); + return match != null ? ['s3', match[1] || ''] : ['', ''] + } + if (hostname.endsWith('.digitaloceanspaces.com')) { + const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.digitaloceanspaces\.com$/); + return match != null ? ['s3', match[1] || ''] : ['', ''] + } const match = hostname.replace('dualstack.', '').match(/([^.]{1,63})\.(?:([^.]{0,63})\.)?amazonaws\.com(?:\.cn)?$/); let service = (match && match[1]) || ''; let region = match && match[2]; diff --git a/dist/aws4fetch.esm.mjs b/dist/aws4fetch.esm.mjs index 9c27de4..598d291 100644 --- a/dist/aws4fetch.esm.mjs +++ b/dist/aws4fetch.esm.mjs @@ -255,6 +255,14 @@ function guessServiceRegion(url, headers) { const match = hostname.match(/^(?:[^.]{1,63}\.)?s3\.([^.]{1,63})\.backblazeb2\.com$/); return match != null ? ['s3', match[1] || ''] : ['', ''] } + if (hostname.endsWith('.linodeobjects.com')) { + const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.linodeobjects\.com$/); + return match != null ? ['s3', match[1] || ''] : ['', ''] + } + if (hostname.endsWith('.digitaloceanspaces.com')) { + const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.digitaloceanspaces\.com$/); + return match != null ? ['s3', match[1] || ''] : ['', ''] + } const match = hostname.replace('dualstack.', '').match(/([^.]{1,63})\.(?:([^.]{0,63})\.)?amazonaws\.com(?:\.cn)?$/); let service = (match && match[1]) || ''; let region = match && match[2]; diff --git a/dist/aws4fetch.umd.js b/dist/aws4fetch.umd.js index 8fdb68f..c7f900b 100644 --- a/dist/aws4fetch.umd.js +++ b/dist/aws4fetch.umd.js @@ -261,6 +261,14 @@ const match = hostname.match(/^(?:[^.]{1,63}\.)?s3\.([^.]{1,63})\.backblazeb2\.com$/); return match != null ? ['s3', match[1] || ''] : ['', ''] } + if (hostname.endsWith('.linodeobjects.com')) { + const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.linodeobjects\.com$/); + return match != null ? ['s3', match[1] || ''] : ['', ''] + } + if (hostname.endsWith('.digitaloceanspaces.com')) { + const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.digitaloceanspaces\.com$/); + return match != null ? ['s3', match[1] || ''] : ['', ''] + } const match = hostname.replace('dualstack.', '').match(/([^.]{1,63})\.(?:([^.]{0,63})\.)?amazonaws\.com(?:\.cn)?$/); let service = (match && match[1]) || ''; let region = match && match[2]; diff --git a/src/main.js b/src/main.js index ebc7212..0c3a8bd 100644 --- a/src/main.js +++ b/src/main.js @@ -404,6 +404,14 @@ function guessServiceRegion(url, headers) { const match = hostname.match(/^(?:[^.]{1,63}\.)?s3\.([^.]{1,63})\.backblazeb2\.com$/) return match != null ? ['s3', match[1] || ''] : ['', ''] } + if (hostname.endsWith('.linodeobjects.com')) { + const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.linodeobjects\.com$/) + return match != null ? ['s3', match[1] || ''] : ['', ''] + } + if (hostname.endsWith('.digitaloceanspaces.com')) { + const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.digitaloceanspaces\.com$/) + return match != null ? ['s3', match[1] || ''] : ['', ''] + } const match = hostname.replace('dualstack.', '').match(/([^.]{1,63})\.(?:([^.]{0,63})\.)?amazonaws\.com(?:\.cn)?$/) let service = (match && match[1]) || '' let region = match && match[2] diff --git a/test/serviceTests.js b/test/serviceTests.js index e44e670..844c3ae 100644 --- a/test/serviceTests.js +++ b/test/serviceTests.js @@ -77,6 +77,10 @@ aa-custom-bucket.s3.us-west-001.backblazeb2.com,s3,us-west-001 s3.us-west-001.backblazeb2.com,s3,us-west-001 12345678.r2.cloudflarestorage.com,s3,auto aa-custom-bucket.12345678.r2.cloudflarestorage.com,s3,auto +my-repo.us-sea-1.linodeobjects.com,s3,us-sea-1 +us-sea-1.linodeobjects.com,s3,us-sea-1 +my-space.nyc3.digitaloceanspaces.com,s3,nyc3 +nyc3.digitaloceanspaces.com,s3,nyc3 ibvt72cx3dkyksnw7jktvkwhme0legmv.lambda-url.us-east-1.on.aws,lambda,us-east-1 ` return csv.trim().split('\n').map(line => {