Skip to content

Commit 4cffeca

Browse files
committed
Add S3-compatible service detection for Linode and DigitalOcean
Fixes automatic service parameter detection for S3-compatible object storage providers: - Linode Object Storage (*.linodeobjects.com) - DigitalOcean Spaces (*.digitaloceanspaces.com) Previously, these endpoints required manually specifying service=s3 in the aws options, causing authorization errors when the service parameter was incorrectly guessed. Now the library automatically detects these endpoints and sets service=s3 with the correct region. Follows existing pattern used for Cloudflare R2 and Backblaze B2 detection. Evidence: - Linode issue: twilligon/git-lfs-s3-proxy#10 - DigitalOcean issue: #15 Added test coverage for both providers with virtual-host and path-style URLs.
1 parent f279a7e commit 4cffeca

8 files changed

Lines changed: 49 additions & 5 deletions

File tree

dist/aws4fetch.cjs.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,14 @@ function guessServiceRegion(url, headers) {
259259
const match = hostname.match(/^(?:[^.]{1,63}\.)?s3\.([^.]{1,63})\.backblazeb2\.com$/);
260260
return match != null ? ['s3', match[1] || ''] : ['', '']
261261
}
262+
if (hostname.endsWith('.linodeobjects.com')) {
263+
const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.linodeobjects\.com$/);
264+
return match != null ? ['s3', match[1] || ''] : ['', '']
265+
}
266+
if (hostname.endsWith('.digitaloceanspaces.com')) {
267+
const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.digitaloceanspaces\.com$/);
268+
return match != null ? ['s3', match[1] || ''] : ['', '']
269+
}
262270
const match = hostname.replace('dualstack.', '').match(/([^.]{1,63})\.(?:([^.]{0,63})\.)?amazonaws\.com(?:\.cn)?$/);
263271
let service = (match && match[1]) || '';
264272
let region = match && match[2];

dist/aws4fetch.esm.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,14 @@ function guessServiceRegion(url, headers) {
255255
const match = hostname.match(/^(?:[^.]{1,63}\.)?s3\.([^.]{1,63})\.backblazeb2\.com$/);
256256
return match != null ? ['s3', match[1] || ''] : ['', '']
257257
}
258+
if (hostname.endsWith('.linodeobjects.com')) {
259+
const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.linodeobjects\.com$/);
260+
return match != null ? ['s3', match[1] || ''] : ['', '']
261+
}
262+
if (hostname.endsWith('.digitaloceanspaces.com')) {
263+
const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.digitaloceanspaces\.com$/);
264+
return match != null ? ['s3', match[1] || ''] : ['', '']
265+
}
258266
const match = hostname.replace('dualstack.', '').match(/([^.]{1,63})\.(?:([^.]{0,63})\.)?amazonaws\.com(?:\.cn)?$/);
259267
let service = (match && match[1]) || '';
260268
let region = match && match[2];

dist/aws4fetch.esm.mjs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,14 @@ function guessServiceRegion(url, headers) {
255255
const match = hostname.match(/^(?:[^.]{1,63}\.)?s3\.([^.]{1,63})\.backblazeb2\.com$/);
256256
return match != null ? ['s3', match[1] || ''] : ['', '']
257257
}
258+
if (hostname.endsWith('.linodeobjects.com')) {
259+
const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.linodeobjects\.com$/);
260+
return match != null ? ['s3', match[1] || ''] : ['', '']
261+
}
262+
if (hostname.endsWith('.digitaloceanspaces.com')) {
263+
const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.digitaloceanspaces\.com$/);
264+
return match != null ? ['s3', match[1] || ''] : ['', '']
265+
}
258266
const match = hostname.replace('dualstack.', '').match(/([^.]{1,63})\.(?:([^.]{0,63})\.)?amazonaws\.com(?:\.cn)?$/);
259267
let service = (match && match[1]) || '';
260268
let region = match && match[2];

