@@ -35,7 +35,7 @@ import qualified Data.Text as T
3535import qualified Data.Text as Text
3636import Data.Text.Encoding (encodeUtf8 )
3737import Data.Typeable
38- import GHC.TypeLits (KnownNat , KnownSymbol , TypeError , symbolVal )
38+ import GHC.TypeLits (KnownSymbol , TypeError , symbolVal )
3939import Network.HTTP.Media (MediaType , matches , parseAccept )
4040import qualified Network.HTTP.Media as M
4141import qualified Network.HTTP.Media as Media
@@ -98,7 +98,6 @@ import Servant.API.ContentTypes
9898 ( AllMime (allMime )
9999 , AllMimeUnrender (allMimeUnrender )
100100 , EventStream
101- , contentTypes
102101 )
103102import Servant.API.Generic
104103 ( GenericMode (.. )
@@ -119,7 +118,7 @@ import Servant.API.ServerSentEvents
119118 ( EventKind (JsonEvent , RawEvent )
120119 , ServerSentEvents'
121120 )
122- import Servant.API.Status (statusFromNat )
121+ import Servant.API.Status (KnownStatus )
123122import Servant.API.Stream (NoFraming )
124123import Servant.API.TypeErrors
125124import Servant.API.TypeLevel (AtMostOneFragment , FragmentUnique )
@@ -299,129 +298,65 @@ instance
299298 hoistClientMonad pm _ f cl = hoistClientMonad pm (Proxy :: Proxy sublayout ) f . cl
300299
301300instance
302- -- Note [Non-Empty Content Types]
303-
304301 {-# OVERLAPPABLE #-}
305- ( KnownNat status
306- , MimeUnrender ct a
302+ ( HasClientContentCheck ctypes
307303 , ReflectMethod method
304+ , ResponseListUnrender ctypes '[Respond status " " a ]
308305 , RunClient m
309- , cts' ~ (ct ': cts)
310306 )
311- => HasClient m (Verb method status cts' a)
307+ => HasClient m (Verb method status ctypes a )
312308 where
313- type Client m (Verb method status cts' a ) = m a
314- clientWithRoute _pm Proxy req = do
315- response <-
316- runRequestAcceptStatus
317- (Just [status])
318- req
319- { requestAccept = fromList $ toList accept
320- , requestMethod = method
321- }
322- response `decodedAs` (Proxy :: Proxy ct )
323- where
324- accept = contentTypes (Proxy :: Proxy ct )
325- method = reflectMethod (Proxy :: Proxy method )
326- status = statusFromNat (Proxy :: Proxy status )
327-
309+ type Client m (Verb method status ctypes a ) = m a
310+ clientWithRoute pm Proxy = clientWithRoute pm (Proxy @ (MultiVerb method ctypes '[Respond status " " a ] a ))
328311 hoistClientMonad _ _ f = f
329312
330313instance
331314 {-# OVERLAPPING #-}
332- ( KnownNat status
315+ ( KnownStatus status
333316 , ReflectMethod method
334317 , RunClient m
335318 )
336- => HasClient m (Verb method status cts NoContent )
319+ => HasClient m (Verb method status ctypes NoContent )
337320 where
338- type
339- Client m (Verb method status cts NoContent ) =
340- m NoContent
341- clientWithRoute _pm Proxy req = do
342- _response <- runRequestAcceptStatus (Just [status]) req{requestMethod = method}
343- pure NoContent
344- where
345- method = reflectMethod (Proxy :: Proxy method )
346- status = statusFromNat (Proxy :: Proxy status )
347-
348- hoistClientMonad _ _ f = f
349-
350- instance
351- (ReflectMethod method , RunClient m )
352- => HasClient m (NoContentVerb method )
353- where
354- type
355- Client m (NoContentVerb method ) =
356- m NoContent
357- clientWithRoute _pm Proxy req = do
358- _response <- runRequest req{requestMethod = method}
359- pure NoContent
360- where
361- method = reflectMethod (Proxy :: Proxy method )
362-
321+ type Client m (Verb method status ctypes NoContent ) = m NoContent
322+ clientWithRoute pm Proxy req =
323+ NoContent
324+ <$ clientWithRoute pm (Proxy @ (MultiVerb method '() '[RespondAs '() status " " () ] () )) req
363325 hoistClientMonad _ _ f = f
364326
365327instance
366- -- Note [Non-Empty Content Types]
367-
368328 {-# OVERLAPPING #-}
369- ( BuildHeadersTo ls
370- , KnownNat status
371- , MimeUnrender ct a
329+ ( HasClientContentCheck ctypes
372330 , ReflectMethod method
331+ , ResponseListUnrender ctypes '[WithHeaders h (Headers h a ) (Respond status " " a )]
373332 , RunClient m
374- , cts' ~ (ct ': cts)
375333 )
376- => HasClient m (Verb method status cts' (Headers ls a))
334+ => HasClient m (Verb method status ctypes (Headers h a ))
377335 where
378- type
379- Client m (Verb method status cts' (Headers ls a )) =
380- m (Headers ls a )
381- clientWithRoute _pm Proxy req = do
382- response <-
383- runRequestAcceptStatus
384- (Just [status])
385- req
386- { requestMethod = method
387- , requestAccept = fromList $ toList accept
388- }
389- val <- response `decodedAs` (Proxy :: Proxy ct )
390- pure $
391- Headers
392- { getResponse = val
393- , getHeadersHList = buildHeadersTo . toList $ responseHeaders response
394- }
395- where
396- method = reflectMethod (Proxy :: Proxy method )
397- accept = contentTypes (Proxy :: Proxy ct )
398- status = statusFromNat (Proxy :: Proxy status )
399-
336+ type Client m (Verb method status ctypes (Headers h a )) = m (Headers h a )
337+ clientWithRoute pm Proxy = clientWithRoute pm (Proxy @ (MultiVerb method ctypes '[WithHeaders h (Headers h a ) (Respond status " " a )] (Headers h a )))
400338 hoistClientMonad _ _ f = f
401339
402340instance
403341 {-# OVERLAPPING #-}
404- ( BuildHeadersTo ls
405- , KnownNat status
406- , ReflectMethod method
342+ ( ReflectMethod method
343+ , ResponseListUnrender '() '[WithHeaders h (Headers h NoContent ) (RespondAs '() status " " () )]
407344 , RunClient m
408345 )
409- => HasClient m (Verb method status cts (Headers ls NoContent ))
346+ => HasClient m (Verb method status ctypes (Headers h NoContent ))
410347 where
411- type
412- Client m (Verb method status cts (Headers ls NoContent )) =
413- m (Headers ls NoContent )
414- clientWithRoute _pm Proxy req = do
415- response <- runRequestAcceptStatus (Just [status]) req{requestMethod = method}
416- pure $
417- Headers
418- { getResponse = NoContent
419- , getHeadersHList = buildHeadersTo . toList $ responseHeaders response
420- }
421- where
422- method = reflectMethod (Proxy :: Proxy method )
423- status = statusFromNat (Proxy :: Proxy status )
348+ type Client m (Verb method status ctypes (Headers h NoContent )) = m (Headers h NoContent )
349+ clientWithRoute pm Proxy = clientWithRoute pm (Proxy @ (MultiVerb method '() '[WithHeaders h (Headers h NoContent ) (RespondAs '() status " " () )] (Headers h NoContent )))
350+ hoistClientMonad _ _ f = f
424351
352+ instance
353+ (ReflectMethod method , RunClient m )
354+ => HasClient m (NoContentVerb method )
355+ where
356+ type Client m (NoContentVerb method ) = m NoContent
357+ clientWithRoute pm Proxy req =
358+ NoContent
359+ <$ clientWithRoute pm (Proxy @ (MultiVerb method '() '[RespondAs '() 204 " " () ] () )) req
425360 hoistClientMonad _ _ f = f
426361
427362data ClientParseError = ClientParseError MediaType String | ClientStatusMismatch | ClientNoMatchingStatus
@@ -1226,9 +1161,21 @@ x // f = f x
12261161(/:) :: (a -> b -> c ) -> b -> a -> c
12271162(/:) = flip
12281163
1164+ class HasClientContentCheck cs where
1165+ clientAcceptList :: Proxy cs -> [M. MediaType ]
1166+ clientContentTypeOk :: Proxy cs -> M. MediaType -> Bool
1167+
1168+ instance AllMime cs => HasClientContentCheck cs where
1169+ clientAcceptList = allMime
1170+ clientContentTypeOk p c = any (M. matches c) (allMime p)
1171+
1172+ instance HasClientContentCheck '() where
1173+ clientAcceptList _ = []
1174+ clientContentTypeOk _ _ = True
1175+
12291176instance
1230- ( AllMime cs
1231- , AsUnion as r
1177+ ( AsUnion as r
1178+ , HasClientContentCheck cs
12321179 , ReflectMethod method
12331180 , ResponseListUnrender cs as
12341181 , RunClient m
@@ -1247,7 +1194,7 @@ instance
12471194 }
12481195
12491196 c <- getResponseContentType response
1250- unless (any ( M. matches c) accept ) $ do
1197+ unless (clientContentTypeOk ( Proxy @ cs ) c ) $ do
12511198 throwClientError $ UnsupportedContentType c response
12521199
12531200 -- NOTE: support streaming in the future
@@ -1260,7 +1207,7 @@ instance
12601207 UnrenderError e -> throwClientError (DecodeFailure (Text. pack e) response)
12611208 UnrenderSuccess x -> pure (fromUnion @ as x)
12621209 where
1263- accept = allMime (Proxy @ cs )
1210+ accept = clientAcceptList (Proxy @ cs )
12641211 method = reflectMethod (Proxy @ method )
12651212
12661213 hoistClientMonad _ _ f = f
@@ -1301,23 +1248,6 @@ checkContentTypeHeader response =
13011248 Nothing -> throwClientError $ InvalidContentTypeHeader response
13021249 Just t' -> pure t'
13031250
1304- decodedAs
1305- :: forall ct a m
1306- . (MimeUnrender ct a , RunClient m )
1307- => Response
1308- -> Proxy ct
1309- -> m a
1310- decodedAs response@ Response {responseBody = body} ct = do
1311- responseContentType <- checkContentTypeHeader response
1312- unless (any (matches responseContentType) accept) $
1313- throwClientError $
1314- UnsupportedContentType responseContentType response
1315- case mimeUnrender ct body of
1316- Left err -> throwClientError $ DecodeFailure (T. pack err) response
1317- Right val -> pure val
1318- where
1319- accept = toList $ contentTypes ct
1320-
13211251-------------------------------------------------------------------------------
13221252-- Custom type errors
13231253-------------------------------------------------------------------------------
0 commit comments