You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							263 lines
						
					
					
						
							7.4 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							263 lines
						
					
					
						
							7.4 KiB
						
					
					
				| package s3api | |
| 
 | |
| import ( | |
| 	"net/http" | |
| 	"testing" | |
| ) | |
| 
 | |
| func TestBuildPathWithForwardedPrefix(t *testing.T) { | |
| 	tests := []struct { | |
| 		name            string | |
| 		forwardedPrefix string | |
| 		urlPath         string | |
| 		expected        string | |
| 	}{ | |
| 		{ | |
| 			name:            "empty prefix returns urlPath", | |
| 			forwardedPrefix: "", | |
| 			urlPath:         "/bucket/obj", | |
| 			expected:        "/bucket/obj", | |
| 		}, | |
| 		{ | |
| 			name:            "prefix without trailing slash", | |
| 			forwardedPrefix: "/storage", | |
| 			urlPath:         "/bucket/obj", | |
| 			expected:        "/storage/bucket/obj", | |
| 		}, | |
| 		{ | |
| 			name:            "prefix with trailing slash", | |
| 			forwardedPrefix: "/storage/", | |
| 			urlPath:         "/bucket/obj", | |
| 			expected:        "/storage/bucket/obj", | |
| 		}, | |
| 		{ | |
| 			name:            "prefix without leading slash", | |
| 			forwardedPrefix: "storage", | |
| 			urlPath:         "/bucket/obj", | |
| 			expected:        "/storage/bucket/obj", | |
| 		}, | |
| 		{ | |
| 			name:            "prefix without leading slash and with trailing slash", | |
| 			forwardedPrefix: "storage/", | |
| 			urlPath:         "/bucket/obj", | |
| 			expected:        "/storage/bucket/obj", | |
| 		}, | |
| 		{ | |
| 			name:            "preserve double slashes in key", | |
| 			forwardedPrefix: "/storage", | |
| 			urlPath:         "/bucket//obj", | |
| 			expected:        "/storage/bucket//obj", | |
| 		}, | |
| 		{ | |
| 			name:            "preserve trailing slash in urlPath", | |
| 			forwardedPrefix: "/storage", | |
| 			urlPath:         "/bucket/folder/", | |
| 			expected:        "/storage/bucket/folder/", | |
| 		}, | |
| 		{ | |
| 			name:            "preserve trailing slash with prefix having trailing slash", | |
| 			forwardedPrefix: "/storage/", | |
| 			urlPath:         "/bucket/folder/", | |
| 			expected:        "/storage/bucket/folder/", | |
| 		}, | |
| 		{ | |
| 			name:            "root path", | |
| 			forwardedPrefix: "/storage", | |
| 			urlPath:         "/", | |
| 			expected:        "/storage/", | |
| 		}, | |
| 		{ | |
| 			name:            "complex key with multiple slashes", | |
| 			forwardedPrefix: "/api/v1", | |
| 			urlPath:         "/bucket/path//with///slashes", | |
| 			expected:        "/api/v1/bucket/path//with///slashes", | |
| 		}, | |
| 		{ | |
| 			name:            "urlPath without leading slash", | |
| 			forwardedPrefix: "/storage", | |
| 			urlPath:         "bucket/obj", | |
| 			expected:        "/storage/bucket/obj", | |
| 		}, | |
| 	} | |
| 
 | |
| 	for _, tt := range tests { | |
| 		t.Run(tt.name, func(t *testing.T) { | |
| 			result := buildPathWithForwardedPrefix(tt.forwardedPrefix, tt.urlPath) | |
| 			if result != tt.expected { | |
| 				t.Errorf("buildPathWithForwardedPrefix(%q, %q) = %q, want %q", | |
| 					tt.forwardedPrefix, tt.urlPath, result, tt.expected) | |
| 			} | |
| 		}) | |
| 	} | |
| } | |
| 
 | |
