summaryrefslogtreecommitdiff
path: root/src/Network/KRPC
diff options
context:
space:
mode:
authorSam Truzjan <pxqr.sta@gmail.com>2013-12-22 04:51:06 +0400
committerSam Truzjan <pxqr.sta@gmail.com>2013-12-22 04:51:06 +0400
commitc61a5412e2ca22f6da783182261fbb0d7e8d9217 (patch)
treea0c58246808997071ab2de19ef6781517bf0ba60 /src/Network/KRPC
parenta6358e332e8b2820541fd75a426d92428c27c58f (diff)
Throw exception if query takes too long
Diffstat (limited to 'src/Network/KRPC')
-rw-r--r--src/Network/KRPC/Manager.hs31
-rw-r--r--src/Network/KRPC/Message.hs14
2 files changed, 34 insertions, 11 deletions
diff --git a/src/Network/KRPC/Manager.hs b/src/Network/KRPC/Manager.hs
index 4a3dc93f..0b090e6b 100644
--- a/src/Network/KRPC/Manager.hs
+++ b/src/Network/KRPC/Manager.hs
@@ -37,6 +37,7 @@ import Network.KRPC.Message
37import Network.KRPC.Method 37import Network.KRPC.Method
38import Network.Socket hiding (listen) 38import Network.Socket hiding (listen)
39import Network.Socket.ByteString as BS 39import Network.Socket.ByteString as BS
40import System.Timeout
40 41
41 42
42type KResult = Either KError KResponse 43type KResult = Either KError KResponse
@@ -51,6 +52,7 @@ type Handler h = (MethodName, HandlerBody h)
51 52
52data Manager h = Manager 53data Manager h = Manager
53 { sock :: !Socket 54 { sock :: !Socket
55 , queryTimeout :: !Int -- ^ in seconds
54 , transactionCounter :: {-# UNPACK #-} !TransactionCounter 56 , transactionCounter :: {-# UNPACK #-} !TransactionCounter
55 , pendingCalls :: {-# UNPACK #-} !PendingCalls 57 , pendingCalls :: {-# UNPACK #-} !PendingCalls
56 , handlers :: [Handler h] 58 , handlers :: [Handler h]
@@ -76,12 +78,15 @@ sockAddrFamily (SockAddrUnix _ ) = AF_UNIX
76seedTransaction :: Int 78seedTransaction :: Int
77seedTransaction = 0 79seedTransaction = 0
78 80
81defaultQueryTimeout :: Int
82defaultQueryTimeout = 10
83
79newManager :: SockAddr -> [Handler h] -> IO (Manager h) 84newManager :: SockAddr -> [Handler h] -> IO (Manager h)
80newManager servAddr handlers = do 85newManager servAddr handlers = do
81 sock <- bindServ 86 sock <- bindServ
82 tran <- newIORef seedTransaction 87 tran <- newIORef seedTransaction
83 calls <- newIORef M.empty 88 calls <- newIORef M.empty
84 return $ Manager sock tran calls handlers 89 return $ Manager sock defaultQueryTimeout tran calls handlers
85 where 90 where
86 bindServ = do 91 bindServ = do
87 let family = sockAddrFamily servAddr 92 let family = sockAddrFamily servAddr
@@ -116,6 +121,8 @@ registerQuery cid ref = do
116 atomicModifyIORef' ref $ \ m -> (M.insert cid ares m, ()) 121 atomicModifyIORef' ref $ \ m -> (M.insert cid ares m, ())
117 return ares 122 return ares
118 123
124-- simultaneous M.lookup and M.delete guarantees that we never get two
125-- or more responses to the same query
119unregisterQuery :: CallId -> PendingCalls -> IO (Maybe CallRes) 126unregisterQuery :: CallId -> PendingCalls -> IO (Maybe CallRes)
120unregisterQuery cid ref = do 127unregisterQuery cid ref = do
121 atomicModifyIORef' ref $ swap . 128 atomicModifyIORef' ref $ swap .
@@ -123,13 +130,11 @@ unregisterQuery cid ref = do
123 130
124queryResponse :: BEncode a => CallRes -> IO a 131queryResponse :: BEncode a => CallRes -> IO a
125queryResponse ares = do 132queryResponse ares = do
126 res <- readMVar ares 133 res <- readMVar ares
127 case res of 134 KResponse {..} <- either throwIO pure res
128 Left e -> throwIO e 135 case fromBEncode respVals of
129 Right (KResponse {..}) -> 136 Right r -> pure r
130 case fromBEncode respVals of 137 Left e -> throwIO $ decodeError e respId
131 Left e -> throwIO (KError ProtocolError (BC.pack e) respId)
132 Right a -> return a
133 138
134query :: forall h m a b. (MonadKRPC h m, KRPC a b) => SockAddr -> a -> m b 139query :: forall h m a b. (MonadKRPC h m, KRPC a b) => SockAddr -> a -> m b
135query addr params = do 140query addr params = do
@@ -138,11 +143,17 @@ query addr params = do
138 tid <- genTransactionId transactionCounter 143 tid <- genTransactionId transactionCounter
139 let Method name = method :: Method a b 144 let Method name = method :: Method a b
140 let q = KQuery (toBEncode params) name tid 145 let q = KQuery (toBEncode params) name tid
146
141 ares <- registerQuery (tid, addr) pendingCalls 147 ares <- registerQuery (tid, addr) pendingCalls
142 sendMessage sock addr q 148 sendMessage sock addr q
143 `onException` unregisterQuery (tid, addr) pendingCalls 149 `onException` unregisterQuery (tid, addr) pendingCalls
144 res <- queryResponse ares 150
145 return res 151 mres <- timeout (queryTimeout * 10 ^ 6) $ queryResponse ares
152 case mres of
153 Just res -> return res
154 Nothing -> do
155 unregisterQuery (tid, addr) pendingCalls
156 throwIO $ timeoutExpired tid
146 157
147{----------------------------------------------------------------------- 158{-----------------------------------------------------------------------
148-- Handlers 159-- Handlers
diff --git a/src/Network/KRPC/Message.hs b/src/Network/KRPC/Message.hs
index 3bbfb1db..0bd34400 100644
--- a/src/Network/KRPC/Message.hs
+++ b/src/Network/KRPC/Message.hs
@@ -30,6 +30,7 @@ module Network.KRPC.Message
30 , decodeError 30 , decodeError
31 , unknownMethod 31 , unknownMethod
32 , unknownMessage 32 , unknownMessage
33 , timeoutExpired
33 34
34 -- * Query 35 -- * Query
35 , KQuery(..) 36 , KQuery(..)
@@ -130,17 +131,28 @@ instance BEncode KError where
130 131
131instance Exception KError 132instance Exception KError
132 133
134-- | Happen when some handler fail.
133serverError :: SomeException -> TransactionId -> KError 135serverError :: SomeException -> TransactionId -> KError
134serverError e = KError ServerError (BC.pack (show e)) 136serverError e = KError ServerError (BC.pack (show e))
135 137
138-- | Received 'queryArgs' or 'respVals' can not be decoded.
136decodeError :: String -> TransactionId -> KError 139decodeError :: String -> TransactionId -> KError
137decodeError msg = KError ProtocolError (BC.pack msg) 140decodeError msg = KError ProtocolError (BC.pack msg)
138 141
142-- | If /remote/ node send query /this/ node doesn't know about then
143-- this error message should be sent in response.
139unknownMethod :: MethodName -> TransactionId -> KError 144unknownMethod :: MethodName -> TransactionId -> KError
140unknownMethod = KError MethodUnknown 145unknownMethod = KError MethodUnknown
141 146
147-- | A remote node has send some 'KMessage' this node is unable to
148-- decode.
142unknownMessage :: String -> KError 149unknownMessage :: String -> KError
143unknownMessage msg = KError ProtocolError (BC.pack msg) "" 150unknownMessage msg = KError ProtocolError (BC.pack msg) unknownTransaction
151
152-- | A /remote/ node is not responding to the /our/ request the for
153-- specified period of time.
154timeoutExpired :: TransactionId -> KError
155timeoutExpired = KError GenericError "timeout expired"
144 156
145{----------------------------------------------------------------------- 157{-----------------------------------------------------------------------
146-- Query messages 158-- Query messages