Skip to content

Commit e5610be

Browse files
committed
Fix extractHeaders to handle duplicate header names
The previous implementation used Seq.partition to remove ALL headers with the matching name at once. This commit changes its behavior to accept duplicate header names (particularly important for some special headers such as `Set-Cookie`).
1 parent 33568db commit e5610be

File tree

2 files changed

+9
-7
lines changed

2 files changed

+9
-7
lines changed

servant-client/test/Servant/ClientTestUtils.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ type TestHeaders = '[Header "X-Example1" Int, Header "X-Example2" String]
138138

139139
type TestSetCookieHeaders = '[Header "Set-Cookie" String, Header "Set-Cookie" String]
140140

141-
-- | AsHeaders instance for extracting two Set-Cookie headers
141+
-- | AsHeaders instance for extracting two headers (Required by the MultiVerbSetCookie test)
142142
-- Returns: (body, (cookie1, cookie2))
143143
instance AsHeaders '[a, b] c (c, (a, b)) where
144144
toHeaders (body, (h1, h2)) = (I h1 :* I h2 :* Nil, body)

servant/src/Servant/API/MultiVerb.hs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -187,14 +187,16 @@ instance
187187
constructHeader @h x
188188
<> constructHeaders @headers xs
189189

190-
-- NOTE: should we concatenate all the matching headers instead of just taking the first one?
190+
-- This implementation retrieves the *first* header with matching name.
191+
-- It leaves other instances of the same header intact for subsequent extraction, which allows
192+
-- multiple headers with the same name to be extracted (e.g. Set-Cookie).
191193
extractHeaders headers = do
192194
let name' = headerName @name
193-
(headers0, headers1) = Seq.partition (\(h, _) -> h == name') headers
194-
x <- case headers0 of
195-
Seq.Empty -> empty
196-
((_, h) :<| _) -> either (const empty) pure (parseHeader h)
197-
xs <- extractHeaders @headers headers1
195+
idx <- Seq.findIndexL (\(h, _) -> h == name') headers
196+
let (_, val) = Seq.index headers idx
197+
headers' = Seq.deleteAt idx headers
198+
x <- either (const empty) pure (parseHeader val)
199+
xs <- extractHeaders @headers headers'
198200
pure (I x :* xs)
199201

200202
class ServantHeader h (name :: Symbol) x | h -> name x where

0 commit comments

Comments
 (0)