replica_url: provider detection fails when the endpoint includes an explicit port
Summary
Every Is*Endpoint provider check fails when the endpoint includes an explicit port. extractEndpointHost returns url.URL.Host, which keeps the port, and the matchers then compare that against a bare hostname.
Providers fall back to generic S3 behavior, silently, for a configuration that is otherwise valid.
Reproduction
litestream.IsHetznerEndpoint("https://fsn1.your-objectstorage.com") // true
litestream.IsHetznerEndpoint("https://fsn1.your-objectstorage.com:443") // falseRun against main:
| Provider | no port | explicit :443 |
|---|---|---|
| Hetzner | true | false |
| Tigris | true | false |
| DigitalOcean | true | false |
| Backblaze | true | false |
| GCS | true | true |
GCS is unaffected because IsGoogleCloudStorageEndpoint does its own parsing rather than relying on the shared suffix match.
Cause
replica_url.go:
func extractEndpointHost(endpoint string) string {
...
if strings.HasPrefix(endpoint, "http://") || strings.HasPrefix(endpoint, "https://") {
if u, err := url.Parse(endpoint); err == nil && u.Host != "" {
return u.Host // includes the port
}
}
return endpoint
}Callers then do exact or suffix matches against a hostname:
return strings.HasSuffix(host, ".your-objectstorage.com")
return host == "fly.storage.tigris.dev" || host == "t3.storage.dev"u.Host is fsn1.your-objectstorage.com:443; u.Hostname() is fsn1.your-objectstorage.com.
Impact
Today this costs provider-specific defaults: Tigris consistency headers, upload tuning, the GCS accept-encoding workaround, and similar. Quiet and recoverable.
It gets sharper with #1509, which routes Hetzner lease behavior — ETag quoting and conditional DELETE — through IsHetznerEndpoint. A Hetzner user who writes an explicit port would take the generic path that PR exists to avoid, on a code path whose job is mutual exclusion between writers.
This bug predates #1509. Filing separately so that PR is not blocked on it.
Fix
Use u.Hostname() in extractEndpointHost, which strips the port, and add table tests covering explicit ports for each provider matcher. Worth checking whether any caller depends on the port being present before changing it.
Source: benbjohnson/litestream