dist/aws4fetch.umd.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,14 @@
261261
const match = hostname.match(/^(?:[^.]{1,63}\.)?s3\.([^.]{1,63})\.backblazeb2\.com$/);
262262
return match != null ? ['s3', match[1] || ''] : ['', '']
263263
}
264+
if (hostname.endsWith('.linodeobjects.com')) {
265+
const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.linodeobjects\.com$/);
266+
return match != null ? ['s3', match[1] || ''] : ['', '']
267+
}
268+
if (hostname.endsWith('.digitaloceanspaces.com')) {
269+
const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.digitaloceanspaces\.com$/);
270+
return match != null ? ['s3', match[1] || ''] : ['', '']
271+
}
264272
const match = hostname.replace('dualstack.', '').match(/([^.]{1,63})\.(?:([^.]{0,63})\.)?amazonaws\.com(?:\.cn)?$/);
265273
let service = (match && match[1]) || '';
266274
let region = match && match[2];

dist/main.d.mts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export class AwsClient {
3333
allHeaders?: boolean;
3434
singleEncode?: boolean;
3535
};
36-
}) | null | undefined): Promise<Request>;
36+
}) | null): Promise<Request>;
3737
fetch(input: Request | {
3838
toString: () => string;
3939
}, init?: (RequestInit & {
@@ -50,7 +50,7 @@ export class AwsClient {
5050
allHeaders?: boolean;
5151
singleEncode?: boolean;
5252
};
53-
}) | null | undefined): Promise<Response>;
53+
}) | null): Promise<Response>;
5454
}
5555
export class AwsV4Signer {
5656
constructor({ method, url, headers, body, accessKeyId, secretAccessKey, sessionToken, service, region, cache, datetime, signQuery, appendSessionToken, allHeaders, singleEncode }: {

dist/main.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export class AwsClient {
3333
allHeaders?: boolean;
3434
singleEncode?: boolean;
3535
};
36-
}) | null | undefined): Promise<Request>;
36+
}) | null): Promise<Request>;
3737
fetch(input: Request | {
3838
toString: () => string;
3939
}, init?: (RequestInit & {
@@ -50,7 +50,7 @@ export class AwsClient {
5050
allHeaders?: boolean;
5151
singleEncode?: boolean;
5252
};
53-
}) | null | undefined): Promise<Response>;
53+
}) | null): Promise<Response>;
5454
}
5555
export class AwsV4Signer {
5656
constructor({ method, url, headers, body, accessKeyId, secretAccessKey, sessionToken, service, region, cache, datetime, signQuery, appendSessionToken, allHeaders, singleEncode }: {

src/main.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,14 @@ function guessServiceRegion(url, headers) {
404404
const match = hostname.match(/^(?:[^.]{1,63}\.)?s3\.([^.]{1,63})\.backblazeb2\.com$/)
405405
return match != null ? ['s3', match[1] || ''] : ['', '']
406406
}
407+
if (hostname.endsWith('.linodeobjects.com')) {
408+
const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.linodeobjects\.com$/)
409+
return match != null ? ['s3', match[1] || ''] : ['', '']
410+
}
411+
if (hostname.endsWith('.digitaloceanspaces.com')) {
412+
const match = hostname.match(/^(?:[^.]{1,63}\.)?([^.]{1,63})\.digitaloceanspaces\.com$/)
413+
return match != null ? ['s3', match[1] || ''] : ['', '']
414+
}
407415
const match = hostname.replace('dualstack.', '').match(/([^.]{1,63})\.(?:([^.]{0,63})\.)?amazonaws\.com(?:\.cn)?$/)
408416
let service = (match && match[1]) || ''
409417
let region = match && match[2]

test/serviceTests.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AwsV4Signer } from '../src/main'
1+
import { AwsV4Signer } from '../src/main.js'
22

33
export default async() => {
44
fixtures().forEach(({ url, service, region }) => {
@@ -77,6 +77,10 @@ aa-custom-bucket.s3.us-west-001.backblazeb2.com,s3,us-west-001
7777
s3.us-west-001.backblazeb2.com,s3,us-west-001
7878
12345678.r2.cloudflarestorage.com,s3,auto
7979
aa-custom-bucket.12345678.r2.cloudflarestorage.com,s3,auto
80+
my-repo.us-sea-1.linodeobjects.com,s3,us-sea-1
81+
us-sea-1.linodeobjects.com,s3,us-sea-1
82+
my-space.nyc3.digitaloceanspaces.com,s3,nyc3
83+
nyc3.digitaloceanspaces.com,s3,nyc3
8084
ibvt72cx3dkyksnw7jktvkwhme0legmv.lambda-url.us-east-1.on.aws,lambda,us-east-1
8185
`
8286
return csv.trim().split('\n').map(line => {

0 commit comments

Comments
 (0)