@@ -1,4 +1,3 @@
1−{-# LANGUAGE MultiWayIf #-}
21module Servant.QuickCheck.Internal.Predicates where
32
43import Control.Monad
@@ -10,15 +9,19 @@ import qualified Data.ByteString.Lazy as LBS
109import Data.CaseInsensitive (mk)
1110import Data.Either (isRight)
1211import Data.List.Split (wordsBy)
13−import Data.Maybe (isJust)
12+import Data.Maybe (fromMaybe, isJust, maybeToList)
1413import Data.Monoid ((<>))
1514import Data.Text (Text)
1615import GHC.Generics (Generic)
1716import Network.HTTP.Client (Manager, Request, Response, httpLbs,
18− method, responseBody, responseHeaders,
17+ method, parseUrl, requestHeaders,
18+ responseBody, responseHeaders,
1919 responseStatus)
20+import Network.HTTP.Media (matchAccept)
2021import Network.HTTP.Types (methodGet, methodHead, parseMethod,
21− status401, renderStdMethod, status405, status500)
22+ renderStdMethod, status200, status201,
23+ status300, status401, status405,
24+ status500)
2225
2326-- | [__Best Practice__]
2427--
@@ -72,16 +75,22 @@ onlyJsonObjects
7275--
7376-- * 201 Created: <https://tools.ietf.org/html/rfc7231#section-6.3.2 RFC 7231 Section 6.3.2>
7477-- * Location header: <https://tools.ietf.org/html/rfc7231#section-7.1.2 RFC 7231 Section 7.1.2>
75−{-createContainsValidLocation :: RequestPredicate Text Bool-}
76−{-createContainsValidLocation-}
77− {-= RequestPredicate-}
78− {-{ reqPredName = "createContainsValidLocation"-}
79− {-, reqResps = \req mg -> do-}
80− {-resp <- httpLbs mgr req-}
81− {-if responseStatus resp == status201-}
82− {-then case lookup "Location" $ responseHeaders resp of-}
83− {-Nothing -> return []-}
84− {-Just l -> if-}
78+createContainsValidLocation :: RequestPredicate Text Bool
79+createContainsValidLocation
80+ = RequestPredicate
81+ { reqPredName = "createContainsValidLocation"
82+ , reqResps = \req mgr -> do
83+ resp <- httpLbs req mgr
84+ if responseStatus resp == status201
85+ then case lookup "Location" $ responseHeaders resp of
86+ Nothing -> return (False, [resp])
87+ Just l -> case parseUrl $ SBSC.unpack l of
88+ Nothing -> return (False, [resp])
89+ Just x -> do
90+ resp2 <- httpLbs x mgr
91+ return (status2XX resp2, [resp, resp2])
92+ else return (True, [resp])
93+ }
8594
8695{-
8796getsHaveLastModifiedHeader :: ResponsePredicate Text Bool
@@ -120,8 +129,9 @@ notAllowedContainsAllowHeader
120129 go x = all (\y -> isRight $ parseMethod $ SBSC.pack y)
121130 $ wordsBy (`elem` (", " :: [Char])) (SBSC.unpack x)
122131
123− {-
124−-- | When a request contains an @Accept@ header, the server must either return
132+-- | [__RFC Compliance__]
133+--
134+-- When a request contains an @Accept@ header, the server must either return
125135-- content in one of the requested representations, or respond with @406 Not
126136-- Acceptable@.
127137--
@@ -133,18 +143,18 @@ notAllowedContainsAllowHeader
133143-- * @Accept@ header: <https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html RFC 2616 Section 14.1>
134144honoursAcceptHeader :: RequestPredicate b Bool
135145honoursAcceptHeader
136− = RequestPredicate name (ResponsePredicate name $ \req mgr -> do
137−
138− resp <- httpLbs req mgr
139− let scode = responseStatus resp
140− sctype = maybeToList $ lookup "Content-Type" $ responseHeaders resp
141− sacc = fromMaybe "*/*" $ lookup "Accept" $ requestHeaders req
142− if 100 < scode && scode < 300
143− then isJust matchAccept sacc sctype
144− else True)
145− where name = "honoursAcceptHeader"
146+ = RequestPredicate
147+ { reqPredName = "honoursAcceptHeader"
148+ , reqResps = \req mgr -> do
149+ resp <- httpLbs req mgr
150+ let scode = responseStatus resp
151+ sctype = lookup "Content-Type" $ responseHeaders resp
152+ sacc = fromMaybe "*/*" $ lookup "Accept" (requestHeaders req)
153+ if 100 < scode && scode < 300
154+ then return (isJust $ sctype >>= \x -> matchAccept x sacc, [resp])
155+ else return (True, [resp])
156+ }
146157
147−-}
148158-- | [__Best Practice__]
149159--
150160-- Whether or not a representation should be cached, it is good practice to
@@ -179,7 +189,7 @@ headsHaveCacheControlHeader
179189 , reqResps = \req mgr -> if method req == methodHead
180190 then do
181191 resp <- httpLbs req mgr
182− let good = isJust $ lookup "Cache-Control" $ responseHeaders resp
192+ let good = hasValidHeader "Cache-Control" (const True) resp
183193 return (good, [resp])
184194 else return (True, [])
185195 }
@@ -334,3 +344,6 @@ hasValidHeader :: SBS.ByteString -> (SBS.ByteString -> Bool) -> Response b -> Bo
334344hasValidHeader hdr p r = case lookup (mk hdr) (responseHeaders r) of
335345 Nothing -> False
336346 Just v -> p v
347+
348+status2XX :: Response b -> Bool
349+status2XX r = status200 <= responseStatus r && responseStatus r < status300