@@ -38,8 +38,8 @@ import Network.Wai.Internal (Response (ResponseBuilder))
3838import Network.Wai.Test (defaultRequest, request,
3939 runSession, simpleBody,
4040 simpleHeaders, simpleStatus)
41−import Servant.API ((:<|>) (..), (:>), Capture, Delete,
42− Get, Header (..),
41+import Servant.API ((:<|>) (..), (:>), BasicAuth, BasicAuthData(BasicAuthData),
42+ Capture, Delete, Get, Header (..),
4343 Headers, HttpVersion,
4444 IsSecure (..), JSON,
4545 NoContent (..), Patch, PlainText,
@@ -49,13 +49,16 @@ import Servant.API ((:<|>) (..), (:>), Capture, Delete,
4949 StdMethod (..), Verb, addHeader)
5050import Servant.API.Internal.Test.ComprehensiveAPI
5151import Servant.Server (ServantErr (..), Server, err404,
52− serve, serveWithContext, Context(EmptyContext))
52+ serve, serveWithContext, Context((:.), EmptyContext))
5353import Test.Hspec (Spec, context, describe, it,
5454 shouldBe, shouldContain)
55+import qualified Test.Hspec.Wai as THW
5556import Test.Hspec.Wai (get, liftIO, matchHeaders,
56− matchStatus, request,
57− shouldRespondWith, with, (<:>))
57+ matchStatus, shouldRespondWith,
58+ with, (<:>))
5859
60+import Servant.Server.Internal.BasicAuth (BasicAuthCheck(BasicAuthCheck),
61+ BasicAuthResult(Authorized,Unauthorized))
5962import Servant.Server.Internal.RoutingApplication
6063 (toApplication, RouteResult(..))
6164import Servant.Server.Internal.Router
@@ -86,6 +89,7 @@ spec = do
8689 responseHeadersSpec
8790 routerSpec
8891 miscCombinatorSpec
92+ basicAuthSpec
8993
9094------------------------------------------------------------------------------
9195-- * verbSpec {{{
@@ -117,49 +121,49 @@ verbSpec = describe "Servant.API.Verb" $ do
117121 -- HEAD and 214/215 need not return bodies
118122 unless (status `elem` [214, 215] || method == methodHead) $
119123 it "returns the person" $ do
120− response <- Test.Hspec.Wai.request method "/" [] ""
124+ response <- THW.request method "/" [] ""
121125 liftIO $ statusCode (simpleStatus response) `shouldBe` status
122126 liftIO $ decode' (simpleBody response) `shouldBe` Just alice
123127
124128 it "returns no content on NoContent" $ do
125− response <- Test.Hspec.Wai.request method "/noContent" [] ""
129+ response <- THW.request method "/noContent" [] ""
126130 liftIO $ statusCode (simpleStatus response) `shouldBe` status
127131 liftIO $ simpleBody response `shouldBe` ""
128132
129133 -- HEAD should not return body
130134 when (method == methodHead) $
131135 it "HEAD returns no content body" $ do
132− response <- Test.Hspec.Wai.request method "/" [] ""
136+ response <- THW.request method "/" [] ""
133137 liftIO $ simpleBody response `shouldBe` ""
134138
135139 it "throws 405 on wrong method " $ do
136− Test.Hspec.Wai.request (wrongMethod method) "/" [] ""
140+ THW.request (wrongMethod method) "/" [] ""
137141 `shouldRespondWith` 405
138142
139143 it "returns headers" $ do
140− response1 <- Test.Hspec.Wai.request method "/header" [] ""
144+ response1 <- THW.request method "/header" [] ""
141145 liftIO $ statusCode (simpleStatus response1) `shouldBe` status
142146 liftIO $ simpleHeaders response1 `shouldContain` [("H", "5")]
143147
144− response2 <- Test.Hspec.Wai.request method "/header" [] ""
148+ response2 <- THW.request method "/header" [] ""
145149 liftIO $ statusCode (simpleStatus response2) `shouldBe` status
146150 liftIO $ simpleHeaders response2 `shouldContain` [("H", "5")]
147151
148152 it "handles trailing '/' gracefully" $ do
149− response <- Test.Hspec.Wai.request method "/headerNC/" [] ""
153+ response <- THW.request method "/headerNC/" [] ""
150154 liftIO $ statusCode (simpleStatus response) `shouldBe` status
151155
152156 it "returns 406 if the Accept header is not supported" $ do
153− Test.Hspec.Wai.request method "" [(hAccept, "crazy/mime")] ""
157+ THW.request method "" [(hAccept, "crazy/mime")] ""
154158 `shouldRespondWith` 406
155159
156160 it "responds if the Accept header is supported" $ do
157− response <- Test.Hspec.Wai.request method ""
161+ response <- THW.request method ""
158162 [(hAccept, "application/json")] ""
159163 liftIO $ statusCode (simpleStatus response) `shouldBe` status
160164
161165 it "sets the Content-Type header" $ do
162− response <- Test.Hspec.Wai.request method "" [] ""
166+ response <- THW.request method "" [] ""
163167 liftIO $ simpleHeaders response `shouldContain`
164168 [("Content-Type", "application/json")]
165169
@@ -306,7 +310,7 @@ reqBodySpec = describe "Servant.API.ReqBody" $ do
306310
307311 let server :: Server ReqBodyApi
308312 server = return :<|> return . age
309− mkReq method x = Test.Hspec.Wai.request method x
313+ mkReq method x = THW.request method x
310314 [(hContentType, "application/json;charset=utf-8")]
311315
312316 with (return $ serve reqBodyApi server) $ do
@@ -319,7 +323,7 @@ reqBodySpec = describe "Servant.API.ReqBody" $ do
319323 mkReq methodPut "/blah" "some invalid body" `shouldRespondWith` 400
320324
321325 it "responds with 415 if the request body media type is unsupported" $ do
322− Test.Hspec.Wai.request methodPost "/"
326+ THW.request methodPost "/"
323327 [(hContentType, "application/nonsense")] "" `shouldRespondWith` 415
324328
325329-- }}}
@@ -455,19 +459,19 @@ responseHeadersSpec = describe "ResponseHeaders" $ do
455459
456460 it "includes the headers in the response" $
457461 forM_ methods $ \method ->
458− Test.Hspec.Wai.request method "/" [] ""
462+ THW.request method "/" [] ""
459463 `shouldRespondWith` "\"hi\""{ matchHeaders = ["H1" <:> "5", "H2" <:> "kilroy"]
460464 , matchStatus = 200
461465 }
462466
463467 it "responds with not found for non-existent endpoints" $
464468 forM_ methods $ \method ->
465− Test.Hspec.Wai.request method "blahblah" [] ""
469+ THW.request method "blahblah" [] ""
466470 `shouldRespondWith` 404
467471
468472 it "returns 406 if the Accept header is not supported" $
469473 forM_ methods $ \method ->
470− Test.Hspec.Wai.request method "" [(hAccept, "crazy/mime")] ""
474+ THW.request method "" [(hAccept, "crazy/mime")] ""
471475 `shouldRespondWith` 406
472476
473477-- }}}
@@ -527,6 +531,39 @@ miscCombinatorSpec = with (return $ serve miscApi miscServ) $
527531 go "/host" "\"0.0.0.0:0\""
528532
529533 where go path res = Test.Hspec.Wai.get path `shouldRespondWith` res
534+
535+-- }}}
536+------------------------------------------------------------------------------
537+-- * Authentication {{{
538+------------------------------------------------------------------------------
539+
540+type BasicAuthAPI = BasicAuth "foo" () :> "basic" :> Get '[JSON] Animal
541+
542+basicAuthApi :: Proxy BasicAuthAPI
543+basicAuthApi = Proxy
544+basicAuthServer :: Server BasicAuthAPI
545+basicAuthServer = const (return jerry)
546+
547+basicAuthContext :: Context '[ BasicAuthCheck () ]
548+basicAuthContext =
549+ let basicHandler = BasicAuthCheck $ (\(BasicAuthData usr pass) ->
550+ if usr == "servant" && pass == "server"
551+ then return (Authorized ())
552+ else return Unauthorized
553+ )
554+ in basicHandler :. EmptyContext
555+
556+basicAuthSpec :: Spec
557+basicAuthSpec = do
558+ describe "Servant.API.BasicAuth" $ do
559+ with (return (serveWithContext basicAuthApi basicAuthContext basicAuthServer)) $ do
560+
561+ context "Basic Authentication" $ do
562+ it "returns with 401 with bad password" $ do
563+ get "/basic" `shouldRespondWith` 401
564+ it "returns 200 with the right password" $ do
565+ THW.request methodGet "/basic" [("Authorization","Basic c2VydmFudDpzZXJ2ZXI=")] "" `shouldRespondWith` 200
566+
530567-- }}}
531568------------------------------------------------------------------------------
532569-- * Test data types {{{