on master help sign in
wai-extra: Add stripHeader/s Middleware component

This Middleware component allows specific HTTP response headers (chosen by header name) to be dropped if the Wai Request matches a predicate. Tests included.

commita6e0c8f4c09eca08a5067080f5623c5c5fd259f7 authorErik de Castro Lopo <erikd@mega-nerd.com> dateFri 3 Jul 2015, 11:58 (11 years ago) parentbffaa1a
4 files changed+115−7
wai-extra/Network/Wai/Middleware/AddHeaders.hs+11−7 wai-extra/Network/Wai/Middleware/StripHeaders.hs+43 wai-extra/test/Network/Wai/Middleware/StripHeadersSpec.hs+58 wai-extra/wai-extra.cabal+3
wai-extra/Network/Wai/Middleware/AddHeaders.hs+11−7
@@ -3,6 +3,7 @@
33-- Since 3.0.3
44module Network.Wai.Middleware.AddHeaders
55 ( addHeaders
6+ , mapResponseHeader
67 ) where
78
89import Network.HTTP.Types (ResponseHeaders, Header)
@@ -20,11 +21,14 @@ addHeaders :: [(ByteString, ByteString)] -> Middleware
2021
2122addHeaders h app req respond = app req $ respond . addHeaders' (map (first CI.mk) h)
2223
23−mapHeader :: (ResponseHeaders -> ResponseHeaders) -> Response -> Response
24−mapHeader f (ResponseFile s h b1 b2) = ResponseFile s (f h) b1 b2
25−mapHeader f (ResponseBuilder s h b) = ResponseBuilder s (f h) b
26−mapHeader f (ResponseStream s h b) = ResponseStream s (f h) b
27−mapHeader _ r@(ResponseRaw _ _) = r
28−
2924addHeaders' :: [Header] -> Response -> Response
30−addHeaders' h = mapHeader (\hs -> h ++ hs)
25+addHeaders' h = mapResponseHeader (\hs -> h ++ hs)
26+
27+
28+-- | Apply the provided function to the response header list of the Response.
29+mapResponseHeader :: (ResponseHeaders -> ResponseHeaders) -> Response -> Response
30+mapResponseHeader f (ResponseFile s h b1 b2) = ResponseFile s (f h) b1 b2
31+mapResponseHeader f (ResponseBuilder s h b) = ResponseBuilder s (f h) b
32+mapResponseHeader f (ResponseStream s h b) = ResponseStream s (f h) b
33+mapResponseHeader _ r@(ResponseRaw _ _) = r
34+
wai-extra/Network/Wai/Middleware/StripHeaders.hsnew file+43
@@ -0,0 +1,43 @@
1+-- This was written for one specific use case and then generalized.
2+
3+-- The specific use case was a JSON API with a consumer that would choke on the
4+-- "Set-Cookie" response header. The solution was to test for the API's
5+-- `pathInfo` in the Request and if it matched, filter the response headers.
6+
7+-- When using this, care should be taken not to strip out headers that are
8+-- required for correct operation of the client (eg Content-Type).
9+
10+module Network.Wai.Middleware.StripHeaders
11+ ( stripHeader
12+ , stripHeaders
13+ ) where
14+
15+import Network.Wai (Middleware, Request)
16+import Data.ByteString (ByteString)
17+import Network.Wai.Middleware.AddHeaders (mapResponseHeader)
18+
19+import qualified Data.CaseInsensitive as CI
20+
21+
22+-- | If the request satisifes the provided predicate, strip headers matching
23+-- the provided header name.
24+--
25+-- Since 3.0.8
26+
27+stripHeader :: ByteString -> (Request -> Bool) -> Middleware
28+stripHeader h rpred app req respond
29+ | rpred req = app req $ respond . mapResponseHeader (filter (\ hdr -> fst hdr /= CI.mk h))
30+ | otherwise = app req respond
31+
32+
33+-- | If the request satisifes the provided predicate, strip all headers whose
34+-- header name is in the list of provided header names.
35+--
36+-- Since 3.0.8
37+
38+stripHeaders :: [ByteString] -> (Request -> Bool) -> Middleware
39+stripHeaders hs rpred app req respond
40+ | rpred req = do
41+ let hnames = map CI.mk hs
42+ app req $ respond . mapResponseHeader (filter (\ hdr -> fst hdr `notElem` hnames))
43+ | otherwise = app req respond
wai-extra/test/Network/Wai/Middleware/StripHeadersSpec.hsnew file+58
@@ -0,0 +1,58 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+module Network.Wai.Middleware.StripHeadersSpec
3+ ( main
4+ , spec
5+ ) where
6+
7+import Test.Hspec
8+
9+import Network.Wai.Middleware.AddHeaders
10+import Network.Wai.Middleware.StripHeaders
11+
12+import Control.Arrow (first)
13+import Data.ByteString (ByteString)
14+import Data.Monoid ((<>))
15+import Network.HTTP.Types (status200)
16+import Network.Wai
17+import Network.Wai.Test
18+
19+import qualified Data.CaseInsensitive as CI
20+
21+
22+main :: IO ()
23+main = hspec spec
24+
25+
26+spec :: Spec
27+spec = describe "stripHeader" $ do
28+ let host = "example.com"
29+ let ciTestHeaders = map (first CI.mk) testHeaders
30+
31+ it "strips a specific header" $ do
32+ resp1 <- runApp host (addHeaders testHeaders) defaultRequest
33+ resp2 <- runApp host (stripHeader "Foo" (const False) . addHeaders testHeaders) defaultRequest
34+ resp3 <- runApp host (stripHeader "Foo" (const True) . addHeaders testHeaders) defaultRequest
35+
36+ simpleHeaders resp1 `shouldBe` ciTestHeaders
37+ simpleHeaders resp2 `shouldBe` ciTestHeaders
38+ simpleHeaders resp3 `shouldBe` tail ciTestHeaders
39+
40+ it "strips specific set of headers" $ do
41+ resp1 <- runApp host (addHeaders testHeaders) defaultRequest
42+ resp2 <- runApp host (stripHeaders ["Bar", "Foo"] (const False) . addHeaders testHeaders) defaultRequest
43+ resp3 <- runApp host (stripHeaders ["Bar", "Foo"] (const True) . addHeaders testHeaders) defaultRequest
44+
45+ simpleHeaders resp1 `shouldBe` ciTestHeaders
46+ simpleHeaders resp2 `shouldBe` ciTestHeaders
47+ simpleHeaders resp3 `shouldBe` [last ciTestHeaders]
48+
49+
50+testHeaders :: [(ByteString, ByteString)]
51+testHeaders = [("Foo", "fooey"), ("Bar", "barbican"), ("Baz", "bazooka")]
52+
53+
54+runApp :: ByteString -> Middleware -> Request -> IO SResponse
55+runApp host mw req = runSession
56+ (request req { requestHeaderHost = Just $ host <> ":80" }) $ mw app
57+ where
58+ app _ respond = respond $ responseLBS status200 [] ""
wai-extra/wai-extra.cabal+3
@@ -130,6 +130,7 @@ Library
130130 Network.Wai.Middleware.MethodOverride
131131 Network.Wai.Middleware.MethodOverridePost
132132 Network.Wai.Middleware.Rewrite
133+ Network.Wai.Middleware.StripHeaders
133134 Network.Wai.Middleware.Vhost
134135 Network.Wai.Middleware.HttpAuth
135136 Network.Wai.Middleware.StreamFile
@@ -152,6 +153,7 @@ test-suite spec
152153 Network.Wai.RequestSpec
153154 Network.Wai.Middleware.ApprootSpec
154155 Network.Wai.Middleware.ForceSSLSpec
156+ Network.Wai.Middleware.StripHeadersSpec
155157 WaiExtraSpec
156158 build-depends: base >= 4 && < 5
157159 , wai-extra
@@ -168,6 +170,7 @@ test-suite spec
168170 , blaze-builder
169171 , cookie
170172 , time
173+ , case-insensitive
171174 ghc-options: -Wall -Werror
172175
173176source-repository head