@@ -24,7 +24,9 @@ module Servant.API.ResponseHeaders
2424 , ResponseHeader (.. )
2525 , AddHeader
2626 , addHeader
27+ , addHeader'
2728 , noHeader
29+ , noHeader'
2830 , HasResponseHeader
2931 , lookupResponseHeader
3032 , BuildHeadersTo (buildHeadersTo )
@@ -37,7 +39,7 @@ module Servant.API.ResponseHeaders
3739import Control.DeepSeq
3840 (NFData (.. ))
3941import Data.ByteString.Char8 as BS
40- (ByteString , init , pack , unlines )
42+ (ByteString , pack )
4143import qualified Data.CaseInsensitive as CI
4244import qualified Data.List as L
4345import Data.Proxy
@@ -52,7 +54,9 @@ import Web.HttpApiData
5254import Prelude ()
5355import Prelude.Compat
5456import Servant.API.Header
55- (Header )
57+ (Header' )
58+ import Servant.API.Modifiers
59+ (Optional , Strict )
5660import Servant.API.UVerb.Union
5761import qualified Data.SOP.BasicFunctors as SOP
5862import qualified Data.SOP.NS as SOP
@@ -81,19 +85,19 @@ instance NFData a => NFData (ResponseHeader sym a) where
8185
8286data HList a where
8387 HNil :: HList '[]
84- HCons :: ResponseHeader h x -> HList xs -> HList (Header h x ': xs )
88+ HCons :: ResponseHeader h x -> HList xs -> HList (Header' mods h x ': xs )
8589
8690class NFDataHList xs where rnfHList :: HList xs -> ()
8791instance NFDataHList '[] where rnfHList HNil = ()
88- instance (y ~ Header h x , NFData x , NFDataHList xs ) => NFDataHList (y ': xs ) where
92+ instance (y ~ Header' mods h x , NFData x , NFDataHList xs ) => NFDataHList (y ': xs ) where
8993 rnfHList (HCons h xs) = rnf h `seq` rnfHList xs
9094
9195instance NFDataHList xs => NFData (HList xs ) where
9296 rnf = rnfHList
9397
9498type family HeaderValMap (f :: * -> * ) (xs :: [* ]) where
9599 HeaderValMap f '[] = '[]
96- HeaderValMap f (Header h x ': xs ) = Header h (f x ) ': HeaderValMap f xs
100+ HeaderValMap f (Header' mods h x ': xs ) = Header' mods h (f x ) ': HeaderValMap f xs
97101
98102
99103class BuildHeadersTo hs where
@@ -105,7 +109,7 @@ instance {-# OVERLAPPING #-} BuildHeadersTo '[] where
105109-- The current implementation does not manipulate HTTP header field lines in any way,
106110-- like merging field lines with the same field name in a single line.
107111instance {-# OVERLAPPABLE #-} ( FromHttpApiData v , BuildHeadersTo xs , KnownSymbol h )
108- => BuildHeadersTo (Header h v ': xs ) where
112+ => BuildHeadersTo (Header' mods h v ': xs ) where
109113 buildHeadersTo headers = case L. find wantedHeader headers of
110114 Nothing -> MissingHeader `HCons ` buildHeadersTo headers
111115 Just header@ (_, val) -> case parseHeader val of
@@ -130,7 +134,7 @@ instance GetHeadersFromHList '[] where
130134 getHeadersFromHList _ = []
131135
132136instance (KnownSymbol h , ToHttpApiData x , GetHeadersFromHList xs )
133- => GetHeadersFromHList (Header h x ': xs )
137+ => GetHeadersFromHList (Header' mods h x ': xs )
134138 where
135139 getHeadersFromHList hdrs = case hdrs of
136140 Header val `HCons ` rest -> (headerName , toHeader val) : getHeadersFromHList rest
@@ -151,42 +155,42 @@ instance GetHeaders' '[] where
151155 getHeaders' _ = []
152156
153157instance (KnownSymbol h , GetHeadersFromHList rest , ToHttpApiData v )
154- => GetHeaders' (Header h v ': rest )
158+ => GetHeaders' (Header' mods h v ': rest )
155159 where
156160 getHeaders' hs = getHeadersFromHList $ getHeadersHList hs
157161
158162-- * Adding headers
159163
160164-- We need all these fundeps to save type inference
161- class AddHeader h v orig new
162- | h v orig -> new , new -> h , new -> v , new -> orig where
165+ class AddHeader ( mods :: [ * ]) h v orig new
166+ | mods h v orig -> new , new -> mods , new -> h , new -> v , new -> orig where
163167 addOptionalHeader :: ResponseHeader h v -> orig -> new -- ^ N.B.: The same header can't be added multiple times
164168
165169-- In this instance, we add a Header on top of something that is already decorated with some headers
166170instance {-# OVERLAPPING #-} ( KnownSymbol h , ToHttpApiData v )
167- => AddHeader h v (Headers (fst ': rest ) a ) (Headers (Header h v ': fst ': rest ) a ) where
171+ => AddHeader mods h v (Headers (fst ': rest ) a ) (Headers (Header' mods h v ': fst ': rest ) a ) where
168172 addOptionalHeader hdr (Headers resp heads) = Headers resp (HCons hdr heads)
169173
170174-- In this instance, 'a' parameter is decorated with a Header.
171- instance {-# OVERLAPPABLE #-} ( KnownSymbol h , ToHttpApiData v , new ~ Headers '[Header h v ] a )
172- => AddHeader h v a new where
175+ instance {-# OVERLAPPABLE #-} ( KnownSymbol h , ToHttpApiData v , new ~ Headers '[Header' mods h v ] a )
176+ => AddHeader mods h v a new where
173177 addOptionalHeader hdr resp = Headers resp (HCons hdr HNil )
174178
175179-- Instances to decorate all responses in a 'Union' with headers. The functional
176180-- dependencies force us to consider singleton lists as the base case in the
177181-- recursion (it is impossible to determine h and v otherwise from old / new
178182-- responses if the list is empty).
179- instance (AddHeader h v old new ) => AddHeader h v (Union '[old ]) (Union '[new ]) where
183+ instance (AddHeader mods h v old new ) => AddHeader mods h v (Union '[old ]) (Union '[new ]) where
180184 addOptionalHeader hdr resp =
181185 SOP. Z $ SOP. I $ addOptionalHeader hdr $ SOP. unI $ SOP. unZ $ resp
182186
183187instance
184- ( AddHeader h v old new , AddHeader h v (Union oldrest ) (Union newrest )
188+ ( AddHeader mods h v old new , AddHeader mods h v (Union oldrest ) (Union newrest )
185189 -- This ensures that the remainder of the response list is _not_ empty
186190 -- It is necessary to prevent the two instances for union types from
187191 -- overlapping.
188192 , oldrest ~ (a ': as ), newrest ~ (b ': bs ))
189- => AddHeader h v (Union (old ': (a ': as ))) (Union (new ': (b ': bs ))) where
193+ => AddHeader mods h v (Union (old ': (a ': as ))) (Union (new ': (b ': bs ))) where
190194 addOptionalHeader hdr resp = case resp of
191195 SOP. Z (SOP. I rHead) -> SOP. Z $ SOP. I $ addOptionalHeader hdr rHead
192196 SOP. S rOthers -> SOP. S $ addOptionalHeader hdr rOthers
@@ -211,21 +215,29 @@ instance
211215-- Note that while in your handlers type annotations are not required, since
212216-- the type can be inferred from the API type, in other cases you may find
213217-- yourself needing to add annotations.
214- addHeader :: AddHeader h v orig new => v -> orig -> new
218+ addHeader :: AddHeader '[ Optional , Strict ] h v orig new => v -> orig -> new
215219addHeader = addOptionalHeader . Header
216220
221+ -- | Same as 'addHeader' but works with `Header'`, so it's possible to use any @mods@.
222+ addHeader' :: AddHeader mods h v orig new => v -> orig -> new
223+ addHeader' = addOptionalHeader . Header
224+
217225-- | Deliberately do not add a header to a value.
218226--
219227-- >>> let example1 = noHeader "hi" :: Headers '[Header "someheader" Int] String
220228-- >>> getHeaders example1
221229-- []
222- noHeader :: AddHeader h v orig new => orig -> new
230+ noHeader :: AddHeader '[ Optional , Strict ] h v orig new => orig -> new
223231noHeader = addOptionalHeader MissingHeader
224232
233+ -- | Same as 'noHeader' but works with `Header'`, so it's possible to use any @mods@.
234+ noHeader' :: AddHeader mods h v orig new => orig -> new
235+ noHeader' = addOptionalHeader MissingHeader
236+
225237class HasResponseHeader h a headers where
226238 hlistLookupHeader :: HList headers -> ResponseHeader h a
227239
228- instance {-# OVERLAPPING #-} HasResponseHeader h a (Header h a ': rest ) where
240+ instance {-# OVERLAPPING #-} HasResponseHeader h a (Header' mods h a ': rest ) where
229241 hlistLookupHeader (HCons ha _) = ha
230242
231243instance {-# OVERLAPPABLE #-} (HasResponseHeader h a rest ) => HasResponseHeader h a (first ': rest ) where
0 commit comments