summaryrefslogtreecommitdiff
path: root/dht/Connection.hs
diff options
context:
space:
mode:
authorJames Crayne <jim.crayne@gmail.com>2019-09-28 13:43:29 -0400
committerJoe Crayne <joe@jerkface.net>2020-01-01 19:27:53 -0500
commit11987749fc6e6d3e53ea737d46d5ab13a16faeb8 (patch)
tree5716463275c2d3e902889db619908ded2a73971c /dht/Connection.hs
parentadd2c76bced51fde5e9917e7449ef52be70faf87 (diff)
Factor out some new libraries
word64-map: Data.Word64Map network-addr: Network.Address tox-crypto: Crypto.Tox lifted-concurrent: Control.Concurrent.Lifted.Instrument Control.Concurrent.Async.Lifted.Instrument psq-wrap: Data.Wrapper.PSQInt Data.Wrapper.PSQ minmax-psq: Data.MinMaxPSQ tasks: Control.Concurrent.Tasks kad: Network.Kademlia Network.Kademlia.Bootstrap Network.Kademlia.Routing Network.Kademlia.CommonAPI Network.Kademlia.Persistence Network.Kademlia.Search
Diffstat (limited to 'dht/Connection.hs')
-rw-r--r--dht/Connection.hs135
1 files changed, 135 insertions, 0 deletions
diff --git a/dht/Connection.hs b/dht/Connection.hs
new file mode 100644
index 00000000..9a4077f5
--- /dev/null
+++ b/dht/Connection.hs
@@ -0,0 +1,135 @@
1{-# LANGUAGE DeriveFunctor #-}
2{-# LANGUAGE LambdaCase #-}
3module Connection where
4
5import Control.Applicative
6import Control.Arrow
7import Control.Concurrent.STM
8import Data.Bits
9import Data.Word
10import qualified Data.Map as Map
11 ;import Data.Map (Map)
12import Network.Socket (SockAddr(..))
13
14import PingMachine
15
16-- | This type indicates the current status of a connection. The type
17-- parameter indicates protocol-specific status information. To present
18-- information as a user-comprehensible string, use 'showStatus'.
19data Status status
20 = Dormant
21 | InProgress status
22 | Established
23 deriving (Show,Eq,Ord,Functor)
24
25-- | A policy indicates a desired connection status.
26data Policy
27 = RefusingToConnect -- ^ We desire no connection.
28 | OpenToConnect -- ^ We will cooperate if a remote side initiates.
29 | TryingToConnect -- ^ We desire to be connected.
30 deriving (Eq,Ord,Show)
31
32-- | Information obtained via the 'connectionStatus' interface to
33-- 'Manager'.
34data Connection status = Connection
35 { connStatus :: Status status
36 , connPolicy :: Policy
37 }
38 deriving Functor
39
40-- | A 'PeerAddress' identifies an active session. For inactive sessions, multiple
41-- values may be feasible.
42
43-- We use a 'SockAddr' as it is convenient for TCP and UDP connections. But if
44-- that is not your use case, see 'uniqueAsKey'.
45newtype PeerAddress = PeerAddress { peerAddress :: SockAddr }
46 deriving (Eq,Ord,Show)
47
48-- | A 24-byte word.
49data Uniq24 = Uniq24 !Word64 !Word64 !Word64
50 deriving (Eq,Ord,Show)
51
52-- | Coerce a 'Uniq24' to a useable 'PeerAddress'. Note that this stores the
53-- special value 0 into the port number of the underlying 'SockAddr' and thus
54-- should be compatible for mixing together with TCP/UDP peers.
55uniqueAsKey :: Uniq24 -> PeerAddress
56uniqueAsKey (Uniq24 x y z) = PeerAddress $ SockAddrInet6 (fromIntegral 0) a bcde f
57 where
58 a = fromIntegral (x `shiftR` 32)
59 b = fromIntegral x
60 c = fromIntegral (y `shiftR` 32)
61 d = fromIntegral y
62 e = fromIntegral (z `shiftR` 32)
63 f = fromIntegral z
64 bcde = (b,c,d,e)
65
66-- | Inverse of 'uniqueAsKey'
67keyAsUnique :: PeerAddress -> Maybe Uniq24
68keyAsUnique (PeerAddress (SockAddrInet6 0 a bcde f)) = Just $ Uniq24 x y z
69 where
70 (b,c,d,e) = bcde
71 x = (fromIntegral a `shiftL` 32) .|. fromIntegral b
72 y = (fromIntegral c `shiftL` 32) .|. fromIntegral d
73 z = (fromIntegral e `shiftL` 32) .|. fromIntegral f
74keyAsUniq _ = Nothing
75
76
77-- | This is an interface to make or query status information about connections
78-- of a specific kind.
79--
80-- Type parameters:
81--
82-- /k/ names a connection. It should implement Ord, and can be parsed and
83-- displayed using 'stringToKey' and 'showKey'.
84--
85-- /status/ indicates the progress of a connection. It is intended as a
86-- parameter to the 'InProgress' constructor of 'Status'.
87--
88data Manager status k = Manager
89 { -- | Connect or disconnect a connection.
90 setPolicy :: k -> Policy -> IO ()
91 -- | Lookup a connection status.
92 , status :: k -> STM (Connection status)
93 -- | Obtain a list of all known connections.
94 , connections :: STM [k]
95 -- | Parse a connection key out of a string. Inverse of 'showKey'.
96 , stringToKey :: String -> Maybe k
97 -- | Convert a progress value to a string.
98 , showProgress :: status -> String
99 -- | Show a connection key as a string.
100 , showKey :: k -> String
101 -- | Obtain an address from a human-friendly name. For TCP/UDP
102 -- connections, this might be a forward-resolving DNS query.
103 , resolvePeer :: k -> IO [PeerAddress]
104 -- | This is the reverse of 'resolvePeer'. For TCP/UDP connections, this
105 -- might be a reverse-resolve DNS query.
106 , reverseAddress :: PeerAddress -> IO [k]
107 }
108
109-- | Present status information (visible in a UI) for a connection.
110showStatus :: Manager status k -> Status status -> String
111showStatus mgr Dormant = "dormant"
112showStatus mgr Established = "established"
113showStatus mgr (InProgress s) = "in progress ("++showProgress mgr s++")"
114
115
116-- | Combine two different species of 'Manager' into a single interface using
117-- 'Either' to combine key and status types.
118addManagers :: (Ord kA, Ord kB) =>
119 Manager statusA kA
120 -> Manager statusB kB
121 -> Manager (Either statusA statusB) (Either kA kB)
122addManagers mgrA mgrB = Manager
123 { setPolicy = either (setPolicy mgrA) (setPolicy mgrB)
124 , status = \case
125 Left k -> fmap Left <$> status mgrA k
126 Right k -> fmap Right <$> status mgrB k
127 , connections = do
128 as <- connections mgrA
129 bs <- connections mgrB
130 return $ map Left as ++ map Right bs
131 , stringToKey = \str -> Left <$> stringToKey mgrA str
132 <|> Right <$> stringToKey mgrB str
133 , showProgress = either (showProgress mgrA) (showProgress mgrB)
134 , showKey = either (showKey mgrA) (showKey mgrB)
135 }