diff --git a/weed/s3api/auth_signature_v4.go b/weed/s3api/auth_signature_v4.go index a55075afe..24ef18a41 100644 --- a/weed/s3api/auth_signature_v4.go +++ b/weed/s3api/auth_signature_v4.go @@ -618,8 +618,15 @@ func extractHostHeader(r *http.Request) string { if forwardedPort == "" { forwardedPort = port } - // If forwardedHost explicitly included a port, always keep it (even if default) - extractedHost = net.JoinHostPort(extractedHost, forwardedPort) + + if !isDefaultPort(scheme, forwardedPort) { + extractedHost = net.JoinHostPort(extractedHost, forwardedPort) + } else { + // No port to add, but IPv6 addresses need brackets for canonical host header + if strings.Contains(extractedHost, ":") && !strings.HasPrefix(extractedHost, "[") { + extractedHost = "[" + extractedHost + "]" + } + } } else { // forwardedHost did not contain a port (e.g., "example.com" or "::1" or "[::1]") // Use forwardedPort if provided and non-default @@ -645,9 +652,16 @@ func extractHostHeader(r *http.Request) string { } h, port, err := net.SplitHostPort(host) if err != nil { + // Handle bare IPv6 address without port + if strings.Contains(host, ":") && !strings.HasPrefix(host, "[") { + return "[" + host + "]" + } return host } if isDefaultPort(scheme, port) { + if strings.Contains(h, ":") { // is IPv6 + return "[" + h + "]" + } return h } return host diff --git a/weed/s3api/auth_signature_v4_test.go b/weed/s3api/auth_signature_v4_test.go index 16f3840c0..6850e9d2b 100644 --- a/weed/s3api/auth_signature_v4_test.go +++ b/weed/s3api/auth_signature_v4_test.go @@ -216,12 +216,12 @@ func TestExtractHostHeader(t *testing.T) { expected: "[2001:db8::1]:8080", }, { - name: "IPv6 full address with brackets and port", + name: "IPv6 full address with brackets and default port (should strip port)", hostHeader: "backend:8333", forwardedHost: "[2001:db8:85a3::8a2e:370:7334]:443", forwardedPort: "443", forwardedProto: "https", - expected: "[2001:db8:85a3::8a2e:370:7334]:443", + expected: "[2001:db8:85a3::8a2e:370:7334]", }, { name: "IPv4-mapped IPv6 address without brackets, should add brackets with port", diff --git a/weed/s3api/auto_signature_v4_test.go b/weed/s3api/auto_signature_v4_test.go index b1c6259c8..ba1bf25ce 100644 --- a/weed/s3api/auto_signature_v4_test.go +++ b/weed/s3api/auto_signature_v4_test.go @@ -409,7 +409,7 @@ func TestSignatureV4WithoutProxy(t *testing.T) { name: "HTTPS with non-standard port", host: "backend:8333", proto: "https", - expectedHost: "backend:8443", + expectedHost: "backend:8333", }, { name: "HTTP with standard port", @@ -445,31 +445,31 @@ func TestSignatureV4WithoutProxy(t *testing.T) { name: "IPv6 HTTPS with non-standard port", host: "[::1]:8333", proto: "https", - expectedHost: "[::1]:8443", + expectedHost: "[::1]:8333", }, { name: "IPv6 HTTP with standard port", host: "[::1]:80", proto: "http", - expectedHost: "::1", + expectedHost: "[::1]", }, { name: "IPv6 HTTPS with standard port", host: "[::1]:443", proto: "https", - expectedHost: "::1", + expectedHost: "[::1]", }, { name: "IPv6 HTTP without port", host: "::1", proto: "http", - expectedHost: "::1", + expectedHost: "[::1]", }, { name: "IPv6 HTTPS without port", host: "::1", proto: "https", - expectedHost: "::1", + expectedHost: "[::1]", }, } @@ -491,6 +491,12 @@ func TestSignatureV4WithoutProxy(t *testing.T) { // Set forwarded headers r.Header.Set("Host", tt.host) + + // First, verify that extractHostHeader returns the expected value + extractedHost := extractHostHeader(r) + if extractedHost != tt.expectedHost { + t.Errorf("extractHostHeader() = %q, want %q", extractedHost, tt.expectedHost) + } // Sign the request with the expected host header // We need to temporarily modify the Host header for signing