summaryrefslogtreecommitdiff
path: root/src/Network/KRPC.hs
diff options
context:
space:
mode:
authorSam Truzjan <pxqr.sta@gmail.com>2013-12-19 17:18:27 +0400
committerSam Truzjan <pxqr.sta@gmail.com>2013-12-19 17:18:27 +0400
commit621c73c849332a9446c6e5b9bcd557b30884b318 (patch)
tree16e29d593dbe676a229b945d695ab50a7e0c2556 /src/Network/KRPC.hs
parent8048000a4ce6df959f2fd5f6fd4fe70cff579d15 (diff)
Move all socket stuff to KRPC module
Diffstat (limited to 'src/Network/KRPC.hs')
-rw-r--r--src/Network/KRPC.hs66
1 files changed, 65 insertions, 1 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.