summaryrefslogtreecommitdiff
path: root/src/Network/KRPC/Manager.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Network/KRPC/Manager.hs')
-rw-r--r--src/Network/KRPC/Manager.hs179
1 files changed, 179 insertions, 0 deletions
diff --git a/src/Network/KRPC/Manager.hs b/src/Network/KRPC/Manager.hs
new file mode 100644
index 00000000..9aa1bea7
--- /dev/null
+++ b/src/Network/KRPC/Manager.hs
@@ -0,0 +1,179 @@
1{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE ScopedTypeVariables #-}
3module Network.KRPC.Manager
4 ( MonadKRPC (..)
5 , newManager
6 , query
7 , handler
8 ) where
9
10import Control.Applicative
11import Control.Arrow
12import Control.Concurrent
13--import Control.Exception hiding (Handler)
14import Control.Exception.Lifted as Lifted hiding (Handler)
15import Control.Monad
16import Control.Monad.Trans.Control
17import Control.Monad.IO.Class
18import Data.BEncode as BE
19import Data.ByteString.Char8 as BC
20import Data.ByteString.Lazy as BL
21import Data.IORef
22import Data.List as L
23import Data.Map as M
24import Network.KRPC.Message
25import Network.KRPC.Method
26import Network.Socket
27import Network.Socket.ByteString as BS
28
29
30type KResult = Either KError KResponse
31
32type TransactionCounter = IORef Int
33type CallId = (TransactionId, SockAddr)
34type CallRes = MVar KResult
35type PendingCalls = IORef (Map CallId CallRes)
36
37type HandlerBody m = SockAddr -> BValue -> m (BE.Result BValue)
38type Handler m = (MethodName, HandlerBody m)
39
40data Manager m = Manager
41 { sock :: !Socket
42 , transactionCounter :: {-# UNPACK #-} !TransactionCounter
43 , pendingCalls :: {-# UNPACK #-} !PendingCalls
44 , handlers :: [Handler m]
45 }
46
47class (MonadBaseControl IO m, MonadIO m) => MonadKRPC m where
48 getManager :: m (Manager a)
49
50sockAddrFamily :: SockAddr -> Family
51sockAddrFamily (SockAddrInet _ _ ) = AF_INET
52sockAddrFamily (SockAddrInet6 _ _ _ _) = AF_INET6
53sockAddrFamily (SockAddrUnix _ ) = AF_UNIX
54
55seedTransaction :: Int
56seedTransaction = 0
57
58newManager :: SockAddr -> IO (Manager a)
59newManager servAddr = do
60 sock <- bindServ
61 tran <- newIORef seedTransaction
62 calls <- newIORef M.empty
63 return $ Manager sock tran calls []
64 where
65 bindServ = do
66 let family = sockAddrFamily servAddr
67 sock <- socket family Datagram defaultProtocol
68 when (family == AF_INET6) $ do
69 setSocketOption sock IPv6Only 0
70 bindSocket sock servAddr
71 return sock
72
73sendMessage :: MonadIO m => BEncode a => Socket -> SockAddr -> a -> m ()
74sendMessage sock addr a =
75 liftIO $ sendManyTo sock (BL.toChunks (BE.encode a)) addr
76
77{-----------------------------------------------------------------------
78-- Client
79-----------------------------------------------------------------------}
80
81genTransactionId :: TransactionCounter -> IO TransactionId
82genTransactionId ref = do
83 cur <- atomicModifyIORef' ref $ \ cur -> (succ cur, cur)
84 return $ BC.pack (show cur)
85
86registerQuery :: CallId -> PendingCalls -> IO CallRes
87registerQuery cid ref = do
88 ares <- newEmptyMVar
89 atomicModifyIORef' ref $ \ m -> (M.insert cid ares m, ())
90 return ares
91
92unregisterQuery :: CallId -> PendingCalls -> IO ()
93unregisterQuery cid ref = do
94 atomicModifyIORef' ref $ \ m -> (M.delete cid m, ())
95
96queryResponse :: BEncode a => CallRes -> IO a
97queryResponse ares = do
98 res <- readMVar ares
99 case res of
100 Left e -> throwIO e
101 Right (KResponse {..}) ->
102 case fromBEncode respVals of
103 Left e -> throwIO (KError ProtocolError (BC.pack e) respId)
104 Right a -> return a
105
106query :: forall m a b. (MonadKRPC m, KRPC a b) => SockAddr -> a -> m b
107query addr params = do
108 Manager {..} <- getManager
109 liftIO $ do
110 tid <- genTransactionId transactionCounter
111 let Method name = method :: Method a b
112 let q = KQuery (toBEncode params) name tid
113 ares <- registerQuery (tid, addr) pendingCalls
114 sendMessage sock addr q
115 `onException` unregisterQuery (tid, addr) pendingCalls
116 queryResponse ares
117
118{-----------------------------------------------------------------------
119-- Handlers
120-----------------------------------------------------------------------}
121
122handler :: forall m a b. (KRPC a b, MonadKRPC m)
123 => (SockAddr -> a -> m b) -> Handler m
124handler body = (name, wrapper)
125 where
126 Method name = method :: Method a b
127 wrapper addr args =
128 case fromBEncode args of
129 Left e -> return $ Left e
130 Right a -> (Right . toBEncode) <$> body addr a
131
132runHandler :: MonadKRPC m => HandlerBody m -> SockAddr -> KQuery -> m KResult
133runHandler handler addr KQuery {..} = wrapper `Lifted.catch` failback
134 where
135 wrapper = ((`decodeError` queryId) +++ (`KResponse` queryId))
136 <$> handler addr queryArgs
137 failback e = return $ Left $ serverError e queryId
138
139dispatchHandler :: MonadKRPC m => KQuery -> SockAddr -> m KResult
140dispatchHandler q @ KQuery {..} addr = do
141 Manager {..} <- getManager
142 case L.lookup queryMethod handlers of
143 Nothing -> return $ Left $ unknownMethod queryMethod queryId
144 Just handler -> runHandler handler addr q
145
146{-----------------------------------------------------------------------
147-- Listener
148-----------------------------------------------------------------------}
149
150handleQuery :: MonadKRPC m => KQuery -> SockAddr -> m ()
151handleQuery q addr = do
152 Manager {..} <- getManager
153 res <- dispatchHandler q addr
154 sendMessage sock addr $ either toBEncode toBEncode res
155
156handleResponse :: MonadKRPC m => KResult -> SockAddr -> m ()
157handleResponse result addr = do
158 Manager {..} <- getManager
159 mcall <- undefined (addr, respId) pendingCalls
160 case mcall of
161 Nothing -> return ()
162 Just ares -> liftIO $ putMVar ares result
163
164handleMessage :: MonadKRPC m => KMessage -> SockAddr -> m ()
165handleMessage (Q q) = handleQuery q
166handleMessage (R r) = handleResponse (Right r)
167handleMessage (E e) = handleResponse (Left e)
168
169maxMsgSize :: Int
170maxMsgSize = 64 * 1024
171
172listener :: MonadKRPC m => m ()
173listener = do
174 Manager {..} <- getManager
175 forever $ do
176 (bs, addr) <- liftIO $ BS.recvFrom sock maxMsgSize
177 case BE.decode bs of
178 Left e -> liftIO $ sendMessage sock addr $ unknownMessage e
179 Right m -> handleMessage m addr