@@ -28,6 +28,10 @@ import Control.Arrow
2828 (left)
2929import Control.Concurrent
3030 (ThreadId, forkIO, killThread)
31+import Control.Concurrent.STM
32+ (atomically)
33+import Control.Concurrent.STM.TVar
34+ (newTVar, readTVar)
3135import Control.Exception
3236 (bracket)
3337import Control.Monad.Error.Class
@@ -37,17 +41,19 @@ import Data.Char
3741 (chr, isPrint)
3842import Data.Foldable
3943 (forM_)
44+import Data.Maybe
45+ (listToMaybe)
4046import Data.Monoid ()
4147import Data.Proxy
4248import Data.Semigroup
4349 ((<>))
44−import qualified Generics.SOP as SOP
50+import qualified Generics.SOP as SOP
4551import GHC.Generics
4652 (Generic)
47−import qualified Network.HTTP.Client as C
48−import qualified Network.HTTP.Types as HTTP
53+import qualified Network.HTTP.Client as C
54+import qualified Network.HTTP.Types as HTTP
4955import Network.Socket
50−import qualified Network.Wai as Wai
56+import qualified Network.Wai as Wai
5157import Network.Wai.Handler.Warp
5258import System.IO.Unsafe
5359 (unsafePerformIO)
@@ -64,12 +70,12 @@ import Servant.API
6470 DeleteNoContent, EmptyAPI, FormUrlEncoded, Get, Header,
6571 Headers, JSON, NoContent (NoContent), Post, Put, QueryFlag,
6672 QueryParam, QueryParams, Raw, ReqBody, addHeader, getHeaders)
67−import Servant.Test.ComprehensiveAPI
6873import Servant.Client
69−import qualified Servant.Client.Core.Internal.Auth as Auth
70−import qualified Servant.Client.Core.Internal.Request as Req
74+import qualified Servant.Client.Core.Internal.Auth as Auth
75+import qualified Servant.Client.Core.Internal.Request as Req
7176import Servant.Server
7277import Servant.Server.Experimental.Auth
78+import Servant.Test.ComprehensiveAPI
7379
7480-- This declaration simply checks that all instances are in place.
7581_ = client comprehensiveAPIWithoutStreaming
@@ -128,6 +134,7 @@ type Api =
128134 Get '[JSON] (String, Maybe Int, Bool, [(String, [Rational])])
129135 :<|> "headers" :> Get '[JSON] (Headers TestHeaders Bool)
130136 :<|> "deleteContentType" :> DeleteNoContent '[JSON] NoContent
137+ :<|> "redirectWithCookie" :> Raw
131138 :<|> "empty" :> EmptyAPI
132139
133140api :: Proxy Api
@@ -148,6 +155,7 @@ getMultiple :: String -> Maybe Int -> Bool -> [(String, [Rational])]
148155 -> ClientM (String, Maybe Int, Bool, [(String, [Rational])])
149156getRespHeaders :: ClientM (Headers TestHeaders Bool)
150157getDeleteContentType :: ClientM NoContent
158+getRedirectWithCookie :: HTTP.Method -> ClientM Response
151159
152160getRoot
153161 :<|> getGet
@@ -163,6 +171,7 @@ getRoot
163171 :<|> getMultiple
164172 :<|> getRespHeaders
165173 :<|> getDeleteContentType
174+ :<|> getRedirectWithCookie
166175 :<|> EmptyClient = client api
167176
168177server :: Application
@@ -184,9 +193,9 @@ server = serve api (
184193 :<|> (\ a b c d -> return (a, b, c, d))
185194 :<|> (return $ addHeader 1729 $ addHeader "eg2" True)
186195 :<|> return NoContent
196+ :<|> (Tagged $ \ _request respond -> respond $ Wai.responseLBS HTTP.found302 [("Location", "testlocation"), ("Set-Cookie", "testcookie=test")] "")
187197 :<|> emptyServer)
188198
189−
190199type FailApi =
191200 "get" :> Raw
192201 :<|> "capture" :> Capture "name" String :> Raw
@@ -364,6 +373,14 @@ sucessSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do
364373 Left e -> assertFailure $ show e
365374 Right val -> getHeaders val `shouldBe` [("X-Example1", "1729"), ("X-Example2", "eg2")]
366375
376+ it "Stores Cookie in CookieJar after a redirect" $ \(_, baseUrl) -> do
377+ mgr <- C.newManager C.defaultManagerSettings
378+ cj <- atomically . newTVar $ C.createCookieJar []
379+ _ <- runClientM (getRedirectWithCookie HTTP.methodGet) (ClientEnv mgr baseUrl (Just cj))
380+ cookie <- listToMaybe . C.destroyCookieJar <$> atomically (readTVar cj)
381+ C.cookie_name <$> cookie `shouldBe` Just "testcookie"
382+ C.cookie_value <$> cookie `shouldBe` Just "test"
383+
367384 modifyMaxSuccess (const 20) $ do
368385 it "works for a combination of Capture, QueryParam, QueryFlag and ReqBody" $ \(_, baseUrl) ->
369386 property $ forAllShrink pathGen shrink $ \(NonEmpty cap) num flag body ->