summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Network/KRPC.hs66
-rw-r--r--src/Network/KRPC/Protocol.hs81
2 files changed, 66 insertions, 81 deletions
diff --git a/src/Network/KRPC.hs b/src/Network/KRPC.hs
index d295a965..802cddc5 100644
--- a/src/Network/KRPC.hs
+++ b/src/Network/KRPC.hs
@@ -114,17 +114,21 @@ module Network.KRPC
114 , server 114 , server
115 ) where 115 ) where
116 116
117import Control.Exception 117import Control.Applicative
118import Control.Exception.Lifted as Lifted
119import Control.Monad
118import Control.Monad.Trans.Control 120import Control.Monad.Trans.Control
119import Control.Monad.IO.Class 121import Control.Monad.IO.Class
120import Data.BEncode as BE 122import Data.BEncode as BE
121import Data.ByteString.Char8 as BC 123import Data.ByteString.Char8 as BC
124import Data.ByteString.Lazy as BL
122import Data.List as L 125import Data.List as L
123import Data.Monoid 126import Data.Monoid
124import Data.String 127import Data.String
125import Data.Typeable 128import Data.Typeable
126import Network 129import Network
127import Network.Socket 130import Network.Socket
131import Network.Socket.ByteString as BS
128 132
129import Network.KRPC.Protocol 133import Network.KRPC.Protocol
130 134
@@ -166,6 +170,32 @@ showsMethod (Method name) =
166 paramsTy = typeOf (impossible :: a) 170 paramsTy = typeOf (impossible :: a)
167 valuesTy = typeOf (impossible :: b) 171 valuesTy = typeOf (impossible :: b)
168 172
173{-----------------------------------------------------------------------
174-- Client
175-----------------------------------------------------------------------}
176
177sendMessage :: BEncode msg => msg -> SockAddr -> Socket -> IO ()
178sendMessage msg addr sock = sendManyTo sock (BL.toChunks (encode msg)) addr
179{-# INLINE sendMessage #-}
180
181maxMsgSize :: Int
182--maxMsgSize = 512 -- release: size of payload of one udp packet
183maxMsgSize = 64 * 1024 -- bench: max UDP MTU
184{-# INLINE maxMsgSize #-}
185
186recvResponse :: Socket -> IO (Either KError KResponse)
187recvResponse sock = do
188 (raw, _) <- BS.recvFrom sock maxMsgSize
189 return $ case decode raw of
190 Right resp -> Right resp
191 Left decE -> Left $ case decode raw of
192 Right kerror -> kerror
193 _ -> ProtocolError (BC.pack decE)
194
195withRemote :: (MonadBaseControl IO m, MonadIO m) => (Socket -> m a) -> m a
196withRemote = bracket (liftIO (socket AF_INET6 Datagram defaultProtocol))
197 (liftIO . sClose)
198{-# SPECIALIZE withRemote :: (Socket -> IO a) -> IO a #-}
169 199
170getResult :: BEncode result => Socket -> IO result 200getResult :: BEncode result => Socket -> IO result
171getResult sock = do 201getResult sock = do
@@ -183,6 +213,10 @@ call addr arg = liftIO $ withRemote $ \sock -> do
183 where 213 where
184 Method name = method :: Method req resp 214 Method name = method :: Method req resp
185 215
216{-----------------------------------------------------------------------
217-- Server
218-----------------------------------------------------------------------}
219
186type HandlerBody remote = SockAddr -> KQuery -> remote (Either KError KResponse) 220type HandlerBody remote = SockAddr -> KQuery -> remote (Either KError KResponse)
187 221
188-- | Procedure signature and implementation binded up. 222-- | Procedure signature and implementation binded up.
@@ -204,6 +238,36 @@ handler body = (name, newbody)
204 r <- body addr a 238 r <- body addr a
205 return (Right (KResponse (toBEncode r))) 239 return (Right (KResponse (toBEncode r)))
206 240
241sockAddrFamily :: SockAddr -> Family
242sockAddrFamily (SockAddrInet _ _ ) = AF_INET
243sockAddrFamily (SockAddrInet6 _ _ _ _) = AF_INET6
244sockAddrFamily (SockAddrUnix _ ) = AF_UNIX
245
246-- | Run server using a given port. Method invocation should be done manually.
247remoteServer :: (MonadBaseControl IO remote, MonadIO remote)
248 => SockAddr -- ^ Port number to listen.
249 -> (SockAddr -> KQuery -> remote (Either KError KResponse))
250 -> remote ()
251remoteServer servAddr action = bracket (liftIO bindServ) (liftIO . sClose) loop
252 where
253 bindServ = do
254 let family = sockAddrFamily servAddr
255 sock <- socket family Datagram defaultProtocol
256 when (family == AF_INET6) $ do
257 setSocketOption sock IPv6Only 0
258 bindSocket sock servAddr
259 return sock
260
261 loop sock = forever $ do
262 (bs, addr) <- liftIO $ BS.recvFrom sock maxMsgSize
263 reply <- handleMsg bs addr
264 liftIO $ sendMessage reply addr sock
265 where
266 handleMsg bs addr = case decode bs of
267 Right query -> (either toBEncode toBEncode <$> action addr query)
268 `Lifted.catch` (return . toBEncode . serverError)
269 Left decodeE -> return $ toBEncode (ProtocolError (BC.pack decodeE))
270
207-- | Run RPC server on specified port by using list of handlers. 271-- | Run RPC server on specified port by using list of handlers.
208-- Server will dispatch procedure specified by callee, but note that 272-- Server will dispatch procedure specified by callee, but note that
209-- it will not create new thread for each connection. 273-- it will not create new thread for each connection.
diff --git a/src/Network/KRPC/Protocol.hs b/src/Network/KRPC/Protocol.hs
index 5b072d79..55bbdf4e 100644
--- a/src/Network/KRPC/Protocol.hs
+++ b/src/Network/KRPC/Protocol.hs
@@ -21,45 +21,23 @@
21module Network.KRPC.Protocol 21module Network.KRPC.Protocol
22 ( -- * Error 22 ( -- * Error
23 KError(..) 23 KError(..)
24 , ErrorCode 24 , serverError
25 , errorCode
26 , mkKError
27 25
28 -- * Query 26 -- * Query
29 , KQuery(..) 27 , KQuery(..)
30 , MethodName 28 , MethodName
31 , ParamName
32 29
33 -- * Response 30 -- * Response
34 , KResponse(..) 31 , KResponse(..)
35 , ValName
36
37 , sendMessage
38 , recvResponse
39
40 -- * Remote
41 , withRemote
42 , remoteServer
43 ) where 32 ) where
44 33
45import Control.Applicative
46import Control.Exception.Lifted as Lifted 34import Control.Exception.Lifted as Lifted
47import Control.Monad
48import Control.Monad.IO.Class
49import Control.Monad.Trans.Control
50
51import Data.BEncode as BE 35import Data.BEncode as BE
52import Data.BEncode.BDict as BE 36import Data.BEncode.BDict as BE
53import Data.BEncode.Types as BE
54import Data.ByteString as B 37import Data.ByteString as B
55import Data.ByteString.Char8 as BC 38import Data.ByteString.Char8 as BC
56import qualified Data.ByteString.Lazy as LB
57import Data.Typeable 39import Data.Typeable
58 40
59import Network.Socket hiding (recvFrom)
60import Network.Socket.ByteString
61
62
63-- | Errors used to signal that some error occurred while processing a 41-- | Errors used to signal that some error occurred while processing a
64-- procedure call. Error may be send only from server to client but 42-- procedure call. Error may be send only from server to client but
65-- not in the opposite direction. 43-- not in the opposite direction.
@@ -120,9 +98,7 @@ mkKError _ = GenericError
120serverError :: SomeException -> KError 98serverError :: SomeException -> KError
121serverError = ServerError . BC.pack . show 99serverError = ServerError . BC.pack . show
122 100
123
124type MethodName = ByteString 101type MethodName = ByteString
125type ParamName = ByteString
126 102
127-- | Query used to signal that caller want to make procedure call to 103-- | Query used to signal that caller want to make procedure call to
128-- callee and pass arguments in. Therefore query may be only sent from 104-- callee and pass arguments in. Therefore query may be only sent from
@@ -155,8 +131,6 @@ instance BEncode KQuery where
155 131
156 fromBEncode _ = decodingError "KQuery" 132 fromBEncode _ = decodingError "KQuery"
157 133
158type ValName = ByteString
159
160-- | KResponse used to signal that callee successufully process a 134-- | KResponse used to signal that callee successufully process a
161-- procedure call and to return values from procedure. KResponse should 135-- procedure call and to return values from procedure. KResponse should
162-- not be sent if error occurred during RPC. Thus KResponse may be only 136-- not be sent if error occurred during RPC. Thus KResponse may be only
@@ -183,56 +157,3 @@ instance BEncode KResponse where
183 KResponse <$>! "r" 157 KResponse <$>! "r"
184 158
185 fromBEncode _ = decodingError "KDict" 159 fromBEncode _ = decodingError "KDict"
186
187sockAddrFamily :: SockAddr -> Family
188sockAddrFamily (SockAddrInet _ _ ) = AF_INET
189sockAddrFamily (SockAddrInet6 _ _ _ _) = AF_INET6
190sockAddrFamily (SockAddrUnix _ ) = AF_UNIX
191
192withRemote :: (MonadBaseControl IO m, MonadIO m) => (Socket -> m a) -> m a
193withRemote = bracket (liftIO (socket AF_INET6 Datagram defaultProtocol))
194 (liftIO . sClose)
195{-# SPECIALIZE withRemote :: (Socket -> IO a) -> IO a #-}
196
197maxMsgSize :: Int
198--maxMsgSize = 512 -- release: size of payload of one udp packet
199maxMsgSize = 64 * 1024 -- bench: max UDP MTU
200{-# INLINE maxMsgSize #-}
201
202sendMessage :: BEncode msg => msg -> SockAddr -> Socket -> IO ()
203sendMessage msg addr sock = sendManyTo sock (LB.toChunks (encode msg)) addr
204{-# INLINE sendMessage #-}
205
206recvResponse :: Socket -> IO (Either KError KResponse)
207recvResponse sock = do
208 (raw, _) <- recvFrom sock maxMsgSize
209 return $ case decode raw of
210 Right resp -> Right resp
211 Left decE -> Left $ case decode raw of
212 Right kerror -> kerror
213 _ -> ProtocolError (BC.pack decE)
214
215-- | Run server using a given port. Method invocation should be done manually.
216remoteServer :: (MonadBaseControl IO remote, MonadIO remote)
217 => SockAddr -- ^ Port number to listen.
218 -> (SockAddr -> KQuery -> remote (Either KError KResponse))
219 -> remote ()
220remoteServer servAddr action = bracket (liftIO bindServ) (liftIO . sClose) loop
221 where
222 bindServ = do
223 let family = sockAddrFamily servAddr
224 sock <- socket family Datagram defaultProtocol
225 when (family == AF_INET6) $ do
226 setSocketOption sock IPv6Only 0
227 bindSocket sock servAddr
228 return sock
229
230 loop sock = forever $ do
231 (bs, addr) <- liftIO $ recvFrom sock maxMsgSize
232 reply <- handleMsg bs addr
233 liftIO $ sendMessage reply addr sock
234 where
235 handleMsg bs addr = case decode bs of
236 Right query -> (either toBEncode toBEncode <$> action addr query)
237 `Lifted.catch` (return . toBEncode . serverError)
238 Left decodeE -> return $ toBEncode (ProtocolError (BC.pack decodeE))