| 21 | } |
| 22 | |
| 23 | export function parseS3Uri( |
| 24 | s3Uri: string, |
| 25 | fallbackRegion?: string |
| 26 | ): { |
| 27 | bucketName: string |
| 28 | region: string |
| 29 | objectKey: string |
| 30 | } { |
| 31 | try { |
| 32 | const url = new URL(s3Uri) |
| 33 | const hostname = url.hostname |
| 34 | const normalizedPath = url.pathname.startsWith('/') ? url.pathname.slice(1) : url.pathname |
| 35 | |
| 36 | const virtualHostedDualstackMatch = hostname.match( |
| 37 | /^(.+)\.s3\.dualstack\.([^.]+)\.amazonaws\.com(?:\.cn)?$/ |
| 38 | ) |
| 39 | const virtualHostedRegionalMatch = hostname.match( |
| 40 | /^(.+)\.s3[.-]([^.]+)\.amazonaws\.com(?:\.cn)?$/ |
| 41 | ) |
| 42 | const virtualHostedGlobalMatch = hostname.match(/^(.+)\.s3\.amazonaws\.com(?:\.cn)?$/) |
| 43 | |
| 44 | const pathStyleDualstackMatch = hostname.match( |
| 45 | /^s3\.dualstack\.([^.]+)\.amazonaws\.com(?:\.cn)?$/ |
| 46 | ) |
| 47 | const pathStyleRegionalMatch = hostname.match(/^s3[.-]([^.]+)\.amazonaws\.com(?:\.cn)?$/) |
| 48 | const pathStyleGlobalMatch = hostname.match(/^s3\.amazonaws\.com(?:\.cn)?$/) |
| 49 | |
| 50 | const isPathStyleHost = Boolean( |
| 51 | pathStyleDualstackMatch || pathStyleRegionalMatch || pathStyleGlobalMatch |
| 52 | ) |
| 53 | |
| 54 | const firstSlashIndex = normalizedPath.indexOf('/') |
| 55 | const pathStyleBucketName = |
| 56 | firstSlashIndex === -1 ? normalizedPath : normalizedPath.slice(0, firstSlashIndex) |
| 57 | const pathStyleObjectKey = |
| 58 | firstSlashIndex === -1 ? '' : normalizedPath.slice(firstSlashIndex + 1) |
| 59 | |
| 60 | const bucketName = isPathStyleHost |
| 61 | ? pathStyleBucketName |
| 62 | : (virtualHostedDualstackMatch?.[1] ?? |
| 63 | virtualHostedRegionalMatch?.[1] ?? |
| 64 | virtualHostedGlobalMatch?.[1] ?? |
| 65 | '') |
| 66 | |
| 67 | const rawObjectKey = isPathStyleHost ? pathStyleObjectKey : normalizedPath |
| 68 | const objectKey = (() => { |
| 69 | try { |
| 70 | return decodeURIComponent(rawObjectKey) |
| 71 | } catch { |
| 72 | return rawObjectKey |
| 73 | } |
| 74 | })() |
| 75 | |
| 76 | const normalizedFallbackRegion = fallbackRegion?.trim() |
| 77 | const regionFromHost = |
| 78 | virtualHostedDualstackMatch?.[2] ?? |
| 79 | virtualHostedRegionalMatch?.[2] ?? |
| 80 | pathStyleDualstackMatch?.[1] ?? |