summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bittorrent.cabal1
-rw-r--r--exsamples/Main.hs13
-rw-r--r--src/Network/BitTorrent.hs19
-rw-r--r--src/Network/BitTorrent/Exchange.hs31
-rw-r--r--src/Network/BitTorrent/Internal.hs27
-rw-r--r--src/Network/BitTorrent/Tracker.hs1
6 files changed, 81 insertions, 11 deletions
diff --git a/bittorrent.cabal b/bittorrent.cabal
index a95d6ac2..28188ce3 100644
--- a/bittorrent.cabal
+++ b/bittorrent.cabal
@@ -86,6 +86,7 @@ library
86 , conduit == 1.* 86 , conduit == 1.*
87 , network-conduit == 1.* 87 , network-conduit == 1.*
88 , cereal-conduit >= 0.5 88 , cereal-conduit >= 0.5
89 , resourcet
89 90
90 -- Misc 91 -- Misc
91 , lens 92 , lens
diff --git a/exsamples/Main.hs b/exsamples/Main.hs
index a5b865b0..88796642 100644
--- a/exsamples/Main.hs
+++ b/exsamples/Main.hs
@@ -6,7 +6,7 @@ import Data.Bitfield
6import Network.BitTorrent 6import Network.BitTorrent
7import System.Environment 7import System.Environment
8import Control.Monad.Reader 8import Control.Monad.Reader
9 9import Data.IORef
10 10
11main :: IO () 11main :: IO ()
12main = do 12main = do
@@ -16,6 +16,8 @@ main = do
16 client <- newClient [] 16 client <- newClient []
17 swarm <- newLeacher client torrent 17 swarm <- newLeacher client torrent
18 18
19 ref <- newIORef 0
20
19 discover swarm $ do 21 discover swarm $ do
20 addr <- asks connectedPeerAddr 22 addr <- asks connectedPeerAddr
21 liftIO $ print $ "connected to" ++ show addr 23 liftIO $ print $ "connected to" ++ show addr
@@ -27,7 +29,14 @@ main = do
27 | Just m <- findMin bf -> yieldEvent (Want (BlockIx m 0 10)) 29 | Just m <- findMin bf -> yieldEvent (Want (BlockIx m 0 10))
28 | otherwise -> return () 30 | otherwise -> return ()
29 Want bix -> liftIO $ print bix 31 Want bix -> liftIO $ print bix
30 Fragment blk -> liftIO $ print (ppBlock blk) 32 Fragment blk -> do
33
34 liftIO $ do
35 readIORef ref >>= print
36 modifyIORef ref succ
37 print (ppBlock blk)
38
39 yieldEvent (Want (BlockIx 0 0 (16 * 1024)))
31 40
32 41
33 print "Bye-bye! =_=" \ No newline at end of file 42 print "Bye-bye! =_=" \ No newline at end of file
diff --git a/src/Network/BitTorrent.hs b/src/Network/BitTorrent.hs
index f7c4c004..7ec0a067 100644
--- a/src/Network/BitTorrent.hs
+++ b/src/Network/BitTorrent.hs
@@ -29,9 +29,14 @@ module Network.BitTorrent
29 , awaitEvent, yieldEvent 29 , awaitEvent, yieldEvent
30 ) where 30 ) where
31 31
32import Control.Concurrent
33import Control.Exception
32import Control.Monad 34import Control.Monad
35
33import Data.IORef 36import Data.IORef
34 37
38import Network
39
35import Data.Torrent 40import Data.Torrent
36import Network.BitTorrent.Internal 41import Network.BitTorrent.Internal
37import Network.BitTorrent.Exchange 42import Network.BitTorrent.Exchange
@@ -45,6 +50,8 @@ import Network.BitTorrent.Tracker
45 50
46discover :: SwarmSession -> P2P () -> IO () 51discover :: SwarmSession -> P2P () -> IO ()
47discover swarm action = do 52discover swarm action = do
53 port <- listener swarm action
54
48 let conn = TConnection (tAnnounce (torrentMeta swarm)) 55 let conn = TConnection (tAnnounce (torrentMeta swarm))
49 (tInfoHash (torrentMeta swarm)) 56 (tInfoHash (torrentMeta swarm))
50 (clientPeerID (clientSession swarm)) 57 (clientPeerID (clientSession swarm))
@@ -57,8 +64,14 @@ discover swarm action = do
57 forever $ do 64 forever $ do
58 addr <- getPeerAddr tses 65 addr <- getPeerAddr tses
59 putStrLn "connecting to peer" 66 putStrLn "connecting to peer"
60 withPeer swarm addr action 67 handle handler (withPeer swarm addr action)
61
62 68
69 where
70 handler :: IOException -> IO ()
71 handler _ = return ()
63 72
64port = 10000 73listener :: SwarmSession -> P2P () -> IO PortNumber
74listener _ _ = do
75 -- TODO:
76-- forkIO loop
77 return 10000
diff --git a/src/Network/BitTorrent/Exchange.hs b/src/Network/BitTorrent/Exchange.hs
index de13d4ce..0e2799b2 100644
--- a/src/Network/BitTorrent/Exchange.hs
+++ b/src/Network/BitTorrent/Exchange.hs
@@ -28,6 +28,7 @@ import Control.Exception
28import Control.Lens 28import Control.Lens
29import Control.Monad.Reader 29import Control.Monad.Reader
30import Control.Monad.State 30import Control.Monad.State
31import Control.Monad.Trans.Resource
31 32
32import Data.IORef 33import Data.IORef
33import Data.Function 34import Data.Function
@@ -77,7 +78,7 @@ awaitMessage = P2P (ReaderT go)
77 liftIO $ putStrLn "trying recv:" 78 liftIO $ putStrLn "trying recv:"
78 mmsg <- await 79 mmsg <- await
79 case mmsg of 80 case mmsg of
80 Nothing -> go se 81 Nothing -> monadThrow SessionException
81 Just msg -> do 82 Just msg -> do
82-- liftIO $ updateIncoming se 83-- liftIO $ updateIncoming se
83 liftIO $ print ("recv:" <+> ppMessage msg) 84 liftIO $ print ("recv:" <+> ppMessage msg)
@@ -250,8 +251,18 @@ awaitEvent = awaitMessage >>= go
250-- @ 251-- @
251-- 252--
252yieldEvent :: Event -> P2P () 253yieldEvent :: Event -> P2P ()
253yieldEvent (Available bf) = undefined 254yieldEvent (Available bf) = undefined
254yieldEvent _ = undefined 255yieldEvent (Want bix) = do
256 offer <- peerOffer
257 if ixPiece bix `BF.member` offer
258 then yieldMessage (Request bix)
259 else return ()
260
261yieldEvent (Fragment blk) = do
262 offer <- clientOffer
263 if blkPiece blk `BF.member` offer
264 then yieldMessage (Piece blk)
265 else return ()
255 266
256--flushBroadcast :: P2P () 267--flushBroadcast :: P2P ()
257--flushBroadcast = nextBroadcast >>= maybe (return ()) go 268--flushBroadcast = nextBroadcast >>= maybe (return ()) go
@@ -265,11 +276,21 @@ checkPiece = undefined
265 P2P monad 276 P2P monad
266-----------------------------------------------------------------------} 277-----------------------------------------------------------------------}
267 278
279-- |
280-- Exceptions:
281--
282-- * SessionException: is visible with one peer session. Use this
283-- exception to terminate P2P session, but not the swarm session.
284--
268newtype P2P a = P2P { 285newtype P2P a = P2P {
269 runP2P :: ReaderT PeerSession PeerWire a 286 runP2P :: ReaderT PeerSession PeerWire a
270 } deriving (Functor, Applicative, Monad, MonadReader PeerSession, MonadIO) 287 } deriving ( Functor, Applicative, Monad
288 , MonadIO, MonadThrow, MonadActive
289 , MonadReader PeerSession
290 )
271 291
272withPeer :: SwarmSession -> PeerAddr -> P2P () -> IO () 292withPeer :: SwarmSession -> PeerAddr -> P2P () -> IO ()
273withPeer se addr p2p = 293withPeer se addr p2p =
274 withPeerSession se addr $ \(sock, pses) -> do 294 withPeerSession se addr $ \(sock, pses) -> do
275 runConduit sock (runReaderT (runP2P p2p) pses) 295 handle putSessionException $
296 runConduit sock (runReaderT (runP2P p2p) pses)
diff --git a/src/Network/BitTorrent/Internal.hs b/src/Network/BitTorrent/Internal.hs
index da104240..a3e1a5cd 100644
--- a/src/Network/BitTorrent/Internal.hs
+++ b/src/Network/BitTorrent/Internal.hs
@@ -16,6 +16,7 @@
16{-# LANGUAGE OverloadedStrings #-} 16{-# LANGUAGE OverloadedStrings #-}
17{-# LANGUAGE RecordWildCards #-} 17{-# LANGUAGE RecordWildCards #-}
18{-# LANGUAGE TemplateHaskell #-} 18{-# LANGUAGE TemplateHaskell #-}
19{-# LANGUAGE DeriveDataTypeable #-}
19{-# LANGUAGE FlexibleInstances #-} 20{-# LANGUAGE FlexibleInstances #-}
20{-# LANGUAGE FlexibleContexts #-} 21{-# LANGUAGE FlexibleContexts #-}
21{-# LANGUAGE MultiParamTypeClasses #-} 22{-# LANGUAGE MultiParamTypeClasses #-}
@@ -23,20 +24,31 @@
23module Network.BitTorrent.Internal 24module Network.BitTorrent.Internal
24 ( Progress(..), startProgress 25 ( Progress(..), startProgress
25 26
27 -- * Client
26 , ClientSession (clientPeerID, allowedExtensions) 28 , ClientSession (clientPeerID, allowedExtensions)
27 , newClient, getCurrentProgress 29 , newClient, getCurrentProgress
28 30
31 -- * Swarm
29 , SwarmSession(SwarmSession, torrentMeta, clientSession) 32 , SwarmSession(SwarmSession, torrentMeta, clientSession)
30 , newLeacher, newSeeder 33 , newLeacher, newSeeder
31 34
35 -- * Peer
32 , PeerSession(PeerSession, connectedPeerAddr 36 , PeerSession(PeerSession, connectedPeerAddr
33 , swarmSession, enabledExtensions 37 , swarmSession, enabledExtensions
34 ) 38 )
35 , SessionState 39 , SessionState
40 , withPeerSession
41
42 -- ** Exceptions
43 , SessionException(..)
44 , isSessionException
45 , putSessionException
46 , sessionError
47
48 -- ** Properties
36 , bitfield, status 49 , bitfield, status
37 , emptyBF, fullBF, singletonBF, adjustBF 50 , emptyBF, fullBF, singletonBF, adjustBF
38 , getPieceCount, getClientBF 51 , getPieceCount, getClientBF
39 , sessionError, withPeerSession
40 52
41 -- * Timeouts 53 -- * Timeouts
42 , updateIncoming, updateOutcoming 54 , updateIncoming, updateOutcoming
@@ -55,6 +67,7 @@ import Data.Default
55import Data.Function 67import Data.Function
56import Data.Ord 68import Data.Ord
57import Data.Set as S 69import Data.Set as S
70import Data.Typeable
58 71
59import Data.Serialize hiding (get) 72import Data.Serialize hiding (get)
60import Text.PrettyPrint 73import Text.PrettyPrint
@@ -212,6 +225,7 @@ data PeerSession = PeerSession {
212 -- to avoid reduntant KA messages. 225 -- to avoid reduntant KA messages.
213 , outcomingTimeout :: TimeoutKey 226 , outcomingTimeout :: TimeoutKey
214 227
228 -- TODO use dupChan for broadcasting
215 , broadcastMessages :: Chan [Message] 229 , broadcastMessages :: Chan [Message]
216 , sessionState :: IORef SessionState 230 , sessionState :: IORef SessionState
217 } 231 }
@@ -234,6 +248,17 @@ instance (MonadIO m, MonadReader PeerSession m)
234 get = asks sessionState >>= liftIO . readIORef 248 get = asks sessionState >>= liftIO . readIORef
235 put s = asks sessionState >>= \ref -> liftIO $ writeIORef ref s 249 put s = asks sessionState >>= \ref -> liftIO $ writeIORef ref s
236 250
251data SessionException = SessionException
252 deriving (Show, Typeable)
253
254instance Exception SessionException
255
256isSessionException :: Monad m => SessionException -> m ()
257isSessionException _ = return ()
258
259putSessionException :: SessionException -> IO ()
260putSessionException = print
261
237sessionError :: MonadIO m => Doc -> m () 262sessionError :: MonadIO m => Doc -> m ()
238sessionError msg 263sessionError msg
239 = liftIO $ throwIO $ userError $ render $ msg <+> "in session" 264 = liftIO $ throwIO $ userError $ render $ msg <+> "in session"
diff --git a/src/Network/BitTorrent/Tracker.hs b/src/Network/BitTorrent/Tracker.hs
index 179dae1d..11bc52de 100644
--- a/src/Network/BitTorrent/Tracker.hs
+++ b/src/Network/BitTorrent/Tracker.hs
@@ -145,6 +145,7 @@ data TSession = TSession {
145 , sePeers :: Chan PeerAddr 145 , sePeers :: Chan PeerAddr
146 -- TODO use something like 'TVar (Set PeerAddr)' 146 -- TODO use something like 'TVar (Set PeerAddr)'
147 -- otherwise we might get space leak 147 -- otherwise we might get space leak
148 -- TODO or maybe BoundedChan?
148 } 149 }
149 150
150newSession :: Progress -> Int -> [PeerAddr] -> IO TSession 151newSession :: Progress -> Int -> [PeerAddr] -> IO TSession