diff options
Diffstat (limited to 'src/Network/BitTorrent/Internal.hs')
-rw-r--r-- | src/Network/BitTorrent/Internal.hs | 125 |
1 files changed, 106 insertions, 19 deletions
diff --git a/src/Network/BitTorrent/Internal.hs b/src/Network/BitTorrent/Internal.hs index 38087f0d..2f538652 100644 --- a/src/Network/BitTorrent/Internal.hs +++ b/src/Network/BitTorrent/Internal.hs | |||
@@ -10,12 +10,33 @@ | |||
10 | -- Network.BitTorrent.Exchange and modules. To hide some internals | 10 | -- Network.BitTorrent.Exchange and modules. To hide some internals |
11 | -- of this module we detach it from Exchange. | 11 | -- of this module we detach it from Exchange. |
12 | -- | 12 | -- |
13 | {-# LANGUAGE RecordWildCards #-} | 13 | -- Note: expose only static data in data field lists, all dynamic |
14 | -- data should be modified through standalone functions. | ||
15 | -- | ||
16 | {-# LANGUAGE OverloadedStrings #-} | ||
17 | {-# LANGUAGE RecordWildCards #-} | ||
18 | {-# LANGUAGE TemplateHaskell #-} | ||
19 | {-# LANGUAGE FlexibleInstances #-} | ||
20 | {-# LANGUAGE FlexibleContexts #-} | ||
21 | {-# LANGUAGE MultiParamTypeClasses #-} | ||
22 | {-# LANGUAGE UndecidableInstances #-} | ||
14 | module Network.BitTorrent.Internal | 23 | module Network.BitTorrent.Internal |
15 | ( Progress(..), startProgress | 24 | ( Progress(..), startProgress |
16 | , ClientSession(..), newClient | 25 | |
17 | , SwarmSession(..), newLeacher, newSeeder | 26 | , ClientSession (clientPeerID, allowedExtensions) |
18 | , PeerSession(..), withPeerSession | 27 | , newClient, getCurrentProgress |
28 | |||
29 | , SwarmSession(SwarmSession, torrentMeta, clientSession) | ||
30 | , newLeacher, newSeeder | ||
31 | |||
32 | , PeerSession(PeerSession, connectedPeerAddr | ||
33 | , swarmSession, enabledExtensions | ||
34 | ) | ||
35 | , SessionState | ||
36 | , bitfield, status | ||
37 | , emptyBF, fullBF, singletonBF | ||
38 | , getPieceCount, getPeerBF | ||
39 | , sessionError, withPeerSession | ||
19 | 40 | ||
20 | -- * Timeouts | 41 | -- * Timeouts |
21 | , updateIncoming, updateOutcoming | 42 | , updateIncoming, updateOutcoming |
@@ -24,6 +45,9 @@ module Network.BitTorrent.Internal | |||
24 | import Control.Applicative | 45 | import Control.Applicative |
25 | import Control.Concurrent | 46 | import Control.Concurrent |
26 | import Control.Concurrent.STM | 47 | import Control.Concurrent.STM |
48 | import Control.Lens | ||
49 | import Control.Monad.State | ||
50 | import Control.Monad.Reader | ||
27 | import Control.Exception | 51 | import Control.Exception |
28 | 52 | ||
29 | import Data.IORef | 53 | import Data.IORef |
@@ -32,10 +56,8 @@ import Data.Function | |||
32 | import Data.Ord | 56 | import Data.Ord |
33 | import Data.Set as S | 57 | import Data.Set as S |
34 | 58 | ||
35 | import Data.Conduit | 59 | import Data.Serialize hiding (get) |
36 | import Data.Conduit.Cereal | 60 | import Text.PrettyPrint |
37 | import Data.Conduit.Network | ||
38 | import Data.Serialize | ||
39 | 61 | ||
40 | import Network | 62 | import Network |
41 | import Network.Socket | 63 | import Network.Socket |
@@ -70,13 +92,20 @@ startProgress = Progress 0 0 | |||
70 | Client session | 92 | Client session |
71 | -----------------------------------------------------------------------} | 93 | -----------------------------------------------------------------------} |
72 | 94 | ||
73 | -- | In one application you could have many clients. | 95 | -- | In one application we could have many clients with difference |
96 | -- ID's and enabled extensions. | ||
74 | data ClientSession = ClientSession { | 97 | data ClientSession = ClientSession { |
75 | clientPeerID :: PeerID -- ^ | 98 | -- | Our peer ID used in handshaked and discovery mechanism. |
76 | , allowedExtensions :: [Extension] -- ^ | 99 | clientPeerID :: PeerID |
100 | |||
101 | -- | Extensions we should try to use. Hovewer some particular peer | ||
102 | -- might not support some extension, so we keep enableExtension in | ||
103 | -- 'PeerSession'. | ||
104 | , allowedExtensions :: [Extension] | ||
105 | |||
77 | , swarmSessions :: TVar (Set SwarmSession) | 106 | , swarmSessions :: TVar (Set SwarmSession) |
78 | , eventManager :: EventManager | 107 | , eventManager :: EventManager |
79 | , currentProgress :: IORef Progress | 108 | , currentProgress :: TVar Progress |
80 | } | 109 | } |
81 | 110 | ||
82 | instance Eq ClientSession where | 111 | instance Eq ClientSession where |
@@ -85,6 +114,9 @@ instance Eq ClientSession where | |||
85 | instance Ord ClientSession where | 114 | instance Ord ClientSession where |
86 | compare = comparing clientPeerID | 115 | compare = comparing clientPeerID |
87 | 116 | ||
117 | getCurrentProgress :: MonadIO m => ClientSession -> m Progress | ||
118 | getCurrentProgress = liftIO . readTVarIO . currentProgress | ||
119 | |||
88 | newClient :: [Extension] -> IO ClientSession | 120 | newClient :: [Extension] -> IO ClientSession |
89 | newClient exts = do | 121 | newClient exts = do |
90 | mgr <- Ev.new | 122 | mgr <- Ev.new |
@@ -95,7 +127,7 @@ newClient exts = do | |||
95 | <*> pure exts | 127 | <*> pure exts |
96 | <*> newTVarIO S.empty | 128 | <*> newTVarIO S.empty |
97 | <*> pure mgr | 129 | <*> pure mgr |
98 | <*> newIORef (startProgress 0) | 130 | <*> newTVarIO (startProgress 0) |
99 | 131 | ||
100 | {----------------------------------------------------------------------- | 132 | {----------------------------------------------------------------------- |
101 | Swarm session | 133 | Swarm session |
@@ -106,7 +138,9 @@ newClient exts = do | |||
106 | data SwarmSession = SwarmSession { | 138 | data SwarmSession = SwarmSession { |
107 | torrentMeta :: Torrent | 139 | torrentMeta :: Torrent |
108 | , clientSession :: ClientSession | 140 | , clientSession :: ClientSession |
109 | , clientBitfield :: IORef Bitfield | 141 | |
142 | -- | Modify this carefully updating global progress. | ||
143 | , clientBitfield :: TVar Bitfield | ||
110 | , connectedPeers :: TVar (Set PeerSession) | 144 | , connectedPeers :: TVar (Set PeerSession) |
111 | } | 145 | } |
112 | 146 | ||
@@ -120,7 +154,7 @@ newSwarmSession :: Bitfield -> ClientSession -> Torrent -> IO SwarmSession | |||
120 | newSwarmSession bf cs @ ClientSession {..} t @ Torrent {..} | 154 | newSwarmSession bf cs @ ClientSession {..} t @ Torrent {..} |
121 | = SwarmSession <$> pure t | 155 | = SwarmSession <$> pure t |
122 | <*> pure cs | 156 | <*> pure cs |
123 | <*> newIORef bf | 157 | <*> newTVarIO bf |
124 | <*> newTVarIO S.empty | 158 | <*> newTVarIO S.empty |
125 | 159 | ||
126 | newSeeder :: ClientSession -> Torrent -> IO SwarmSession | 160 | newSeeder :: ClientSession -> Torrent -> IO SwarmSession |
@@ -134,13 +168,29 @@ newLeacher cs t @ Torrent {..} | |||
134 | isLeacher :: SwarmSession -> IO Bool | 168 | isLeacher :: SwarmSession -> IO Bool |
135 | isLeacher = undefined | 169 | isLeacher = undefined |
136 | 170 | ||
171 | getClientBitfield :: MonadIO m => SwarmSession -> m Bitfield | ||
172 | getClientBitfield = liftIO . readTVarIO . clientBitfield | ||
173 | |||
174 | {- | ||
175 | haveDone :: MonadIO m => PieceIx -> SwarmSession -> m () | ||
176 | haveDone ix = | ||
177 | liftIO $ atomically $ do | ||
178 | bf <- readTVar clientBitfield | ||
179 | writeTVar (have ix bf) | ||
180 | currentProgress | ||
181 | -} | ||
137 | {----------------------------------------------------------------------- | 182 | {----------------------------------------------------------------------- |
138 | Peer session | 183 | Peer session |
139 | -----------------------------------------------------------------------} | 184 | -----------------------------------------------------------------------} |
140 | 185 | ||
141 | data PeerSession = PeerSession { | 186 | data PeerSession = PeerSession { |
187 | -- | Used as unique 'PeerSession' identifier within one | ||
188 | -- 'SwarmSession'. | ||
142 | connectedPeerAddr :: PeerAddr | 189 | connectedPeerAddr :: PeerAddr |
190 | |||
143 | , swarmSession :: SwarmSession | 191 | , swarmSession :: SwarmSession |
192 | |||
193 | -- | Extensions such that both peer and client support. | ||
144 | , enabledExtensions :: [Extension] | 194 | , enabledExtensions :: [Extension] |
145 | 195 | ||
146 | -- | To dissconnect from died peers appropriately we should check | 196 | -- | To dissconnect from died peers appropriately we should check |
@@ -163,16 +213,31 @@ data PeerSession = PeerSession { | |||
163 | , outcomingTimeout :: TimeoutKey | 213 | , outcomingTimeout :: TimeoutKey |
164 | 214 | ||
165 | , broadcastMessages :: Chan [Message] | 215 | , broadcastMessages :: Chan [Message] |
166 | , peerBitfield :: IORef Bitfield | 216 | , sessionState :: IORef SessionState |
167 | , peerSessionStatus :: IORef SessionStatus | 217 | } |
218 | |||
219 | data SessionState = SessionState { | ||
220 | _bitfield :: Bitfield | ||
221 | , _status :: SessionStatus | ||
168 | } | 222 | } |
169 | 223 | ||
224 | $(makeLenses ''SessionState) | ||
225 | |||
170 | instance Eq PeerSession where | 226 | instance Eq PeerSession where |
171 | (==) = (==) `on` connectedPeerAddr | 227 | (==) = (==) `on` connectedPeerAddr |
172 | 228 | ||
173 | instance Ord PeerSession where | 229 | instance Ord PeerSession where |
174 | compare = comparing connectedPeerAddr | 230 | compare = comparing connectedPeerAddr |
175 | 231 | ||
232 | instance (MonadIO m, MonadReader PeerSession m) | ||
233 | => MonadState SessionState m where | ||
234 | get = asks sessionState >>= liftIO . readIORef | ||
235 | put s = asks sessionState >>= \ref -> liftIO $ writeIORef ref s | ||
236 | |||
237 | sessionError :: MonadIO m => Doc -> m () | ||
238 | sessionError msg | ||
239 | = liftIO $ throwIO $ userError $ render $ msg <+> "in session" | ||
240 | |||
176 | -- TODO check if it connected yet peer | 241 | -- TODO check if it connected yet peer |
177 | withPeerSession :: SwarmSession -> PeerAddr | 242 | withPeerSession :: SwarmSession -> PeerAddr |
178 | -> ((Socket, PeerSession) -> IO a) | 243 | -> ((Socket, PeerSession) -> IO a) |
@@ -196,12 +261,34 @@ withPeerSession ss @ SwarmSession {..} addr | |||
196 | <*> registerTimeout (eventManager clientSession) | 261 | <*> registerTimeout (eventManager clientSession) |
197 | maxOutcomingTime (sendKA sock) | 262 | maxOutcomingTime (sendKA sock) |
198 | <*> newChan | 263 | <*> newChan |
199 | <*> pure clientBitfield | 264 | <*> do { |
200 | <*> newIORef def | 265 | ; tc <- totalCount <$> readTVarIO clientBitfield |
266 | ; newIORef (SessionState (haveNone tc) def) | ||
267 | } | ||
201 | return (sock, ps) | 268 | return (sock, ps) |
202 | 269 | ||
203 | closeSession = close . fst | 270 | closeSession = close . fst |
204 | 271 | ||
272 | getPieceCount :: (MonadReader PeerSession m) => m PieceCount | ||
273 | getPieceCount = asks (pieceCount . tInfo . torrentMeta . swarmSession) | ||
274 | |||
275 | emptyBF :: (MonadReader PeerSession m) => m Bitfield | ||
276 | emptyBF = liftM haveNone getPieceCount | ||
277 | |||
278 | fullBF :: (MonadReader PeerSession m) => m Bitfield | ||
279 | fullBF = liftM haveAll getPieceCount | ||
280 | |||
281 | singletonBF :: (MonadReader PeerSession m) => PieceIx -> m Bitfield | ||
282 | singletonBF ix = liftM (BF.singleton ix) getPieceCount | ||
283 | |||
284 | getPeerBF :: (MonadIO m, MonadReader PeerSession m) => m Bitfield | ||
285 | getPeerBF = asks swarmSession >>= liftIO . readTVarIO . clientBitfield | ||
286 | |||
287 | --data Signal = | ||
288 | --nextBroadcast :: P2P (Maybe Signal) | ||
289 | --nextBroadcast = | ||
290 | |||
291 | |||
205 | {----------------------------------------------------------------------- | 292 | {----------------------------------------------------------------------- |
206 | Timeouts | 293 | Timeouts |
207 | -----------------------------------------------------------------------} | 294 | -----------------------------------------------------------------------} |