* Parses and validates a custom S3-compatible endpoint string. * * Accepts a full origin such as `https://accountid.r2.cloudflarestorage.com` or * `http://localhost:9000`. Trailing slashes are stripped. Throws when the value * is not a valid URL, carries a path/query/fragment beyond `/` (which w
(raw: string)
| 148 | * the SigV4 canonical Host byte-identical to the wire Host. |
| 149 | */ |
| 150 | function parseEndpoint(raw: string): S3Endpoint { |
| 151 | let url: URL |
| 152 | try { |
| 153 | url = new URL(raw) |
| 154 | } catch { |
| 155 | throw new Error('Endpoint must be a valid URL, e.g. https://accountid.r2.cloudflarestorage.com') |
| 156 | } |
| 157 | |
| 158 | if (url.protocol !== 'https:' && url.protocol !== 'http:') { |
| 159 | throw new Error('Endpoint must use http:// or https://') |
| 160 | } |
| 161 | const scheme = url.protocol === 'https:' ? 'https' : 'http' |
| 162 | |
| 163 | if (url.username || url.password) { |
| 164 | throw new Error('Endpoint must not contain credentials') |
| 165 | } |
| 166 | if (url.search || url.hash) { |
| 167 | throw new Error('Endpoint must not contain a query string or fragment') |
| 168 | } |
| 169 | const path = url.pathname.replace(/\/+$/, '') |
| 170 | if (path !== '') { |
| 171 | throw new Error('Endpoint must not contain a path — provide only the host, e.g. https://host') |
| 172 | } |
| 173 | |
| 174 | const host = url.hostname |
| 175 | if (!host) throw new Error('Endpoint is missing a host') |
| 176 | if (scheme === 'http' && !(isLoopbackHost(host) && !isHosted)) { |
| 177 | throw new Error( |
| 178 | 'Plain http:// endpoints are only allowed for localhost on self-hosted deployments — use https:// otherwise' |
| 179 | ) |
| 180 | } |
| 181 | |
| 182 | const defaultPort = scheme === 'https' ? '443' : '80' |
| 183 | const port = url.port && url.port !== defaultPort ? url.port : '' |
| 184 | const hostHeader = port ? `${host}:${port}` : host |
| 185 | |
| 186 | return { scheme, host, hostHeader } |
| 187 | } |
| 188 | |
| 189 | /** |
| 190 | * Resolves AWS credentials and the target bucket from the connector's |
no test coverage detected