diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Network/KRPC.hs | 66 | ||||
-rw-r--r-- | src/Network/KRPC/Protocol.hs | 81 |
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 | ||
117 | import Control.Exception | 117 | import Control.Applicative |
118 | import Control.Exception.Lifted as Lifted | ||
119 | import Control.Monad | ||
118 | import Control.Monad.Trans.Control | 120 | import Control.Monad.Trans.Control |
119 | import Control.Monad.IO.Class | 121 | import Control.Monad.IO.Class |
120 | import Data.BEncode as BE | 122 | import Data.BEncode as BE |
121 | import Data.ByteString.Char8 as BC | 123 | import Data.ByteString.Char8 as BC |
124 | import Data.ByteString.Lazy as BL | ||
122 | import Data.List as L | 125 | import Data.List as L |
123 | import Data.Monoid | 126 | import Data.Monoid |
124 | import Data.String | 127 | import Data.String |
125 | import Data.Typeable | 128 | import Data.Typeable |
126 | import Network | 129 | import Network |
127 | import Network.Socket | 130 | import Network.Socket |
131 | import Network.Socket.ByteString as BS | ||
128 | 132 | ||
129 | import Network.KRPC.Protocol | 133 | import 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 | |||
177 | sendMessage :: BEncode msg => msg -> SockAddr -> Socket -> IO () | ||
178 | sendMessage msg addr sock = sendManyTo sock (BL.toChunks (encode msg)) addr | ||
179 | {-# INLINE sendMessage #-} | ||
180 | |||
181 | maxMsgSize :: Int | ||
182 | --maxMsgSize = 512 -- release: size of payload of one udp packet | ||
183 | maxMsgSize = 64 * 1024 -- bench: max UDP MTU | ||
184 | {-# INLINE maxMsgSize #-} | ||
185 | |||
186 | recvResponse :: Socket -> IO (Either KError KResponse) | ||
187 | recvResponse 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 | |||
195 | withRemote :: (MonadBaseControl IO m, MonadIO m) => (Socket -> m a) -> m a | ||
196 | withRemote = bracket (liftIO (socket AF_INET6 Datagram defaultProtocol)) | ||
197 | (liftIO . sClose) | ||
198 | {-# SPECIALIZE withRemote :: (Socket -> IO a) -> IO a #-} | ||
169 | 199 | ||
170 | getResult :: BEncode result => Socket -> IO result | 200 | getResult :: BEncode result => Socket -> IO result |
171 | getResult sock = do | 201 | getResult 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 | |||
186 | type HandlerBody remote = SockAddr -> KQuery -> remote (Either KError KResponse) | 220 | type 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 | ||
241 | sockAddrFamily :: SockAddr -> Family | ||
242 | sockAddrFamily (SockAddrInet _ _ ) = AF_INET | ||
243 | sockAddrFamily (SockAddrInet6 _ _ _ _) = AF_INET6 | ||
244 | sockAddrFamily (SockAddrUnix _ ) = AF_UNIX | ||
245 | |||
246 | -- | Run server using a given port. Method invocation should be done manually. | ||
247 | remoteServer :: (MonadBaseControl IO remote, MonadIO remote) | ||
248 | => SockAddr -- ^ Port number to listen. | ||
249 | -> (SockAddr -> KQuery -> remote (Either KError KResponse)) | ||
250 | -> remote () | ||
251 | remoteServer 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 @@ | |||
21 | module Network.KRPC.Protocol | 21 | module 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 | ||
45 | import Control.Applicative | ||
46 | import Control.Exception.Lifted as Lifted | 34 | import Control.Exception.Lifted as Lifted |
47 | import Control.Monad | ||
48 | import Control.Monad.IO.Class | ||
49 | import Control.Monad.Trans.Control | ||
50 | |||
51 | import Data.BEncode as BE | 35 | import Data.BEncode as BE |
52 | import Data.BEncode.BDict as BE | 36 | import Data.BEncode.BDict as BE |
53 | import Data.BEncode.Types as BE | ||
54 | import Data.ByteString as B | 37 | import Data.ByteString as B |
55 | import Data.ByteString.Char8 as BC | 38 | import Data.ByteString.Char8 as BC |
56 | import qualified Data.ByteString.Lazy as LB | ||
57 | import Data.Typeable | 39 | import Data.Typeable |
58 | 40 | ||
59 | import Network.Socket hiding (recvFrom) | ||
60 | import 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 | |||
120 | serverError :: SomeException -> KError | 98 | serverError :: SomeException -> KError |
121 | serverError = ServerError . BC.pack . show | 99 | serverError = ServerError . BC.pack . show |
122 | 100 | ||
123 | |||
124 | type MethodName = ByteString | 101 | type MethodName = ByteString |
125 | type 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 | ||
158 | type 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 | |||
187 | sockAddrFamily :: SockAddr -> Family | ||
188 | sockAddrFamily (SockAddrInet _ _ ) = AF_INET | ||
189 | sockAddrFamily (SockAddrInet6 _ _ _ _) = AF_INET6 | ||
190 | sockAddrFamily (SockAddrUnix _ ) = AF_UNIX | ||
191 | |||
192 | withRemote :: (MonadBaseControl IO m, MonadIO m) => (Socket -> m a) -> m a | ||
193 | withRemote = bracket (liftIO (socket AF_INET6 Datagram defaultProtocol)) | ||
194 | (liftIO . sClose) | ||
195 | {-# SPECIALIZE withRemote :: (Socket -> IO a) -> IO a #-} | ||
196 | |||
197 | maxMsgSize :: Int | ||
198 | --maxMsgSize = 512 -- release: size of payload of one udp packet | ||
199 | maxMsgSize = 64 * 1024 -- bench: max UDP MTU | ||
200 | {-# INLINE maxMsgSize #-} | ||
201 | |||
202 | sendMessage :: BEncode msg => msg -> SockAddr -> Socket -> IO () | ||
203 | sendMessage msg addr sock = sendManyTo sock (LB.toChunks (encode msg)) addr | ||
204 | {-# INLINE sendMessage #-} | ||
205 | |||
206 | recvResponse :: Socket -> IO (Either KError KResponse) | ||
207 | recvResponse 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. | ||
216 | remoteServer :: (MonadBaseControl IO remote, MonadIO remote) | ||
217 | => SockAddr -- ^ Port number to listen. | ||
218 | -> (SockAddr -> KQuery -> remote (Either KError KResponse)) | ||
219 | -> remote () | ||
220 | remoteServer 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)) | ||