@@ -1,34 +1,33 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
1+{-# LANGUAGE CPP #-}
22{-# LANGUAGE ConstraintKinds #-}
33{-# LANGUAGE DataKinds #-}
44{-# LANGUAGE DeriveDataTypeable #-}
55{-# LANGUAGE FlexibleContexts #-}
66{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE InstanceSigs #-}
87{-# LANGUAGE MultiParamTypeClasses #-}
98{-# LANGUAGE OverloadedStrings #-}
109{-# LANGUAGE PolyKinds #-}
11−{-# LANGUAGE QuantifiedConstraints #-}
1210{-# LANGUAGE RankNTypes #-}
1311{-# LANGUAGE ScopedTypeVariables #-}
1412{-# LANGUAGE TupleSections #-}
15−{-# LANGUAGE TypeApplications #-}
1613{-# LANGUAGE TypeFamilies #-}
1714{-# LANGUAGE TypeOperators #-}
1815{-# LANGUAGE UndecidableInstances #-}
1916
17+#if MIN_VERSION_base(4,9,0) && __GLASGOW_HASKELL__ >= 802
18+#define HAS_TYPE_ERROR
19+#endif
20+
21+#include "overlapping-compat.h"
22+
2023module Servant.Server.Internal
2124 ( module Servant.Server.Internal
2225 , module Servant.Server.Internal.BasicAuth
2326 , module Servant.Server.Internal.Context
24− , module Servant.Server.Internal.Delayed
25− , module Servant.Server.Internal.DelayedIO
26− , module Servant.Server.Internal.ErrorFormatter
2727 , module Servant.Server.Internal.Handler
2828 , module Servant.Server.Internal.Router
29− , module Servant.Server.Internal.RouteResult
3029 , module Servant.Server.Internal.RoutingApplication
31− , module Servant.Server.Internal.ServerError
30+ , module Servant.Server.Internal.ServantErr
3231 ) where
3332
3433import Control.Monad
@@ -41,11 +40,12 @@ import qualified Data.ByteString as B
4140import qualified Data.ByteString.Builder as BB
4241import qualified Data.ByteString.Char8 as BC8
4342import qualified Data.ByteString.Lazy as BL
44−import Data.Constraint (Constraint, Dict(..))
4543import Data.Either
4644 (partitionEithers)
4745import Data.Maybe
4846 (fromMaybe, isNothing, mapMaybe, maybeToList)
47+import Data.Semigroup
48+ ((<>))
4949import Data.String
5050 (IsString (..))
5151import Data.String.Conversions
@@ -54,9 +54,8 @@ import Data.Tagged
5454 (Tagged (..), retag, untag)
5555import qualified Data.Text as T
5656import Data.Typeable
57−import GHC.Generics
5857import GHC.TypeLits
59− (KnownNat, KnownSymbol, TypeError, symbolVal)
58+ (KnownNat, KnownSymbol, natVal, symbolVal)
6059import qualified Network.HTTP.Media as NHM
6160import Network.HTTP.Types hiding
6261 (Header, ResponseHeaders)
@@ -64,52 +63,42 @@ import Network.Socket
6463 (SockAddr)
6564import Network.Wai
6665 (Application, Request, httpVersion, isSecure, lazyRequestBody,
67− queryString, remoteHost, getRequestBodyChunk, requestHeaders,
68− requestMethod, responseLBS, responseStream, vault)
66+ rawQueryString, remoteHost, requestHeaders, requestMethod,
67+ responseLBS, responseStream, vault)
6968import Prelude ()
7069import Prelude.Compat
7170import Servant.API
72− ((:<|>) (..), (:>), Accept (..), BasicAuth, Capture',
73− CaptureAll, Description, EmptyAPI, Fragment,
74− FramingRender (..), FramingUnrender (..), FromSourceIO (..),
75− Header', If, IsSecure (..), NoContentVerb, QueryFlag,
76− QueryParam', QueryParams, Raw, ReflectMethod (reflectMethod),
77− RemoteHost, ReqBody', SBool (..), SBoolI (..), SourceIO,
78− Stream, StreamBody', Summary, ToSourceIO (..), Vault, Verb,
79− WithNamedContext, NamedRoutes)
80−import Servant.API.Generic (GenericMode(..), ToServant, ToServantApi, GServantProduct, toServant, fromServant)
71+ ((:<|>) (..), (:>), Accept (..), BasicAuth,
72+ BoundaryStrategy (..), Capture', CaptureAll, Description,
73+ EmptyAPI, FramingRender (..), Header', If, IsSecure (..),
74+ QueryFlag, QueryParam', QueryParams, Raw,
75+ ReflectMethod (reflectMethod), RemoteHost, ReqBody',
76+ SBool (..), SBoolI (..), Stream, StreamGenerator (..),
77+ Summary, ToStreamGenerator (..), Vault, Verb,
78+ WithNamedContext)
8179import Servant.API.ContentTypes
8280 (AcceptHeader (..), AllCTRender (..), AllCTUnrender (..),
83− AllMime, MimeRender (..), MimeUnrender (..), NoContent,
84− canHandleAcceptH)
81+ AllMime, MimeRender (..), canHandleAcceptH)
8582import Servant.API.Modifiers
8683 (FoldLenient, FoldRequired, RequestArgument,
8784 unfoldRequestArgument)
8885import Servant.API.ResponseHeaders
8986 (GetHeaders, Headers, getHeaders, getResponse)
90−import Servant.API.Status
91− (statusFromNat)
92−import qualified Servant.Types.SourceT as S
93−import Servant.API.TypeErrors
9487import Web.HttpApiData
95− (FromHttpApiData, parseHeader, parseQueryParam, parseUrlPiece,
96− parseUrlPieces)
88+ (FromHttpApiData, parseHeader, parseQueryParam,
89+ parseUrlPieceMaybe, parseUrlPieces)
9790
9891import Servant.Server.Internal.BasicAuth
9992import Servant.Server.Internal.Context
100−import Servant.Server.Internal.Delayed
101−import Servant.Server.Internal.DelayedIO
102−import Servant.Server.Internal.ErrorFormatter
10393import Servant.Server.Internal.Handler
10494import Servant.Server.Internal.Router
105−import Servant.Server.Internal.RouteResult
10695import Servant.Server.Internal.RoutingApplication
107−import Servant.Server.Internal.ServerError
96+import Servant.Server.Internal.ServantErr
10897
98+#ifdef HAS_TYPE_ERROR
10999import GHC.TypeLits
110100 (ErrorMessage (..), TypeError)
111−import Servant.API.TypeLevel
112− (AtLeastOneFragment, FragmentUnique)
101+#endif
113102
114103class HasServer api context where
115104 type ServerT api (m :: * -> *) :: *
@@ -173,31 +162,22 @@ instance (HasServer a context, HasServer b context) => HasServer (a :<|> b) cont
173162-- > server = getBook
174163-- > where getBook :: Text -> Handler Book
175164-- > getBook isbn = ...
176−instance (KnownSymbol capture, FromHttpApiData a, Typeable a
177− , HasServer api context, SBoolI (FoldLenient mods)
178− , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
179− )
165+instance (KnownSymbol capture, FromHttpApiData a, HasServer api context)
180166 => HasServer (Capture' mods capture a :> api) context where
181167
182168 type ServerT (Capture' mods capture a :> api) m =
183− If (FoldLenient mods) (Either String a) a -> ServerT api m
169+ a -> ServerT api m
184170
185171 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
186172
187173 route Proxy context d =
188− CaptureRouter [hint] $
174+ CaptureRouter $
189175 route (Proxy :: Proxy api)
190176 context
191− (addCapture d $ \ txt -> withRequest $ \ request ->
192− case ( sbool :: SBool (FoldLenient mods)
193− , parseUrlPiece txt :: Either T.Text a) of
194− (SFalse, Left e) -> delayedFail $ formatError rep request $ cs e
195− (SFalse, Right v) -> return v
196− (STrue, piece) -> return $ (either (Left . cs) Right) piece)
197− where
198− rep = typeRep (Proxy :: Proxy Capture')
199− formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
200− hint = CaptureHint (T.pack $ symbolVal $ Proxy @capture) (typeRep (Proxy :: Proxy a))
177+ (addCapture d $ \ txt -> case parseUrlPieceMaybe txt of
178+ Nothing -> delayedFail err400
179+ Just v -> return v
180+ )
201181
202182-- | If you use 'CaptureAll' in one of the endpoints for your API,
203183-- this automatically requires your server-side handler to be a
@@ -216,10 +196,7 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
216196-- > server = getSourceFile
217197-- > where getSourceFile :: [Text] -> Handler Book
218198-- > getSourceFile pathSegments = ...
219−instance (KnownSymbol capture, FromHttpApiData a, Typeable a
220− , HasServer api context
221− , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
222− )
199+instance (KnownSymbol capture, FromHttpApiData a, HasServer api context)
223200 => HasServer (CaptureAll capture a :> api) context where
224201
225202 type ServerT (CaptureAll capture a :> api) m =
@@ -228,18 +205,14 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
228205 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
229206
230207 route Proxy context d =
231− CaptureAllRouter [hint] $
208+ CaptureAllRouter $
232209 route (Proxy :: Proxy api)
233210 context
234− (addCapture d $ \ txts -> withRequest $ \ request ->
235− case parseUrlPieces txts of
236− Left e -> delayedFail $ formatError rep request $ cs e
237− Right v -> return v
211+ (addCapture d $ \ txts -> case parseUrlPieces txts of
212+ Left _ -> delayedFail err400
213+ Right v -> return v
238214 )
239− where
240− rep = typeRep (Proxy :: Proxy CaptureAll)
241− formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
242− hint = CaptureHint (T.pack $ symbolVal $ Proxy @capture) (typeRep (Proxy :: Proxy [a]))
215+
243216
244217allowedMethodHead :: Method -> Request -> Bool
245218allowedMethodHead method request = method == methodGet && requestMethod request == methodHead
@@ -259,10 +232,10 @@ methodCheck method request
259232-- body check is no longer an option. However, we now run the accept
260233-- check before the body check and can therefore afford to make it
261234-- recoverable.
262−acceptCheck :: (AllMime list) => Proxy list -> AcceptHeader -> DelayedIO ()
235+acceptCheck :: (AllMime list) => Proxy list -> B.ByteString -> DelayedIO ()
263236acceptCheck proxy accH
264− | canHandleAcceptH proxy accH = return ()
265− | otherwise = delayedFail err406
237+ | canHandleAcceptH proxy (AcceptHeader accH) = return ()
238+ | otherwise = delayedFail err406
266239
267240methodRouter :: (AllCTRender ctypes a)
268241 => (b -> ([(HeaderName, B.ByteString)], a))
@@ -272,29 +245,18 @@ methodRouter :: (AllCTRender ctypes a)
272245methodRouter splitHeaders method proxy status action = leafRouter route'
273246 where
274247 route' env request respond =
275− let accH = getAcceptHeader request
248+ let accH = fromMaybe ct_wildcard $ lookup hAccept $ requestHeaders request
276249 in runAction (action `addMethodCheck` methodCheck method request
277250 `addAcceptCheck` acceptCheck proxy accH
278251 ) env request respond $ \ output -> do
279252 let (headers, b) = splitHeaders output
280− case handleAcceptH proxy accH b of
253+ case handleAcceptH proxy (AcceptHeader accH) b of
281254 Nothing -> FailFatal err406 -- this should not happen (checked before), so we make it fatal if it does
282255 Just (contentT, body) ->
283256 let bdy = if allowedMethodHead method request then "" else body
284257 in Route $ responseLBS status ((hContentType, cs contentT) : headers) bdy
285258
286−noContentRouter :: Method
287− -> Status
288− -> Delayed env (Handler b)
289− -> Router env
290−noContentRouter method status action = leafRouter route'
291− where
292− route' env request respond =
293− runAction (action `addMethodCheck` methodCheck method request)
294− env request respond $ \ _output ->
295− Route $ responseLBS status [] ""
296−
297−instance {-# OVERLAPPABLE #-}
259+instance OVERLAPPABLE_
298260 ( AllCTRender ctypes a, ReflectMethod method, KnownNat status
299261 ) => HasServer (Verb method status ctypes a) context where
300262
@@ -303,9 +265,9 @@ instance {-# OVERLAPPABLE #-}
303265
304266 route Proxy _ = methodRouter ([],) method (Proxy :: Proxy ctypes) status
305267 where method = reflectMethod (Proxy :: Proxy method)
306− status = statusFromNat (Proxy :: Proxy status)
268+ status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
307269
308−instance {-# OVERLAPPING #-}
270+instance OVERLAPPING_
309271 ( AllCTRender ctypes a, ReflectMethod method, KnownNat status
310272 , GetHeaders (Headers h a)
311273 ) => HasServer (Verb method status ctypes (Headers h a)) context where
@@ -315,46 +277,37 @@ instance {-# OVERLAPPING #-}
315277
316278 route Proxy _ = methodRouter (\x -> (getHeaders x, getResponse x)) method (Proxy :: Proxy ctypes) status
317279 where method = reflectMethod (Proxy :: Proxy method)
318− status = statusFromNat (Proxy :: Proxy status)
319−
320−instance (ReflectMethod method) =>
321− HasServer (NoContentVerb method) context where
322−
323− type ServerT (NoContentVerb method) m = m NoContent
324− hoistServerWithContext _ _ nt s = nt s
280+ status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
325281
326− route Proxy _ = noContentRouter method status204
327− where method = reflectMethod (Proxy :: Proxy method)
328282
329−instance {-# OVERLAPPABLE #-}
330− ( MimeRender ctype chunk, ReflectMethod method, KnownNat status,
331− FramingRender framing, ToSourceIO chunk a
332− ) => HasServer (Stream method status framing ctype a) context where
283+instance OVERLAPPABLE_
284+ ( MimeRender ctype a, ReflectMethod method, KnownNat status,
285+ FramingRender framing ctype, ToStreamGenerator b a
286+ ) => HasServer (Stream method status framing ctype b) context where
333287
334− type ServerT (Stream method status framing ctype a) m = m a
288+ type ServerT (Stream method status framing ctype b) m = m b
335289 hoistServerWithContext _ _ nt s = nt s
336290
337291 route Proxy _ = streamRouter ([],) method status (Proxy :: Proxy framing) (Proxy :: Proxy ctype)
338292 where method = reflectMethod (Proxy :: Proxy method)
339− status = statusFromNat (Proxy :: Proxy status)
340−
293+ status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
341294
342−instance {-# OVERLAPPING #-}
343− ( MimeRender ctype chunk, ReflectMethod method, KnownNat status,
344− FramingRender framing, ToSourceIO chunk a,
345− GetHeaders (Headers h a)
346− ) => HasServer (Stream method status framing ctype (Headers h a)) context where
295+instance OVERLAPPING_
296+ ( MimeRender ctype a, ReflectMethod method, KnownNat status,
297+ FramingRender framing ctype, ToStreamGenerator b a,
298+ GetHeaders (Headers h b)
299+ ) => HasServer (Stream method status framing ctype (Headers h b)) context where
347300
348− type ServerT (Stream method status framing ctype (Headers h a)) m = m (Headers h a)
301+ type ServerT (Stream method status framing ctype (Headers h b)) m = m (Headers h b)
349302 hoistServerWithContext _ _ nt s = nt s
350303
351304 route Proxy _ = streamRouter (\x -> (getHeaders x, getResponse x)) method status (Proxy :: Proxy framing) (Proxy :: Proxy ctype)
352305 where method = reflectMethod (Proxy :: Proxy method)
353− status = statusFromNat (Proxy :: Proxy status)
306+ status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
354307
355308
356−streamRouter :: forall ctype a c chunk env framing. (MimeRender ctype chunk, FramingRender framing, ToSourceIO chunk a) =>
357− (c -> ([(HeaderName, B.ByteString)], a))
309+streamRouter :: (MimeRender ctype a, FramingRender framing ctype, ToStreamGenerator b a) =>
310+ (c -> ([(HeaderName, B.ByteString)], b))
358311 -> Method
359312 -> Status
360313 -> Proxy framing
@@ -362,7 +315,7 @@ streamRouter :: forall ctype a c chunk env framing. (MimeRender ctype chunk, Fra
362315 -> Delayed env (Handler c)
363316 -> Router env
364317streamRouter splitHeaders method status framingproxy ctypeproxy action = leafRouter $ \env request respond ->
365− let AcceptHeader accH = getAcceptHeader request
318+ let accH = fromMaybe ct_wildcard $ lookup hAccept $ requestHeaders request
366319 cmediatype = NHM.matchAccept [contentType ctypeproxy] accH
367320 accCheck = when (isNothing cmediatype) $ delayedFail err406
368321 contentHeader = (hContentType, NHM.renderHeader . maybeToList $ cmediatype)
@@ -370,19 +323,28 @@ streamRouter splitHeaders method status framingproxy ctypeproxy action = leafRou
370323 `addAcceptCheck` accCheck
371324 ) env request respond $ \ output ->
372325 let (headers, fa) = splitHeaders output
373− sourceT = toSourceIO fa
374− S.SourceT kStepLBS = framingRender framingproxy (mimeRender ctypeproxy :: chunk -> BL.ByteString) sourceT
375− in Route $ responseStream status (contentHeader : headers) $ \write flush -> do
376− let loop S.Stop = flush
377− loop (S.Error err) = fail err -- TODO: throw better error
378− loop (S.Skip s) = loop s
379− loop (S.Effect ms) = ms >>= loop
380− loop (S.Yield lbs s) = do
381− write (BB.lazyByteString lbs)
382− flush
383− loop s
384−
385− kStepLBS loop
326+ k = getStreamGenerator . toStreamGenerator $ fa in
327+ Route $ responseStream status (contentHeader : headers) $ \write flush -> do
328+ write . BB.lazyByteString $ header framingproxy ctypeproxy
329+ case boundary framingproxy ctypeproxy of
330+ BoundaryStrategyBracket f ->
331+ let go x = let bs = mimeRender ctypeproxy x
332+ (before, after) = f bs
333+ in write ( BB.lazyByteString before
334+ <> BB.lazyByteString bs
335+ <> BB.lazyByteString after) >> flush
336+ in k go go
337+ BoundaryStrategyIntersperse sep -> k
338+ (\x -> do
339+ write . BB.lazyByteString . mimeRender ctypeproxy $ x
340+ flush)
341+ (\x -> do
342+ write . (BB.lazyByteString sep <>) . BB.lazyByteString . mimeRender ctypeproxy $ x
343+ flush)
344+ BoundaryStrategyGeneral f ->
345+ let go = (>> flush) . write . BB.lazyByteString . f . mimeRender ctypeproxy
346+ in k go go
347+ write . BB.lazyByteString $ trailer framingproxy ctypeproxy
386348
387349-- | If you use 'Header' in one of the endpoints for your API,
388350-- this automatically requires your server-side handler to be a function
@@ -407,7 +369,6 @@ streamRouter splitHeaders method status framingproxy ctypeproxy action = leafRou
407369instance
408370 (KnownSymbol sym, FromHttpApiData a, HasServer api context
409371 , SBoolI (FoldRequired mods), SBoolI (FoldLenient mods)
410− , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
411372 )
412373 => HasServer (Header' mods sym a :> api) context where
413374------
@@ -419,9 +380,6 @@ instance
419380 route Proxy context subserver = route (Proxy :: Proxy api) context $
420381 subserver `addHeaderCheck` withRequest headerCheck
421382 where
422− rep = typeRep (Proxy :: Proxy Header')
423− formatError = headerParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
424−
425383 headerName :: IsString n => n
426384 headerName = fromString $ symbolVal (Proxy :: Proxy sym)
427385
@@ -432,13 +390,15 @@ instance
432390 mev :: Maybe (Either T.Text a)
433391 mev = fmap parseHeader $ lookup headerName (requestHeaders req)
434392
435− errReq = delayedFailFatal $ formatError rep req
436− $ "Header " <> headerName <> " is required"
393+ errReq = delayedFailFatal err400
394+ { errBody = "Header " <> headerName <> " is required"
395+ }
437396
438− errSt e = delayedFailFatal $ formatError rep req
439− $ cs $ "Error parsing header "
440− <> headerName
441− <> " failed: " <> e
397+ errSt e = delayedFailFatal err400
398+ { errBody = cs $ "Error parsing header "
399+ <> headerName
400+ <> " failed: " <> e
401+ }
442402
443403-- | If you use @'QueryParam' "author" Text@ in one of the endpoints for your API,
444404-- this automatically requires your server-side handler to be a function
@@ -464,7 +424,6 @@ instance
464424instance
465425 ( KnownSymbol sym, FromHttpApiData a, HasServer api context
466426 , SBoolI (FoldRequired mods), SBoolI (FoldLenient mods)
467− , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
468427 )
469428 => HasServer (QueryParam' mods sym a :> api) context where
470429------
@@ -474,12 +433,9 @@ instance
474433 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
475434
476435 route Proxy context subserver =
477− let querytext = queryToQueryText . queryString
436+ let querytext req = parseQueryText $ rawQueryString req
478437 paramname = cs $ symbolVal (Proxy :: Proxy sym)
479438
480− rep = typeRep (Proxy :: Proxy QueryParam')
481− formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
482−
483439 parseParam :: Request -> DelayedIO (RequestArgument mods a)
484440 parseParam req =
485441 unfoldRequestArgument (Proxy :: Proxy mods) errReq errSt mev
@@ -487,12 +443,14 @@ instance
487443 mev :: Maybe (Either T.Text a)
488444 mev = fmap parseQueryParam $ join $ lookup paramname $ querytext req
489445
490− errReq = delayedFailFatal $ formatError rep req
491− $ cs $ "Query parameter " <> paramname <> " is required"
446+ errReq = delayedFailFatal err400
447+ { errBody = cs $ "Query parameter " <> paramname <> " is required"
448+ }
492449
493− errSt e = delayedFailFatal $ formatError rep req
494− $ cs $ "Error parsing query parameter "
495− <> paramname <> " failed: " <> e
450+ errSt e = delayedFailFatal err400
451+ { errBody = cs $ "Error parsing query parameter "
452+ <> paramname <> " failed: " <> e
453+ }
496454
497455 delayed = addParameterCheck subserver . withRequest $ \req ->
498456 parseParam req
@@ -518,8 +476,7 @@ instance
518476-- > server = getBooksBy
519477-- > where getBooksBy :: [Text] -> Handler [Book]
520478-- > getBooksBy authors = ...return all books by these authors...
521−instance (KnownSymbol sym, FromHttpApiData a, HasServer api context
522− , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters)
479+instance (KnownSymbol sym, FromHttpApiData a, HasServer api context)
523480 => HasServer (QueryParams sym a :> api) context where
524481
525482 type ServerT (QueryParams sym a :> api) m =
@@ -530,23 +487,21 @@ instance (KnownSymbol sym, FromHttpApiData a, HasServer api context
530487 route Proxy context subserver = route (Proxy :: Proxy api) context $
531488 subserver `addParameterCheck` withRequest paramsCheck
532489 where
533− rep = typeRep (Proxy :: Proxy QueryParams)
534− formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
535−
536490 paramname = cs $ symbolVal (Proxy :: Proxy sym)
537491 paramsCheck req =
538492 case partitionEithers $ fmap parseQueryParam params of
539493 ([], parsed) -> return parsed
540− (errs, _) -> delayedFailFatal $ formatError rep req
541− $ cs $ "Error parsing query parameter(s) "
542− <> paramname <> " failed: "
543− <> T.intercalate ", " errs
494+ (errs, _) -> delayedFailFatal err400
495+ { errBody = cs $ "Error parsing query parameter(s) "
496+ <> paramname <> " failed: "
497+ <> T.intercalate ", " errs
498+ }
544499 where
545500 params :: [T.Text]
546501 params = mapMaybe snd
547502 . filter (looksLikeParam . fst)
548− . queryToQueryText
549− . queryString
503+ . parseQueryText
504+ . rawQueryString
550505 $ req
551506
552507 looksLikeParam name = name == paramname || name == (paramname <> "[]")
@@ -572,7 +527,7 @@ instance (KnownSymbol sym, HasServer api context)
572527 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
573528
574529 route Proxy context subserver =
575− let querytext = queryToQueryText . queryString
530+ let querytext r = parseQueryText $ rawQueryString r
576531 param r = case lookup paramname (querytext r) of
577532 Just Nothing -> True -- param is there, with no value
578533 Just (Just v) -> examine v -- param with a value
@@ -614,7 +569,7 @@ instance HasServer Raw context where
614569-- The @Content-Type@ header is inspected, and the list provided is used to
615570-- attempt deserialization. If the request does not have a @Content-Type@
616571-- header, it is treated as @application/octet-stream@ (as specified in
617−-- [RFC 7231 section 3.1.1.5](http://tools.ietf.org/html/rfc7231#section-3.1.1.5)).
572+-- <http://tools.ietf.org/html/rfc7231#section-3.1.1.5 RFC7231>.
618573-- This lets servant worry about extracting it from the request and turning
619574-- it into a value of the type you specify.
620575--
@@ -630,7 +585,6 @@ instance HasServer Raw context where
630585-- > where postBook :: Book -> Handler Book
631586-- > postBook book = ...insert into your db...
632587instance ( AllCTUnrender list a, HasServer api context, SBoolI (FoldLenient mods)
633− , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
634588 ) => HasServer (ReqBody' mods list a :> api) context where
635589
636590 type ServerT (ReqBody' mods list a :> api) m =
@@ -642,9 +596,6 @@ instance ( AllCTUnrender list a, HasServer api context, SBoolI (FoldLenient mods
642596 = route (Proxy :: Proxy api) context $
643597 addBodyCheck subserver ctCheck bodyCheck
644598 where
645− rep = typeRep (Proxy :: Proxy ReqBody')
646− formatError = bodyParserErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
647−
648599 -- Content-Type check, we only lookup we can try to parse the request body
649600 ctCheck = withRequest $ \ request -> do
650601 -- See HTTP RFC 2616, section 7.2.1
@@ -663,34 +614,9 @@ instance ( AllCTUnrender list a, HasServer api context, SBoolI (FoldLenient mods
663614 case sbool :: SBool (FoldLenient mods) of
664615 STrue -> return mrqbody
665616 SFalse -> case mrqbody of
666− Left e -> delayedFailFatal $ formatError rep request e
617+ Left e -> delayedFailFatal err400 { errBody = cs e }
667618 Right v -> return v
668619
669−instance
670− ( FramingUnrender framing, FromSourceIO chunk a, MimeUnrender ctype chunk
671− , HasServer api context
672− ) => HasServer (StreamBody' mods framing ctype a :> api) context
673− where
674− type ServerT (StreamBody' mods framing ctype a :> api) m = a -> ServerT api m
675−
676− hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
677−
678− route Proxy context subserver = route (Proxy :: Proxy api) context $
679− addBodyCheck subserver ctCheck bodyCheck
680− where
681− ctCheck :: DelayedIO (SourceIO chunk -> a)
682− -- TODO: do content-type check
683− ctCheck = return fromSourceIO
684−
685− bodyCheck :: (SourceIO chunk -> a) -> DelayedIO a
686− bodyCheck fromRS = withRequest $ \req -> do
687− let mimeUnrender' = mimeUnrender (Proxy :: Proxy ctype) :: BL.ByteString -> Either String chunk
688− let framingUnrender' = framingUnrender (Proxy :: Proxy framing) mimeUnrender' :: SourceIO B.ByteString -> SourceIO chunk
689− let body = getRequestBodyChunk req
690− let rs = S.fromAction B.null body
691− let rs' = fromRS $ framingUnrender' rs
692− return rs'
693−
694620-- | Make sure the incoming request starts with @"/path"@, strip it and
695621-- pass the rest of the request path to @api@.
696622instance (KnownSymbol path, HasServer api context) => HasServer (path :> api) context where
@@ -755,12 +681,12 @@ data EmptyServer = EmptyServer deriving (Typeable, Eq, Show, Bounded, Enum)
755681emptyServer :: ServerT EmptyAPI m
756682emptyServer = Tagged EmptyServer
757683
758−-- | The server for an `EmptyAPI` is `emptyServer`.
684+-- | The server for an `EmptyAPI` is `emptyAPIServer`.
759685--
760686-- > type MyApi = "nothing" :> EmptyApi
761687-- >
762688-- > server :: Server MyApi
763−-- > server = emptyServer
689+-- > server = emptyAPIServer
764690instance HasServer EmptyAPI context where
765691 type ServerT EmptyAPI m = Tagged m EmptyServer
766692
@@ -789,10 +715,7 @@ instance ( KnownSymbol realm
789715-- * helpers
790716
791717ct_wildcard :: B.ByteString
792−ct_wildcard = "*" <> "/" <> "*"
793−
794−getAcceptHeader :: Request -> AcceptHeader
795−getAcceptHeader = AcceptHeader . fromMaybe ct_wildcard . lookup hAccept . requestHeaders
718+ct_wildcard = "*" <> "/" <> "*" -- Because CPP
796719
797720-- * General Authentication
798721
@@ -817,15 +740,39 @@ instance (HasContextEntry context (NamedContext name subContext), HasServer subA
817740 hoistServerWithContext _ _ nt s = hoistServerWithContext (Proxy :: Proxy subApi) (Proxy :: Proxy subContext) nt s
818741
819742-------------------------------------------------------------------------------
820−-- Custom type errors
743+-- TypeError helpers
821744-------------------------------------------------------------------------------
822745
823−-- Erroring instance for 'HasServer' when a combinator is not fully applied
824−instance TypeError (PartialApplication HasServer arr) => HasServer ((arr :: a -> b) :> sub) context
746+#ifdef HAS_TYPE_ERROR
747+-- | This instance catches mistakes when there are non-saturated
748+-- type applications on LHS of ':>'.
749+--
750+-- >>> serve (Proxy :: Proxy (Capture "foo" :> Get '[JSON] Int)) (error "...")
751+-- ...
752+-- ...Expected something of kind Symbol or *, got: k -> l on the LHS of ':>'.
753+-- ...Maybe you haven't applied enough arguments to
754+-- ...Capture' '[] "foo"
755+-- ...
756+--
757+-- >>> undefined :: Server (Capture "foo" :> Get '[JSON] Int)
758+-- ...
759+-- ...Expected something of kind Symbol or *, got: k -> l on the LHS of ':>'.
760+-- ...Maybe you haven't applied enough arguments to
761+-- ...Capture' '[] "foo"
762+-- ...
763+--
764+instance TypeError (HasServerArrowKindError arr) => HasServer ((arr :: k -> l) :> api) context
825765 where
826− type ServerT (arr :> sub) _ = TypeError (PartialApplication (HasServer :: * -> [*] -> Constraint) arr)
827− route = error "unreachable"
828− hoistServerWithContext _ _ _ _ = error "unreachable"
766+ type ServerT (arr :> api) m = TypeError (HasServerArrowKindError arr)
767+ -- it doens't really matter what sub route we peak
768+ route _ _ _ = error "servant-server panic: impossible happened in HasServer (arr :> api)"
769+ hoistServerWithContext _ _ _ = id
770+
771+-- Cannot have TypeError here, otherwise use of this symbol will error :)
772+type HasServerArrowKindError arr =
773+ 'Text "Expected something of kind Symbol or *, got: k -> l on the LHS of ':>'."
774+ ':$$: 'Text "Maybe you haven't applied enough arguments to"
775+ ':$$: 'ShowType arr
829776
830777-- | This instance prevents from accidentally using '->' instead of ':>'
831778--
@@ -859,103 +806,7 @@ type HasServerArrowTypeError a b =
859806 ':$$: 'ShowType a
860807 ':$$: 'Text "and"
861808 ':$$: 'ShowType b
862−
863−-- Erroring instances for 'HasServer' for unknown API combinators
864−
865−-- XXX: This omits the @context@ parameter, e.g.:
866−--
867−-- "There is no instance for HasServer (Bool :> …)". Do we care ?
868−instance {-# OVERLAPPABLE #-} TypeError (NoInstanceForSub HasServer ty) => HasServer (ty :> sub) context
869−
870−instance {-# OVERLAPPABLE #-} TypeError (NoInstanceFor (HasServer api context)) => HasServer api context
871−
872−-- | Ignore @'Fragment'@ in server handlers.
873−-- See <https://ietf.org/rfc/rfc2616.html#section-15.1.3> for more details.
874−--
875−-- Example:
876−--
877−-- > type MyApi = "books" :> Fragment Text :> Get '[JSON] [Book]
878−-- >
879−-- > server :: Server MyApi
880−-- > server = getBooks
881−-- > where getBooks :: Handler [Book]
882−-- > getBooks = ...return all books...
883−instance (AtLeastOneFragment api, FragmentUnique (Fragment a1 :> api), HasServer api context)
884− => HasServer (Fragment a1 :> api) context where
885− type ServerT (Fragment a1 :> api) m = ServerT api m
886−
887− route _ = route (Proxy :: Proxy api)
888−
889− hoistServerWithContext _ = hoistServerWithContext (Proxy :: Proxy api)
809+#endif
890810
891811-- $setup
892812-- >>> import Servant
893−
894−-- | A type that specifies that an API record contains a server implementation.
895−data AsServerT (m :: * -> *)
896−instance GenericMode (AsServerT m) where
897− type AsServerT m :- api = ServerT api m
898−
899−type AsServer = AsServerT Handler
900−
901−
902−-- | Set of constraints required to convert to / from vanilla server types.
903−type GServerConstraints api m =
904− ( ToServant api (AsServerT m) ~ ServerT (ToServantApi api) m
905− , GServantProduct (Rep (api (AsServerT m)))
906− )
907−
908−-- | This class is a necessary evil: in the implementation of 'HasServer' for
909−-- @'NamedRoutes' api@, we essentially need the quantified constraint @forall
910−-- m. 'GServerConstraints' m@ to hold.
911−--
912−-- We cannot require do that directly as the definition of 'GServerConstraints'
913−-- contains type family applications ('Rep' and 'ServerT'). The trick is to hide
914−-- those type family applications behind a typeclass providing evidence for
915−-- @'GServerConstraints' api m@ in the form of a dictionary, and require that
916−-- @forall m. 'GServer' api m@ instead.
917−--
918−-- Users shouldn't have to worry about this class, as the only possible instance
919−-- is provided in this module for all record APIs.
920−
921−class GServer (api :: * -> *) (m :: * -> *) where
922− gServerProof :: Dict (GServerConstraints api m)
923−
924−instance
925− ( ToServant api (AsServerT m) ~ ServerT (ToServantApi api) m
926− , GServantProduct (Rep (api (AsServerT m)))
927− ) => GServer api m where
928− gServerProof = Dict
929−
930−instance
931− ( HasServer (ToServantApi api) context
932− , forall m. Generic (api (AsServerT m))
933− , forall m. GServer api m
934− ) => HasServer (NamedRoutes api) context where
935−
936− type ServerT (NamedRoutes api) m = api (AsServerT m)
937−
938− route
939− :: Proxy (NamedRoutes api)
940− -> Context context
941− -> Delayed env (api (AsServerT Handler))
942− -> Router env
943− route _ ctx delayed =
944− case gServerProof @api @Handler of
945− Dict -> route (Proxy @(ToServantApi api)) ctx (toServant <$> delayed)
946−
947− hoistServerWithContext
948− :: forall m n. Proxy (NamedRoutes api)
949− -> Proxy context
950− -> (forall x. m x -> n x)
951− -> api (AsServerT m)
952− -> api (AsServerT n)
953− hoistServerWithContext _ pctx nat server =
954− case (gServerProof @api @m, gServerProof @api @n) of
955− (Dict, Dict) ->
956− fromServant servantSrvN
957− where
958− servantSrvM :: ServerT (ToServantApi api) m =
959− toServant server
960− servantSrvN :: ServerT (ToServantApi api) n =
961− hoistServerWithContext (Proxy @(ToServantApi api)) pctx nat servantSrvM