summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Network/KRPC.hs217
-rw-r--r--src/Network/KRPC/Protocol.hs28
2 files changed, 55 insertions, 190 deletions
diff --git a/src/Network/KRPC.hs b/src/Network/KRPC.hs
index f891d5a0..d295a965 100644
--- a/src/Network/KRPC.hs
+++ b/src/Network/KRPC.hs
@@ -93,45 +93,45 @@
93{-# LANGUAGE ExplicitForAll #-} 93{-# LANGUAGE ExplicitForAll #-}
94{-# LANGUAGE KindSignatures #-} 94{-# LANGUAGE KindSignatures #-}
95{-# LANGUAGE ScopedTypeVariables #-} 95{-# LANGUAGE ScopedTypeVariables #-}
96{-# LANGUAGE DeriveGeneric #-} 96{-# LANGUAGE MultiParamTypeClasses #-}
97{-# LANGUAGE GeneralizedNewtypeDeriving #-}
98{-# LANGUAGE FunctionalDependencies #-}
97module Network.KRPC 99module Network.KRPC
98 ( -- * Method 100 ( KRPC (..)
99 Method(..) 101
100 , method 102 -- * Exception
101 , idM 103 , KError (..)
104
105 -- * Method
106 , Method(..)
102 107
103 -- * Client 108 -- * Client
104 , call 109 , call
105 110
106 -- * Server 111 -- * Server
107 , MethodHandler 112 , MethodHandler
108 , (==>) 113 , handler
109 , (==>@)
110 , server 114 , server
111
112 -- * Internal
113 , call_
114 , withRemote
115 ) where 115 ) where
116 116
117import Control.Applicative
118import Control.Exception 117import Control.Exception
119import Control.Monad.Trans.Control 118import Control.Monad.Trans.Control
120import Control.Monad.IO.Class 119import Control.Monad.IO.Class
121import Data.BEncode as BE 120import Data.BEncode as BE
122import Data.BEncode.BDict as BE
123import Data.BEncode.Types as BE
124import Data.ByteString.Char8 as BC 121import Data.ByteString.Char8 as BC
125import Data.List as L 122import Data.List as L
126import Data.Monoid 123import Data.Monoid
124import Data.String
127import Data.Typeable 125import Data.Typeable
128import Network 126import Network
129import Network.Socket 127import Network.Socket
130import GHC.Generics
131 128
132import Network.KRPC.Protocol 129import Network.KRPC.Protocol
133 130
134 131
132class (BEncode req, BEncode resp) => KRPC req resp | req -> resp where
133 method :: Method req resp
134
135-- | Method datatype used to describe name, parameters and return 135-- | Method datatype used to describe name, parameters and return
136-- values of procedure. Client use a method to /invoke/, server 136-- values of procedure. Client use a method to /invoke/, server
137-- /implements/ the method to make the actual work. 137-- /implements/ the method to make the actual work.
@@ -147,187 +147,62 @@ import Network.KRPC.Protocol
147-- exsample @Method (Foo, Bar) (Bar, Foo)@ will take two arguments 147-- exsample @Method (Foo, Bar) (Bar, Foo)@ will take two arguments
148-- and return two values. 148-- and return two values.
149-- 149--
150-- To pass raw dictionaries you should specify empty param list: 150newtype Method param result = Method MethodName
151-- 151 deriving (Eq, Ord, IsString, BEncode)
152-- > method "my_method" [] [] :: Method BEncode BEncode
153--
154-- In this case you should handle dictionary extraction by hand, both
155-- in client and server.
156--
157data Method param result = Method {
158 -- | Name used in query.
159 methodName :: MethodName
160
161 -- | Name of each parameter in /right to left/ order.
162 , methodParams :: [ParamName]
163
164 -- | Name of each return value in /right to left/ order.
165 , methodVals :: [ValName]
166 } deriving (Eq, Ord, Generic)
167
168instance BEncode (Method a b)
169 152
170instance (Typeable a, Typeable b) => Show (Method a b) where 153instance (Typeable a, Typeable b) => Show (Method a b) where
171 showsPrec _ = showsMethod 154 showsPrec _ = showsMethod
172 155
173showsMethod 156showsMethod :: forall a. forall b. Typeable a => Typeable b
174 :: forall a. forall b. 157 => Method a b -> ShowS
175 Typeable a => Typeable b 158showsMethod (Method name) =
176 => Method a b -> ShowS 159 shows name <>
177showsMethod Method {..} =
178 showString (BC.unpack methodName) <>
179 showString " :: " <> 160 showString " :: " <>
180 showsTuple methodParams paramsTy <> 161 shows paramsTy <>
181 showString " -> " <> 162 showString " -> " <>
182 showsTuple methodVals valuesTy 163 shows valuesTy
183 where
184 paramsTy = typeOf (error "KRPC.showsMethod: impossible" :: a)
185 valuesTy = typeOf (error "KRPC.showsMethod: impossible" :: b)
186
187 showsTuple ns ty
188 = showChar '('
189 <> mconcat (L.intersperse (showString ", ") $
190 L.zipWith showsTyArgName ns (detuple ty))
191 <> showChar ')'
192
193 showsTyArgName ns ty
194 = showString (BC.unpack ns)
195 <> showString " :: "
196 <> showString (show ty)
197
198 detuple tyRep
199 | L.null args = [tyRep]
200 | otherwise = args
201 where
202 args = typeRepArgs tyRep
203
204
205-- | Identity procedure signature. Could be used for echo
206-- servers. Implemented as:
207--
208-- > idM = method "id" ["x"] ["y"]
209--
210idM :: Method a a
211idM = method "id" ["x"] ["y"]
212{-# INLINE idM #-}
213
214-- | Makes method signature. Note that order of parameters and return
215-- values are not important as long as corresponding names and types
216-- are match. For exsample this is the equal definitions:
217--
218-- > methodA : Method (Foo, Bar) (Baz, Quux)
219-- > methodA = method "mymethod" ["a", "b"] ["c", "d"]
220--
221-- > methodA : Method (Bar, Foo) (Quux, Baz)
222-- > methodB = method "mymethod" ["b", "a"] ["d", "c"]
223--
224method :: MethodName -> [ParamName] -> [ValName] -> Method param result
225method = Method
226{-# INLINE method #-}
227
228lookupKey :: ParamName -> BDict -> Result BValue
229lookupKey x = maybe (Left ("not found key " ++ BC.unpack x)) Right . BE.lookup x
230
231extractArgs :: [ParamName] -> BDict -> Result BValue
232extractArgs [] d = Right $ if BE.null d then BList [] else BDict d
233extractArgs [x] d = lookupKey x d
234extractArgs xs d = BList <$> mapM (`lookupKey` d) xs
235{-# INLINE extractArgs #-}
236
237zipBDict :: [BKey] -> [BValue] -> BDict
238zipBDict (k : ks) (v : vs) = Cons k v (zipBDict ks vs)
239zipBDict _ _ = Nil
240
241injectVals :: [ParamName] -> BValue -> BDict
242injectVals [] (BList []) = BE.empty
243injectVals [] (BDict d ) = d
244injectVals [] be = invalidParamList [] be
245injectVals [p] arg = BE.singleton p arg
246injectVals ps (BList as) = zipBDict ps as
247injectVals ps be = invalidParamList ps be
248{-# INLINE injectVals #-}
249
250invalidParamList :: [ParamName] -> BValue -> a
251invalidParamList pl be
252 = error $ "KRPC invalid parameter list: " ++ show pl ++ "\n" ++
253 "while procedure args are: " ++ show be
254
255queryCall :: BEncode param => Socket -> SockAddr
256 -> Method param result -> param -> IO ()
257queryCall sock addr m arg = sendMessage q addr sock
258 where 164 where
259 q = kquery (methodName m) (injectVals (methodParams m) (toBEncode arg)) 165 impossible = error "KRPC.showsMethod: impossible"
166 paramsTy = typeOf (impossible :: a)
167 valuesTy = typeOf (impossible :: b)
260 168
261getResult :: BEncode result => Socket -> Method param result -> IO result
262getResult sock m = do
263 resp <- recvResponse sock
264 case resp of
265 Left e -> throw e
266 Right (respVals -> dict) -> do
267 case fromBEncode =<< extractArgs (methodVals m) dict of
268 Right vals -> return vals
269 Left e -> throw (ProtocolError (BC.pack e))
270 169
170getResult :: BEncode result => Socket -> IO result
171getResult sock = do
172 resp <- either throw (return . respVals) =<< recvResponse sock
173 either (throw . ProtocolError . BC.pack) return $ fromBEncode resp
271 174
272-- | Makes remote procedure call. Throws RPCException on any error 175-- | Makes remote procedure call. Throws RPCException on any error
273-- occurred. 176-- occurred.
274call :: (MonadBaseControl IO host, MonadIO host) 177call :: forall req resp host.
275 => (BEncode param, BEncode result) 178 (MonadBaseControl IO host, MonadIO host, KRPC req resp)
276 => SockAddr -- ^ Address of callee. 179 => SockAddr -> req -> host resp
277 -> Method param result -- ^ Procedure to call. 180call addr arg = liftIO $ withRemote $ \sock -> do
278 -> param -- ^ Arguments passed by callee to procedure. 181 sendMessage (KQuery name (toBEncode arg)) addr sock
279 -> host result -- ^ Values returned by callee from the procedure. 182 getResult sock
280call addr m arg = liftIO $ withRemote $ \sock -> do call_ sock addr m arg 183 where
281 184 Method name = method :: Method req resp
282-- | The same as 'call' but use already opened socket.
283call_ :: (MonadBaseControl IO host, MonadIO host)
284 => (BEncode param, BEncode result)
285 => Socket -- ^ Socket to use
286 -> SockAddr -- ^ Address of callee.
287 -> Method param result -- ^ Procedure to call.
288 -> param -- ^ Arguments passed by callee to procedure.
289 -> host result -- ^ Values returned by callee from the procedure.
290call_ sock addr m arg = liftIO $ do
291 queryCall sock addr m arg
292 getResult sock m
293
294 185
295type HandlerBody remote = SockAddr -> KQuery -> remote (Either KError KResponse) 186type HandlerBody remote = SockAddr -> KQuery -> remote (Either KError KResponse)
296 187
297-- | Procedure signature and implementation binded up. 188-- | Procedure signature and implementation binded up.
298type MethodHandler remote = (MethodName, HandlerBody remote) 189type MethodHandler remote = (MethodName, HandlerBody remote)
299 190
300-- we can safely erase types in (==>)
301-- | Assign method implementation to the method signature.
302(==>) :: forall (remote :: * -> *) (param :: *) (result :: *).
303 (BEncode param, BEncode result)
304 => Monad remote
305 => Method param result -- ^ Signature.
306 -> (param -> remote result) -- ^ Implementation.
307 -> MethodHandler remote -- ^ Handler used by server.
308{-# INLINE (==>) #-}
309m ==> body = m ==>@ const body
310infix 1 ==>
311
312-- | Similar to '==>@' but additionally pass caller address. 191-- | Similar to '==>@' but additionally pass caller address.
313(==>@) :: forall (remote :: * -> *) (param :: *) (result :: *). 192handler :: forall (remote :: * -> *) (req :: *) (resp :: *).
314 (BEncode param, BEncode result) 193 (KRPC req resp, Monad remote)
315 => Monad remote 194 => (SockAddr -> req -> remote resp) -> MethodHandler remote
316 => Method param result -- ^ Signature. 195handler body = (name, newbody)
317 -> (SockAddr -> param -> remote result) -- ^ Implementation.
318 -> MethodHandler remote -- ^ Handler used by server.
319{-# INLINE (==>@) #-}
320m ==>@ body = (methodName m, newbody)
321 where 196 where
197 Method name = method :: Method req resp
198
322 {-# INLINE newbody #-} 199 {-# INLINE newbody #-}
323 newbody addr q = 200 newbody addr q =
324 case fromBEncode =<< extractArgs (methodParams m) (queryArgs q) of 201 case fromBEncode (queryArgs q) of
325 Left e -> return (Left (ProtocolError (BC.pack e))) 202 Left e -> return (Left (ProtocolError (BC.pack e)))
326 Right a -> do 203 Right a -> do
327 r <- body addr a 204 r <- body addr a
328 return (Right (kresponse (injectVals (methodVals m) (toBEncode r)))) 205 return (Right (KResponse (toBEncode r)))
329
330infix 1 ==>@
331 206
332-- | Run RPC server on specified port by using list of handlers. 207-- | Run RPC server on specified port by using list of handlers.
333-- Server will dispatch procedure specified by callee, but note that 208-- Server will dispatch procedure specified by callee, but note that
diff --git a/src/Network/KRPC/Protocol.hs b/src/Network/KRPC/Protocol.hs
index adc02b5f..5b072d79 100644
--- a/src/Network/KRPC/Protocol.hs
+++ b/src/Network/KRPC/Protocol.hs
@@ -26,15 +26,13 @@ module Network.KRPC.Protocol
26 , mkKError 26 , mkKError
27 27
28 -- * Query 28 -- * Query
29 , KQuery(queryMethod, queryArgs) 29 , KQuery(..)
30 , MethodName 30 , MethodName
31 , ParamName 31 , ParamName
32 , kquery
33 32
34 -- * Response 33 -- * Response
35 , KResponse(respVals) 34 , KResponse(..)
36 , ValName 35 , ValName
37 , kresponse
38 36
39 , sendMessage 37 , sendMessage
40 , recvResponse 38 , recvResponse
@@ -134,16 +132,16 @@ type ParamName = ByteString
134-- 132--
135-- > { "y" : "q", "q" : "<method_name>", "a" : [<arg1>, <arg2>, ...] } 133-- > { "y" : "q", "q" : "<method_name>", "a" : [<arg1>, <arg2>, ...] }
136-- 134--
137data KQuery = KQuery { 135data KQuery = KQuery
138 queryMethod :: !MethodName 136 { queryMethod :: !MethodName
139 , queryArgs :: BDict 137 , queryArgs :: !BValue
140 } deriving (Show, Read, Eq, Ord, Typeable) 138 } deriving (Show, Read, Eq, Ord, Typeable)
141 139
142instance BEncode KQuery where 140instance BEncode KQuery where
143 {-# SPECIALIZE instance BEncode KQuery #-} 141 {-# SPECIALIZE instance BEncode KQuery #-}
144 {-# INLINE toBEncode #-} 142 {-# INLINE toBEncode #-}
145 toBEncode (KQuery m args) = toDict $ 143 toBEncode (KQuery m args) = toDict $
146 "a" .=! BDict args 144 "a" .=! args
147 .: "q" .=! m 145 .: "q" .=! m
148 .: "y" .=! ("q" :: ByteString) 146 .: "y" .=! ("q" :: ByteString)
149 .: endDict 147 .: endDict
@@ -157,11 +155,6 @@ instance BEncode KQuery where
157 155
158 fromBEncode _ = decodingError "KQuery" 156 fromBEncode _ = decodingError "KQuery"
159 157
160kquery :: MethodName -> BDict -> KQuery
161kquery = KQuery
162{-# INLINE kquery #-}
163
164
165type ValName = ByteString 158type ValName = ByteString
166 159
167-- | KResponse used to signal that callee successufully process a 160-- | KResponse used to signal that callee successufully process a
@@ -173,8 +166,9 @@ type ValName = ByteString
173-- 166--
174-- > { "y" : "r", "r" : [<val1>, <val2>, ...] } 167-- > { "y" : "r", "r" : [<val1>, <val2>, ...] }
175-- 168--
176newtype KResponse = KResponse { respVals :: BDict } 169newtype KResponse = KResponse
177 deriving (Show, Read, Eq, Ord, Typeable) 170 { respVals :: BValue
171 } deriving (Show, Read, Eq, Ord, Typeable)
178 172
179instance BEncode KResponse where 173instance BEncode KResponse where
180 {-# INLINE toBEncode #-} 174 {-# INLINE toBEncode #-}
@@ -190,10 +184,6 @@ instance BEncode KResponse where
190 184
191 fromBEncode _ = decodingError "KDict" 185 fromBEncode _ = decodingError "KDict"
192 186
193kresponse :: BDict -> KResponse
194kresponse = KResponse
195{-# INLINE kresponse #-}
196
197sockAddrFamily :: SockAddr -> Family 187sockAddrFamily :: SockAddr -> Family
198sockAddrFamily (SockAddrInet _ _ ) = AF_INET 188sockAddrFamily (SockAddrInet _ _ ) = AF_INET
199sockAddrFamily (SockAddrInet6 _ _ _ _) = AF_INET6 189sockAddrFamily (SockAddrInet6 _ _ _ _) = AF_INET6