1
1
{-# LANGUAGE CPP #-}
2
2
{-# LANGUAGE DeriveFunctor #-}
3
- {-# LANGUAGE OverloadedStrings #-}
4
- {-# LANGUAGE TypeOperators #-}
3
+ {-# LANGUAGE FlexibleInstances #-}
5
4
{-# LANGUAGE GADTs #-}
5
+ {-# LANGUAGE GeneralizedNewtypeDeriving #-}
6
6
{-# LANGUAGE KindSignatures #-}
7
+ {-# LANGUAGE MultiParamTypeClasses #-}
8
+ {-# LANGUAGE OverloadedStrings #-}
7
9
{-# LANGUAGE RecordWildCards #-}
8
10
{-# LANGUAGE StandaloneDeriving #-}
9
11
{-# LANGUAGE TupleSections #-}
12
+ {-# LANGUAGE TypeFamilies #-}
13
+ {-# LANGUAGE TypeOperators #-}
14
+ {-# LANGUAGE UndecidableInstances #-}
10
15
module Servant.Server.Internal.RoutingApplication where
11
16
12
- import Control.Exception (finally )
13
17
import Control.Monad (ap , liftM )
14
- import Control.Monad.Trans (MonadIO (.. ))
15
- import Data.IORef (newIORef , readIORef , IORef , atomicModifyIORef )
16
- import Network.Wai (Application , Request ,
17
- Response , ResponseReceived )
18
+ import Control.Monad.Base (MonadBase (.. ))
19
+ import Control.Monad.Catch (MonadThrow (.. ))
20
+ import Control.Monad.Reader (MonadReader (.. ), ReaderT , runReaderT )
21
+ import Control.Monad.Trans (MonadIO (.. ), MonadTrans (.. ))
22
+ import Control.Monad.Trans.Control (ComposeSt , MonadBaseControl (.. ), MonadTransControl (.. ),
23
+ defaultLiftBaseWith , defaultRestoreM )
24
+ import Control.Monad.Trans.Resource (MonadResource (.. ), ResourceT , runResourceT , transResourceT )
25
+ import Network.Wai (Application , Request , Response , ResponseReceived )
18
26
import Prelude ()
19
27
import Prelude.Compat
20
- import Servant.Server.Internal.ServantErr
21
28
import Servant.Server.Internal.Handler
29
+ import Servant.Server.Internal.ServantErr
22
30
23
31
type RoutingApplication =
24
32
Request -- ^ the request, the field 'pathInfo' may be modified by url routing
@@ -32,6 +40,55 @@ data RouteResult a =
32
40
| Route ! a
33
41
deriving (Eq , Show , Read , Functor )
34
42
43
+ instance Applicative RouteResult where
44
+ pure = return
45
+ (<*>) = ap
46
+
47
+ instance Monad RouteResult where
48
+ return = Route
49
+ Route a >>= f = f a
50
+ Fail e >>= _ = Fail e
51
+ FailFatal e >>= _ = FailFatal e
52
+
53
+ newtype RouteResultT m a = RouteResultT { runRouteResultT :: m (RouteResult a ) }
54
+ deriving (Functor )
55
+
56
+ instance MonadTrans RouteResultT where
57
+ lift = RouteResultT . liftM Route
58
+
59
+ instance (Functor m , Monad m ) => Applicative (RouteResultT m ) where
60
+ pure = return
61
+ (<*>) = ap
62
+
63
+ instance Monad m => Monad (RouteResultT m ) where
64
+ return = RouteResultT . return . Route
65
+ m >>= k = RouteResultT $ do
66
+ a <- runRouteResultT m
67
+ case a of
68
+ Fail e -> return $ Fail e
69
+ FailFatal e -> return $ FailFatal e
70
+ Route b -> runRouteResultT (k b)
71
+
72
+ instance MonadIO m => MonadIO (RouteResultT m ) where
73
+ liftIO = lift . liftIO
74
+
75
+ instance MonadBase b m => MonadBase b (RouteResultT m ) where
76
+ liftBase = lift . liftBase
77
+
78
+ instance MonadBaseControl b m => MonadBaseControl b (RouteResultT m ) where
79
+ type StM (RouteResultT m ) a = ComposeSt RouteResultT m a
80
+ liftBaseWith = defaultLiftBaseWith
81
+ restoreM = defaultRestoreM
82
+
83
+ instance MonadTransControl RouteResultT where
84
+ type StT RouteResultT a = RouteResult a
85
+ liftWith f = RouteResultT $ liftM return $ f $ runRouteResultT
86
+ restoreT = RouteResultT
87
+
88
+ instance MonadThrow m => MonadThrow (RouteResultT m ) where
89
+ throwM = lift . throwM
90
+
91
+
35
92
toApplication :: RoutingApplication -> Application
36
93
toApplication ra request respond = ra request routingRespond
37
94
where
@@ -115,53 +172,30 @@ instance Functor (Delayed env) where
115
172
, ..
116
173
} -- Note [Existential Record Update]
117
174
118
- -- | A mutable cleanup action
119
- newtype CleanupRef = CleanupRef (IORef (IO () ))
120
-
121
- newCleanupRef :: IO CleanupRef
122
- newCleanupRef = CleanupRef <$> newIORef (return () )
123
-
124
- -- | Add a clean up action to a 'CleanupRef'
125
- addCleanup' :: IO () -> CleanupRef -> IO ()
126
- addCleanup' act (CleanupRef ref) = atomicModifyIORef ref (\ act' -> (act' >> act, () ))
127
-
128
- addCleanup :: IO () -> DelayedIO ()
129
- addCleanup act = DelayedIO $ \ _req cleanupRef ->
130
- addCleanup' act cleanupRef >> return (Route () )
131
-
132
- -- | Run all the clean up actions registered in
133
- -- a 'CleanupRef'.
134
- runCleanup :: CleanupRef -> IO ()
135
- runCleanup (CleanupRef ref) = do
136
- act <- readIORef ref
137
- act
138
-
139
175
-- | Computations used in a 'Delayed' can depend on the
140
176
-- incoming 'Request', may perform 'IO, and result in a
141
177
-- 'RouteResult, meaning they can either suceed, fail
142
178
-- (with the possibility to recover), or fail fatally.
143
179
--
144
- newtype DelayedIO a = DelayedIO { runDelayedIO :: Request -> CleanupRef -> IO (RouteResult a ) }
145
-
146
- instance Functor DelayedIO where
147
- fmap = liftM
180
+ newtype DelayedIO a = DelayedIO { runDelayedIO' :: ReaderT Request (ResourceT (RouteResultT IO )) a }
181
+ deriving
182
+ ( Functor , Applicative , Monad
183
+ , MonadIO , MonadReader Request
184
+ , MonadBase IO
185
+ , MonadThrow
186
+ , MonadResource
187
+ )
148
188
149
- instance Applicative DelayedIO where
150
- pure = return
151
- (<*>) = ap
189
+ liftRouteResult :: RouteResult a -> DelayedIO a
190
+ liftRouteResult x = DelayedIO $ lift . lift $ RouteResultT . return $ x
152
191
153
- instance Monad DelayedIO where
154
- return x = DelayedIO (\ _req _cleanup -> return (Route x))
155
- DelayedIO m >>= f =
156
- DelayedIO $ \ req cl -> do
157
- r <- m req cl
158
- case r of
159
- Fail e -> return $ Fail e
160
- FailFatal e -> return $ FailFatal e
161
- Route a -> runDelayedIO (f a) req cl
192
+ instance MonadBaseControl IO DelayedIO where
193
+ type StM DelayedIO a = StM (ReaderT Request (ResourceT (RouteResultT IO ))) a
194
+ liftBaseWith f = DelayedIO $ liftBaseWith $ \ g -> f (g . runDelayedIO')
195
+ restoreM = DelayedIO . restoreM
162
196
163
- instance MonadIO DelayedIO where
164
- liftIO m = DelayedIO ( \ _req _cl -> Route <$> m)
197
+ runDelayedIO :: DelayedIO a -> Request -> ResourceT IO ( RouteResult a )
198
+ runDelayedIO m req = transResourceT runRouteResultT $ runReaderT (runDelayedIO' m) req
165
199
166
200
-- | A 'Delayed' without any stored checks.
167
201
emptyDelayed :: RouteResult a -> Delayed env a
@@ -172,15 +206,17 @@ emptyDelayed result =
172
206
173
207
-- | Fail with the option to recover.
174
208
delayedFail :: ServantErr -> DelayedIO a
175
- delayedFail err = DelayedIO ( \ _req _cleanup -> return $ Fail err)
209
+ delayedFail err = liftRouteResult $ Fail err
176
210
177
211
-- | Fail fatally, i.e., without any option to recover.
178
212
delayedFailFatal :: ServantErr -> DelayedIO a
179
- delayedFailFatal err = DelayedIO ( \ _req _cleanup -> return $ FailFatal err)
213
+ delayedFailFatal err = liftRouteResult $ FailFatal err
180
214
181
215
-- | Gain access to the incoming request.
182
216
withRequest :: (Request -> DelayedIO a ) -> DelayedIO a
183
- withRequest f = DelayedIO (\ req cl -> runDelayedIO (f req) req cl)
217
+ withRequest f = do
218
+ req <- ask
219
+ f req
184
220
185
221
-- | Add a capture to the end of the capture block.
186
222
addCapture :: Delayed env (a -> b )
@@ -264,18 +300,17 @@ passToServer Delayed{..} x =
264
300
runDelayed :: Delayed env a
265
301
-> env
266
302
-> Request
267
- -> CleanupRef
268
- -> IO (RouteResult a )
269
- runDelayed Delayed {.. } env req cleanupRef =
303
+ -> ResourceT IO (RouteResult a )
304
+ runDelayed Delayed {.. } env req =
270
305
runDelayedIO
271
306
(do c <- capturesD env
272
307
methodD
273
308
a <- authD
274
309
b <- bodyD
275
- DelayedIO $ \ r _cleanup -> return (serverD c a b r)
310
+ r <- ask
311
+ liftRouteResult (serverD c a b r)
276
312
)
277
313
req
278
- cleanupRef
279
314
280
315
-- | Runs a delayed server and the resulting action.
281
316
-- Takes a continuation that lets us send a response.
@@ -287,15 +322,12 @@ runAction :: Delayed env (Handler a)
287
322
-> (RouteResult Response -> IO r )
288
323
-> (a -> RouteResult Response )
289
324
-> IO r
290
- runAction action env req respond k = do
291
- cleanupRef <- newCleanupRef
292
- (runDelayed action env req cleanupRef >>= go >>= respond)
293
- `finally` runCleanup cleanupRef
294
-
325
+ runAction action env req respond k = runResourceT $ do
326
+ runDelayed action env req >>= go >>= liftIO . respond
295
327
where
296
328
go (Fail e) = return $ Fail e
297
329
go (FailFatal e) = return $ FailFatal e
298
- go (Route a) = do
330
+ go (Route a) = liftIO $ do
299
331
e <- runHandler a
300
332
case e of
301
333
Left err -> return . Route $ responseServantErr err
0 commit comments