From 42fc6caecec1088f2c64bbee50570ffd32ce5722 Mon Sep 17 00:00:00 2001 From: Hashim1999164 <64767361+Hashim1999164@users.noreply.github.com> Date: Thu, 20 Aug 2026 00:21:45 +0500 Subject: [PATCH 1/2] Make HTTPFields hashing consistent with equality. --- Sources/HTTPTypes/HTTPFields.swift | 18 +++++++++++++++++- Tests/HTTPTypesTests/HTTPTypesTests.swift | 6 +----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Sources/HTTPTypes/HTTPFields.swift b/Sources/HTTPTypes/HTTPFields.swift index 905aacc..f9c243d 100644 --- a/Sources/HTTPTypes/HTTPFields.swift +++ b/Sources/HTTPTypes/HTTPFields.swift @@ -293,9 +293,25 @@ extension HTTPFields: Equatable { extension HTTPFields: Hashable { public func hash(into hasher: inout Hasher) { + // Equality ignores the order of differently named fields, so hashing + // must too. Combine each name's sequence (order of same-named fields + // still matters), then mix those group hashes commutatively. + var grouped = [String: Hasher]() for field in self.fields { - hasher.combine(field) + let key = field.name.canonicalName + var nameHasher = grouped[key] ?? Hasher() + nameHasher.combine(field) + grouped[key] = nameHasher } + var combined = 0 + for (name, nameHasher) in grouped { + var entryHasher = Hasher() + entryHasher.combine(name) + entryHasher.combine(nameHasher.finalize()) + combined ^= entryHasher.finalize() + } + hasher.combine(combined) + hasher.combine(self.fields.count) } } diff --git a/Tests/HTTPTypesTests/HTTPTypesTests.swift b/Tests/HTTPTypesTests/HTTPTypesTests.swift index 3ee9e87..5423762 100644 --- a/Tests/HTTPTypesTests/HTTPTypesTests.swift +++ b/Tests/HTTPTypesTests/HTTPTypesTests.swift @@ -279,11 +279,7 @@ extension HTTPField.Name { .acceptEncoding: "gzip", ] #expect(fields1 == fields2) - - // Equal values must therefore hash equally. - withKnownIssue("HTTPFields.hash(into:) is order sensitive while == is not") { - #expect(fields1.hashValue == fields2.hashValue) - } + #expect(fields1.hashValue == fields2.hashValue) } @Test func sendable() { From 7085c1a05678d819ba33008eee059ca04f715453 Mon Sep 17 00:00:00 2001 From: Hashim1999164 <64767361+Hashim1999164@users.noreply.github.com> Date: Fri, 28 Aug 2026 23:04:12 +0500 Subject: [PATCH 2/2] Simplify HTTPFields hashing using grouped default Hasher --- Sources/HTTPTypes/HTTPFields.swift | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Sources/HTTPTypes/HTTPFields.swift b/Sources/HTTPTypes/HTTPFields.swift index f9c243d..eb5cd3c 100644 --- a/Sources/HTTPTypes/HTTPFields.swift +++ b/Sources/HTTPTypes/HTTPFields.swift @@ -299,16 +299,12 @@ extension HTTPFields: Hashable { var grouped = [String: Hasher]() for field in self.fields { let key = field.name.canonicalName - var nameHasher = grouped[key] ?? Hasher() - nameHasher.combine(field) - grouped[key] = nameHasher + grouped[key, default: Hasher()].combine(field) } var combined = 0 - for (name, nameHasher) in grouped { - var entryHasher = Hasher() - entryHasher.combine(name) - entryHasher.combine(nameHasher.finalize()) - combined ^= entryHasher.finalize() + for nameHasher in grouped.values { + // HTTPField already hashes its name, so XOR the per-name sequence hashes directly. + combined ^= nameHasher.finalize() } hasher.combine(combined) hasher.combine(self.fields.count)