diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Network/QueryResponse.hs | 95 |
1 files changed, 72 insertions, 23 deletions
diff --git a/src/Network/QueryResponse.hs b/src/Network/QueryResponse.hs index f6f2807d..4fc9438c 100644 --- a/src/Network/QueryResponse.hs +++ b/src/Network/QueryResponse.hs | |||
@@ -1,30 +1,36 @@ | |||
1 | -- | This module can implement any query\/response protocol. It was written | 1 | -- | This module can implement any query\/response protocol. It was written |
2 | -- with Kademlia implementations in mind. | 2 | -- with Kademlia implementations in mind. |
3 | 3 | ||
4 | {-# LANGUAGE CPP #-} | 4 | {-# LANGUAGE CPP #-} |
5 | {-# LANGUAGE RankNTypes #-} | 5 | {-# LANGUAGE GADTs #-} |
6 | {-# LANGUAGE LambdaCase #-} | 6 | {-# LANGUAGE LambdaCase #-} |
7 | {-# LANGUAGE PartialTypeSignatures #-} | 7 | {-# LANGUAGE PartialTypeSignatures #-} |
8 | {-# LANGUAGE GADTs #-} | 8 | {-# LANGUAGE RankNTypes #-} |
9 | {-# LANGUAGE ScopedTypeVariables #-} | ||
9 | module Network.QueryResponse where | 10 | module Network.QueryResponse where |
10 | 11 | ||
11 | #ifdef THREAD_DEBUG | 12 | #ifdef THREAD_DEBUG |
12 | import Control.Concurrent.Lifted.Instrument | 13 | import Control.Concurrent.Lifted.Instrument |
13 | #else | 14 | #else |
14 | import GHC.Conc (labelThread) | ||
15 | import Control.Concurrent | 15 | import Control.Concurrent |
16 | import GHC.Conc (labelThread) | ||
16 | #endif | 17 | #endif |
17 | import Control.Concurrent.STM | 18 | import Control.Concurrent.STM |
18 | import System.Timeout | ||
19 | import Data.Function | ||
20 | import Control.Exception | 19 | import Control.Exception |
21 | import Control.Monad | 20 | import Control.Monad |
22 | import qualified Data.ByteString as B | 21 | import qualified Data.ByteString as B |
23 | ;import Data.ByteString (ByteString) | 22 | ;import Data.ByteString (ByteString) |
23 | import Data.Function | ||
24 | import qualified Data.IntMap as IntMap | ||
25 | ;import Data.IntMap (IntMap) | ||
26 | import qualified Data.Map as Map | ||
27 | ;import Data.Map (Map) | ||
28 | import Data.Maybe | ||
29 | import Data.Typeable | ||
24 | import Network.Socket | 30 | import Network.Socket |
25 | import Network.Socket.ByteString as B | 31 | import Network.Socket.ByteString as B |
26 | import System.IO.Error | 32 | import System.IO.Error |
27 | import Data.Maybe | 33 | import System.Timeout |
28 | 34 | ||
29 | -- * Using a query\/response 'Client'. | 35 | -- * Using a query\/response 'Client'. |
30 | 36 | ||
@@ -184,12 +190,12 @@ layerTransport parse encode tr = | |||
184 | 190 | ||
185 | 191 | ||
186 | -- | To dipatch responses to our outbound queries, we require three primitives. | 192 | -- | To dipatch responses to our outbound queries, we require three primitives. |
187 | -- See the 'transactionTableMethods' function to create these primitives out of a | 193 | -- See the 'transactionMethods' function to create these primitives out of a |
188 | -- lookup table and a generator for transaction ids. | 194 | -- lookup table and a generator for transaction ids. |
189 | -- | 195 | -- |
190 | -- The type variable /d/ is used to represent the current state of the | 196 | -- The type variable /d/ is used to represent the current state of the |
191 | -- transaction generator and the table of pending transactions. | 197 | -- transaction generator and the table of pending transactions. |
192 | data TableMethods d tid x = TableMethods | 198 | data TransactionMethods d tid x = TransactionMethods |
193 | { | 199 | { |
194 | -- | Before a query is sent, this function stores an 'MVar' to which the | 200 | -- | Before a query is sent, this function stores an 'MVar' to which the |
195 | -- response will be written too. The returned _tid_ is a transaction id | 201 | -- response will be written too. The returned _tid_ is a transaction id |
@@ -206,19 +212,55 @@ data TableMethods d tid x = TableMethods | |||
206 | , dispatchCancel :: tid -> d -> d | 212 | , dispatchCancel :: tid -> d -> d |
207 | } | 213 | } |
208 | 214 | ||
209 | -- | Construct 'TableMethods' methods out of 3 lookup table primitives and a | 215 | -- | The standard lookup table methods for use as input to 'transactionMethods' |
216 | -- in lieu of directly implementing 'TransactionMethods'. | ||
217 | data TableMethods t tid = TableMethods | ||
218 | { -- | Insert a new _tid_ entry into the transaction table. | ||
219 | tblInsert :: forall a. tid -> a -> t a -> t a | ||
220 | -- | Delete transaction _tid_ from the transaction table. | ||
221 | , tblDelete :: forall a. tid -> t a -> t a | ||
222 | -- | Lookup the value associated with transaction /tid/. | ||
223 | , tblLookup :: forall a. tid -> t a -> Maybe a | ||
224 | } | ||
225 | |||
226 | -- | Methods for using 'Data.IntMap. | ||
227 | intMapMethods :: TableMethods IntMap Int | ||
228 | intMapMethods = TableMethods IntMap.insert IntMap.delete IntMap.lookup | ||
229 | |||
230 | -- | Methods for using 'Data.Map' | ||
231 | mapMethods :: Ord tid => TableMethods (Map tid) tid | ||
232 | mapMethods = TableMethods Map.insert Map.delete Map.lookup | ||
233 | |||
234 | -- | Change the key type for a lookup table implementation. | ||
235 | -- | ||
236 | -- This can be used with 'intMapMethods' or 'mapMethods' to restrict lookups to | ||
237 | -- only a part of the generated /tid/ value. This is useful for /tid/ types | ||
238 | -- that are especially large due their use for other purposes, such as secure | ||
239 | -- nonces for encryption. | ||
240 | contramapT f (TableMethods ins del lookup) = | ||
241 | TableMethods (\k v t -> ins (f k) v t) | ||
242 | (\k t -> del (f k) t) | ||
243 | (\k t -> lookup (f k) t) | ||
244 | |||
245 | -- | Since 'Int' may be 32 or 64 bits, this function is provided as a | ||
246 | -- convenience to test if an integral type, such as 'Data.Word.Word64', can be | ||
247 | -- safely transformed into an 'Int' for use with 'IntMap'. | ||
248 | -- | ||
249 | -- Returns 'True' if the proxied type can be losslessly convered to 'Int' using | ||
250 | -- 'fromIntegral'. | ||
251 | fitsInInt :: forall word. (Bounded word, Integral word) => Proxy word -> Bool | ||
252 | fitsInInt Proxy = (original == casted) | ||
253 | where | ||
254 | original = div maxBound 2 :: word | ||
255 | casted = fromIntegral (fromIntegral original :: Int) :: word | ||
256 | |||
257 | -- | Construct 'TransactionMethods' methods out of 3 lookup table primitives and a | ||
210 | -- function for generating unique transaction ids. | 258 | -- function for generating unique transaction ids. |
211 | transactionTableMethods :: | 259 | transactionMethods :: |
212 | (forall a. tid -> a -> t a -> t a) | 260 | TableMethods t tid -- ^ Table methods to lookup values by /tid/. |
213 | -- ^ Insert a new _tid_ entry into the transaction table. | 261 | -> (g -> (tid,g)) -- ^ Generate a new unique /tid/ value and update the generator state /g/. |
214 | -> (forall a. tid -> t a -> t a) | 262 | -> TransactionMethods (g,t (MVar x)) tid x |
215 | -- ^ Delete transaction _tid_ from the transaction table. | 263 | transactionMethods (TableMethods insert delete lookup) generate = TransactionMethods |
216 | -> (forall a. tid -> t a -> Maybe a) | ||
217 | -- ^ Lookup the value associated with transaction _tid_. | ||
218 | -> (g -> (tid,g)) | ||
219 | -- ^ Generate a new unique _tid_ value and update the generator state _g_. | ||
220 | -> TableMethods (g,t (MVar x)) tid x | ||
221 | transactionTableMethods insert delete lookup generate = TableMethods | ||
222 | { dispatchCancel = \tid (g,t) -> (g, delete tid t) | 264 | { dispatchCancel = \tid (g,t) -> (g, delete tid t) |
223 | , dispatchRegister = \v (g,t) -> | 265 | , dispatchRegister = \v (g,t) -> |
224 | let (tid,g') = generate g | 266 | let (tid,g') = generate g |
@@ -238,7 +280,7 @@ data DispatchMethods tbl err meth tid addr x ctx = DispatchMethods | |||
238 | -- | Lookup the handler for a inbound query. | 280 | -- | Lookup the handler for a inbound query. |
239 | , lookupHandler :: meth -> Maybe (MethodHandler err tid addr x ctx) | 281 | , lookupHandler :: meth -> Maybe (MethodHandler err tid addr x ctx) |
240 | -- | Methods for handling incomming responses. | 282 | -- | Methods for handling incomming responses. |
241 | , tableMethods :: TableMethods tbl tid x | 283 | , tableMethods :: TransactionMethods tbl tid x |
242 | } | 284 | } |
243 | 285 | ||
244 | -- | These methods indicate what should be done upon various conditions. Write | 286 | -- | These methods indicate what should be done upon various conditions. Write |
@@ -264,6 +306,13 @@ data ErrorReporter addr x meth tid err = ErrorReporter | |||
264 | , reportTimeout :: meth -> tid -> addr -> IO () | 306 | , reportTimeout :: meth -> tid -> addr -> IO () |
265 | } | 307 | } |
266 | 308 | ||
309 | -- Change the /err/ type for an 'ErrorReporter'. | ||
310 | contramapE f (ErrorReporter pe mh unk tim) | ||
311 | = ErrorReporter (\e -> pe (f e)) | ||
312 | mh | ||
313 | (\addr x e -> unk addr x (f e)) | ||
314 | tim | ||
315 | |||
267 | -- | Handle a single inbound packet and then invoke the given continuation. | 316 | -- | Handle a single inbound packet and then invoke the given continuation. |
268 | -- The 'forkListener' function is implemeneted by passing this function to | 317 | -- The 'forkListener' function is implemeneted by passing this function to |
269 | -- 'fix' in a forked thread that loops until 'awaitMessage' returns 'Nothing' | 318 | -- 'fix' in a forked thread that loops until 'awaitMessage' returns 'Nothing' |