| // TestExtractHostHeader tests the extractHostHeader function with various scenarios | |
| func TestExtractHostHeader(t *testing.T) { | |
| 	tests := []struct { | |
| 		name           string | |
| 		hostHeader     string | |
| 		forwardedHost  string | |
| 		forwardedPort  string | |
| 		forwardedProto string | |
| 		expected       string | |
| 	}{ | |
| 		{ | |
| 			name:           "basic host without forwarding", | |
| 			hostHeader:     "example.com", | |
| 			forwardedHost:  "", | |
| 			forwardedPort:  "", | |
| 			forwardedProto: "", | |
| 			expected:       "example.com", | |
| 		}, | |
| 		{ | |
| 			name:           "host with port without forwarding", | |
| 			hostHeader:     "example.com:8080", | |
| 			forwardedHost:  "", | |
| 			forwardedPort:  "", | |
| 			forwardedProto: "", | |
| 			expected:       "example.com:8080", | |
| 		}, | |
| 		{ | |
| 			name:           "X-Forwarded-Host without port", | |
| 			hostHeader:     "backend:8333", | |
| 			forwardedHost:  "example.com", | |
| 			forwardedPort:  "", | |
| 			forwardedProto: "", | |
| 			expected:       "example.com", | |
| 		}, | |
| 		{ | |
| 			name:           "X-Forwarded-Host with X-Forwarded-Port (HTTP non-standard)", | |
| 			hostHeader:     "backend:8333", | |
| 			forwardedHost:  "example.com", | |
| 			forwardedPort:  "8080", | |
| 			forwardedProto: "http", | |
| 			expected:       "example.com:8080", | |
| 		}, | |
| 		{ | |
| 			name:           "X-Forwarded-Host with X-Forwarded-Port (HTTPS non-standard)", | |
| 			hostHeader:     "backend:8333", | |
| 			forwardedHost:  "example.com", | |
| 			forwardedPort:  "8443", | |
| 			forwardedProto: "https", | |
| 			expected:       "example.com:8443", | |
| 		}, | |
| 		{ | |
| 			name:           "X-Forwarded-Host with X-Forwarded-Port (HTTP standard port 80)", | |
| 			hostHeader:     "backend:8333", | |
| 			forwardedHost:  "example.com", | |
| 			forwardedPort:  "80", | |
| 			forwardedProto: "http", | |
| 			expected:       "example.com", | |
| 		}, | |
| 		{ | |
| 			name:           "X-Forwarded-Host with X-Forwarded-Port (HTTPS standard port 443)", | |
| 			hostHeader:     "backend:8333", | |
| 			forwardedHost:  "example.com", | |
| 			forwardedPort:  "443", | |
| 			forwardedProto: "https", | |
| 			expected:       "example.com", | |
| 		}, | |
| 		// Issue #6649: X-Forwarded-Host already contains port (Traefik/HAProxy style) | |
| 		{ | |
| 			name:           "X-Forwarded-Host with port already included (should not add port again)", | |
| 			hostHeader:     "backend:8333", | |
| 			forwardedHost:  "127.0.0.1:8433", | |
| 			forwardedPort:  "8433", | |
| 			forwardedProto: "https", | |
| 			expected:       "127.0.0.1:8433", | |
| 		}, | |
| 		{ | |
| 			name:           "X-Forwarded-Host with port, no X-Forwarded-Port header", | |
| 			hostHeader:     "backend:8333", | |
| 			forwardedHost:  "example.com:9000", | |
| 			forwardedPort:  "", | |
| 			forwardedProto: "http", | |
| 			expected:       "example.com:9000", | |
| 		}, | |
| 		// IPv6 test cases | |
| 		{ | |
| 			name:           "IPv6 address with brackets and port in X-Forwarded-Host", | |
| 			hostHeader:     "backend:8333", | |
| 			forwardedHost:  "[::1]:8080", | |
| 			forwardedPort:  "8080", | |
| 			forwardedProto: "http", | |
| 			expected:       "[::1]:8080", | |
| 		}, | |
| 		{ | |
| 			name:           "IPv6 address without brackets, should add brackets with port", | |
| 			hostHeader:     "backend:8333", | |
| 			forwardedHost:  "::1", | |
| 			forwardedPort:  "8080", | |
| 			forwardedProto: "http", | |
| 			expected:       "[::1]:8080", | |
| 		}, | |
| 		{ | |
| 			name:           "IPv6 address without brackets and standard port, should return bracketed IPv6", | |
| 			hostHeader:     "backend:8333", | |
| 			forwardedHost:  "::1", | |
| 			forwardedPort:  "80", | |
| 			forwardedProto: "http", | |
| 			expected:       "[::1]", | |
| 		}, | |
| 		{ | |
| 			name:           "IPv6 address without brackets and standard HTTPS port, should return bracketed IPv6", | |
| 			hostHeader:     "backend:8333", | |
| 			forwardedHost:  "2001:db8::1", | |
| 			forwardedPort:  "443", | |
| 			forwardedProto: "https", | |
| 			expected:       "[2001:db8::1]", | |
| 		}, | |
| 		{ | |
| 			name:           "IPv6 address with brackets but no port, should add port", | |
| 			hostHeader:     "backend:8333", | |
| 			forwardedHost:  "[2001:db8::1]", | |
| 			forwardedPort:  "8080", | |
| 			forwardedProto: "http", | |
| 			expected:       "[2001:db8::1]:8080", | |
| 		}, | |
| 		{ | |
| 			name:           "IPv6 full address with brackets and port", | |
| 			hostHeader:     "backend:8333", | |
| 			forwardedHost:  "[2001:db8:85a3::8a2e:370:7334]:443", | |
| 			forwardedPort:  "443", | |
| 			forwardedProto: "https", | |
| 			expected:       "[2001:db8:85a3::8a2e:370:7334]:443", | |
| 		}, | |
| 		{ | |
| 			name:           "IPv4-mapped IPv6 address without brackets, should add brackets with port", | |
| 			hostHeader:     "backend:8333", | |
| 			forwardedHost:  "::ffff:127.0.0.1", | |
| 			forwardedPort:  "8080", | |
| 			forwardedProto: "http", | |
| 			expected:       "[::ffff:127.0.0.1]:8080", | |
| 		}, | |
| 	} | |
| 
 | |
| 	for _, tt := range tests { | |
| 		t.Run(tt.name, func(t *testing.T) { | |
| 			// Create a mock request | |
| 			req, err := http.NewRequest("GET", "http://"+tt.hostHeader+"/bucket/object", nil) | |
| 			if err != nil { | |
| 				t.Fatalf("Failed to create request: %v", err) | |
| 			} | |
| 
 | |
| 			// Set headers | |
| 			req.Host = tt.hostHeader | |
| 			if tt.forwardedHost != "" { | |
| 				req.Header.Set("X-Forwarded-Host", tt.forwardedHost) | |
| 			} | |
| 			if tt.forwardedPort != "" { | |
| 				req.Header.Set("X-Forwarded-Port", tt.forwardedPort) | |
| 			} | |
| 			if tt.forwardedProto != "" { | |
| 				req.Header.Set("X-Forwarded-Proto", tt.forwardedProto) | |
| 			} | |
| 
 | |
| 			// Test the function | |
| 			result := extractHostHeader(req) | |
| 			if result != tt.expected { | |
| 				t.Errorf("extractHostHeader() = %q, want %q", result, tt.expected) | |
| 			} | |
| 		}) | |
| 	} | |
| }
 |