summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Data/Bitfield.hs8
-rw-r--r--src/Network/BitTorrent.hs10
-rw-r--r--src/Network/BitTorrent/Exchange.hs124
-rw-r--r--src/Network/BitTorrent/Exchange/Protocol.hs4
-rw-r--r--src/Network/BitTorrent/Internal.hs20
-rw-r--r--src/Network/BitTorrent/Peer.hs4
-rw-r--r--src/Network/BitTorrent/Tracker.hs4
7 files changed, 128 insertions, 46 deletions
diff --git a/src/Data/Bitfield.hs b/src/Data/Bitfield.hs
index 9c62e183..18f80a4e 100644
--- a/src/Data/Bitfield.hs
+++ b/src/Data/Bitfield.hs
@@ -41,6 +41,8 @@ module Data.Bitfield
41 , member, notMember 41 , member, notMember
42 , findMin, findMax 42 , findMin, findMax
43 43
44 , isSubsetOf
45
44 , Frequency, frequencies, rarest 46 , Frequency, frequencies, rarest
45 47
46 -- * Combine 48 -- * Combine
@@ -182,6 +184,9 @@ findMax Bitfield {..}
182 | S.null bfSet = Nothing 184 | S.null bfSet = Nothing
183 | otherwise = Just (S.findMax bfSet) 185 | otherwise = Just (S.findMax bfSet)
184 186
187isSubsetOf :: Bitfield -> Bitfield -> Bool
188isSubsetOf a b = bfSet a `S.isSubsetOf` bfSet b
189
185-- | Frequencies are needed in piece selection startegies which use 190-- | Frequencies are needed in piece selection startegies which use
186-- availability quantity to find out the optimal next piece index to 191-- availability quantity to find out the optimal next piece index to
187-- download. 192-- download.
@@ -208,7 +213,8 @@ frequencies xs = runST $ do
208rarest :: [Bitfield] -> Maybe PieceIx 213rarest :: [Bitfield] -> Maybe PieceIx
209rarest xs 214rarest xs
210 | V.null freqMap = Nothing 215 | V.null freqMap = Nothing
211 | otherwise = Just $ fst $ V.ifoldr' minIx (0, freqMap V.! 0) freqMap 216 | otherwise
217 = Just $ fst $ V.ifoldr' minIx (0, freqMap V.! 0) freqMap
212 where 218 where
213 freqMap = frequencies xs 219 freqMap = frequencies xs
214 220
diff --git a/src/Network/BitTorrent.hs b/src/Network/BitTorrent.hs
index e9b5f17d..f7c4c004 100644
--- a/src/Network/BitTorrent.hs
+++ b/src/Network/BitTorrent.hs
@@ -22,10 +22,11 @@ module Network.BitTorrent
22 22
23 -- * Peer to Peer 23 -- * Peer to Peer
24 , P2P 24 , P2P
25 , Event(..)
25 , PeerSession ( connectedPeerAddr, enabledExtensions ) 26 , PeerSession ( connectedPeerAddr, enabledExtensions )
26 , Block(..), BlockIx(..) 27 , Block(..), BlockIx(..), ppBlock, ppBlockIx
27 28
28 , awaitEvent, signalEvent 29 , awaitEvent, yieldEvent
29 ) where 30 ) where
30 31
31import Control.Monad 32import Control.Monad
@@ -34,6 +35,7 @@ import Data.IORef
34import Data.Torrent 35import Data.Torrent
35import Network.BitTorrent.Internal 36import Network.BitTorrent.Internal
36import Network.BitTorrent.Exchange 37import Network.BitTorrent.Exchange
38import Network.BitTorrent.Exchange.Protocol
37import Network.BitTorrent.Tracker 39import Network.BitTorrent.Tracker
38 40
39 41
@@ -49,10 +51,14 @@ discover swarm action = do
49 port 51 port
50 52
51 progress <- getCurrentProgress (clientSession swarm) 53 progress <- getCurrentProgress (clientSession swarm)
54
55 putStrLn "lookup peers"
52 withTracker progress conn $ \tses -> do 56 withTracker progress conn $ \tses -> do
53 forever $ do 57 forever $ do
54 addr <- getPeerAddr tses 58 addr <- getPeerAddr tses
59 putStrLn "connecting to peer"
55 withPeer swarm addr action 60 withPeer swarm addr action
56 61
57 62
63
58port = 10000 64port = 10000
diff --git a/src/Network/BitTorrent/Exchange.hs b/src/Network/BitTorrent/Exchange.hs
index 65ec0eb7..de13d4ce 100644
--- a/src/Network/BitTorrent/Exchange.hs
+++ b/src/Network/BitTorrent/Exchange.hs
@@ -18,7 +18,7 @@ module Network.BitTorrent.Exchange
18 , Event(..) 18 , Event(..)
19 19
20 , P2P, withPeer 20 , P2P, withPeer
21 , awaitEvent, signalEvent 21 , awaitEvent, yieldEvent
22 ) where 22 ) where
23 23
24import Control.Applicative 24import Control.Applicative
@@ -70,29 +70,31 @@ runConduit sock p2p =
70 conduitPut S.put $$ 70 conduitPut S.put $$
71 sinkSocket sock 71 sinkSocket sock
72 72
73waitMessage :: P2P Message 73awaitMessage :: P2P Message
74waitMessage = P2P (ReaderT go) 74awaitMessage = P2P (ReaderT go)
75 where 75 where
76 go se = do 76 go se = do
77 liftIO $ putStrLn "trying recv:"
77 mmsg <- await 78 mmsg <- await
78 case mmsg of 79 case mmsg of
79 Nothing -> go se 80 Nothing -> go se
80 Just msg -> do 81 Just msg -> do
81 liftIO $ updateIncoming se 82-- liftIO $ updateIncoming se
82 liftIO $ print msg 83 liftIO $ print ("recv:" <+> ppMessage msg)
83 return msg 84 return msg
84 85
85signalMessage :: Message -> P2P () 86yieldMessage :: Message -> P2P ()
86signalMessage msg = P2P $ ReaderT $ \se -> do 87yieldMessage msg = P2P $ ReaderT $ \se -> do
87 C.yield msg 88 C.yield msg
89 liftIO $ print $ "sent:" <+> ppMessage msg
88 liftIO $ updateOutcoming se 90 liftIO $ updateOutcoming se
89 91
90 92
91peerWant :: P2P Bitfield 93peerWant :: P2P Bitfield
92peerWant = BF.difference <$> getPeerBF <*> use bitfield 94peerWant = BF.difference <$> getClientBF <*> use bitfield
93 95
94clientWant :: P2P Bitfield 96clientWant :: P2P Bitfield
95clientWant = BF.difference <$> use bitfield <*> getPeerBF 97clientWant = BF.difference <$> use bitfield <*> getClientBF
96 98
97peerOffer :: P2P Bitfield 99peerOffer :: P2P Bitfield
98peerOffer = do 100peerOffer = do
@@ -104,13 +106,17 @@ clientOffer = do
104 sessionStatus <- use status 106 sessionStatus <- use status
105 if canUpload sessionStatus then peerWant else emptyBF 107 if canUpload sessionStatus then peerWant else emptyBF
106 108
107revise :: P2P () 109revise :: P2P Bitfield
108revise = do 110revise = do
109 peerInteresting <- (not . BF.null) <$> clientWant 111 want <- clientWant
112 let peerInteresting = not (BF.null want)
110 clientInterested <- use (status.clientStatus.interested) 113 clientInterested <- use (status.clientStatus.interested)
111 114
112 when (clientInterested /= peerInteresting) $ 115 when (clientInterested /= peerInteresting) $ do
113 signalMessage $ if peerInteresting then Interested else NotInterested 116 yieldMessage $ if peerInteresting then Interested else NotInterested
117 status.clientStatus.interested .= peerInteresting
118
119 return want
114 120
115requireExtension :: Extension -> P2P () 121requireExtension :: Extension -> P2P ()
116requireExtension required = do 122requireExtension required = do
@@ -118,9 +124,6 @@ requireExtension required = do
118 unless (required `elem` enabled) $ 124 unless (required `elem` enabled) $
119 sessionError $ ppExtension required <+> "not enabled" 125 sessionError $ ppExtension required <+> "not enabled"
120 126
121peerHave :: P2P Event
122peerHave = undefined
123
124-- haveMessage bf = do 127-- haveMessage bf = do
125-- cbf <- undefined -- liftIO $ readIORef $ clientBitfield swarmSession 128-- cbf <- undefined -- liftIO $ readIORef $ clientBitfield swarmSession
126-- if undefined -- ix `member` bf 129-- if undefined -- ix `member` bf
@@ -129,12 +132,21 @@ peerHave = undefined
129 132
130 133
131-- | 134-- |
135-- +----------+---------+
136-- | Leacher | Seeder |
137-- |----------+---------+
138-- | Available| |
139-- | Want | Want |
140-- | Fragment | |
141-- +----------+---------+
142--
143--
132-- properties: 144-- properties:
133-- 145--
134-- forall (Fragment block). isPiece block == True 146-- forall (Fragment block). isPiece block == True
135-- 147--
136awaitEvent :: P2P Event 148awaitEvent :: P2P Event
137awaitEvent = waitMessage >>= go 149awaitEvent = awaitMessage >>= go
138 where 150 where
139 go KeepAlive = awaitEvent 151 go KeepAlive = awaitEvent
140 go Choke = do 152 go Choke = do
@@ -142,8 +154,11 @@ awaitEvent = waitMessage >>= go
142 awaitEvent 154 awaitEvent
143 155
144 go Unchoke = do 156 go Unchoke = do
145 status.clientStatus.choking .= False 157 status.peerStatus.choking .= False
146 awaitEvent 158 offer <- peerOffer
159 if BF.null offer
160 then awaitEvent
161 else return (Available offer)
147 162
148 go Interested = do 163 go Interested = do
149 status.peerStatus.interested .= True 164 status.peerStatus.interested .= True
@@ -153,21 +168,45 @@ awaitEvent = waitMessage >>= go
153 status.peerStatus.interested .= False 168 status.peerStatus.interested .= False
154 awaitEvent 169 awaitEvent
155 170
156-- go (Have ix) = peerHave =<< singletonBF ix 171 go (Have ix) = do
157-- go (Bitfield bf) = peerHave =<< adjustBF bf 172 new <- singletonBF ix
173 bitfield %= BF.union new
174 revise
175
176 offer <- peerOffer
177 if not (BF.null offer)
178 then return (Available offer)
179 else awaitEvent
180
181 go (Bitfield bf) = do
182 new <- adjustBF bf
183 bitfield .= new
184 revise
185
186 offer <- peerOffer
187 if not (BF.null offer)
188 then return (Available offer)
189 else awaitEvent
190
158 go (Request bix) = do 191 go (Request bix) = do
159 bf <- use bitfield 192 bf <- clientOffer
160 if ixPiece bix `BF.member` bf 193 if ixPiece bix `BF.member` bf
161 then return (Want bix) 194 then return (Want bix)
162 else do 195 else do
163 signalMessage (RejectRequest bix) 196-- check if extension is enabled
197-- yieldMessage (RejectRequest bix)
164 awaitEvent 198 awaitEvent
165 199
166 go (Piece blk) = undefined 200 go (Piece blk) = do
201 -- this protect us from malicious peers and duplication
202 wanted <- clientWant
203 if blkPiece blk `BF.member` wanted
204 then return (Fragment blk)
205 else awaitEvent
167 206
168{- 207{-
169 go msg @ (Port _) 208 go (Port _) = do
170 = checkExtension msg ExtDHT $ do 209 requireExtension ExtDHT
171 undefined 210 undefined
172 211
173 go HaveAll = do 212 go HaveAll = do
@@ -189,24 +228,39 @@ awaitEvent = waitMessage >>= go
189 then Available <$> singletonBF ix 228 then Available <$> singletonBF ix
190 else awaitEvent 229 else awaitEvent
191 230
192 go msg @ (RejectRequest ix) 231 go (RejectRequest ix) = do
193 = checkExtension msg ExtFast $ do 232 requireExtension ExtFast
194 undefined 233 awaitMessage
195 234
196 go msg @ (AllowedFast pix) 235 go (AllowedFast pix) =
197 = checkExtension msg ExtFast $ do 236 requireExtension ExtFast
198 undefined 237 awaitMessage
199-} 238-}
200 239
201signalEvent :: Event -> P2P () 240
202signalEvent (Available bf) = undefined 241-- |
203signalEvent _ = undefined 242-- @
243-- +----------+---------+
244-- | Leacher | Seeder |
245-- |----------+---------+
246-- | Available| |
247-- | Want |Fragment |
248-- | Fragment | |
249-- +----------+---------+
250-- @
251--
252yieldEvent :: Event -> P2P ()
253yieldEvent (Available bf) = undefined
254yieldEvent _ = undefined
204 255
205--flushBroadcast :: P2P () 256--flushBroadcast :: P2P ()
206--flushBroadcast = nextBroadcast >>= maybe (return ()) go 257--flushBroadcast = nextBroadcast >>= maybe (return ()) go
207-- where 258-- where
208-- go = undefined 259-- go = undefined
209 260
261checkPiece :: PieceLIx -> {-ByteString -> -} P2P Bool
262checkPiece = undefined
263
210{----------------------------------------------------------------------- 264{-----------------------------------------------------------------------
211 P2P monad 265 P2P monad
212-----------------------------------------------------------------------} 266-----------------------------------------------------------------------}
diff --git a/src/Network/BitTorrent/Exchange/Protocol.hs b/src/Network/BitTorrent/Exchange/Protocol.hs
index 718e339d..6b97d8d1 100644
--- a/src/Network/BitTorrent/Exchange/Protocol.hs
+++ b/src/Network/BitTorrent/Exchange/Protocol.hs
@@ -164,14 +164,18 @@ defaultHandshake = Handshake defaultBTProtocol defaultReserved
164-- | Handshaking with a peer specified by the second argument. 164-- | Handshaking with a peer specified by the second argument.
165handshake :: Socket -> Handshake -> IO Handshake 165handshake :: Socket -> Handshake -> IO Handshake
166handshake sock hs = do 166handshake sock hs = do
167 putStrLn "send handshake"
167 sendAll sock (S.encode hs) 168 sendAll sock (S.encode hs)
168 169
170 putStrLn "recv handshake size"
169 header <- recv sock 1 171 header <- recv sock 1
170 when (B.length header == 0) $ 172 when (B.length header == 0) $
171 throw $ userError "Unable to receive handshake." 173 throw $ userError "Unable to receive handshake."
172 174
173 let protocolLen = B.head header 175 let protocolLen = B.head header
174 let restLen = handshakeSize protocolLen - 1 176 let restLen = handshakeSize protocolLen - 1
177
178 putStrLn "recv handshake body"
175 body <- recv sock restLen 179 body <- recv sock restLen
176 let resp = B.cons protocolLen body 180 let resp = B.cons protocolLen body
177 181
diff --git a/src/Network/BitTorrent/Internal.hs b/src/Network/BitTorrent/Internal.hs
index 2f538652..3d07a82f 100644
--- a/src/Network/BitTorrent/Internal.hs
+++ b/src/Network/BitTorrent/Internal.hs
@@ -34,8 +34,8 @@ module Network.BitTorrent.Internal
34 ) 34 )
35 , SessionState 35 , SessionState
36 , bitfield, status 36 , bitfield, status
37 , emptyBF, fullBF, singletonBF 37 , emptyBF, fullBF, singletonBF, adjustBF
38 , getPieceCount, getPeerBF 38 , getPieceCount, getClientBF
39 , sessionError, withPeerSession 39 , sessionError, withPeerSession
40 40
41 -- * Timeouts 41 -- * Timeouts
@@ -250,10 +250,15 @@ withPeerSession ss @ SwarmSession {..} addr
250 let caps = encodeExts $ allowedExtensions $ clientSession 250 let caps = encodeExts $ allowedExtensions $ clientSession
251 let pid = clientPeerID $ clientSession 251 let pid = clientPeerID $ clientSession
252 let chs = Handshake defaultBTProtocol caps (tInfoHash torrentMeta) pid 252 let chs = Handshake defaultBTProtocol caps (tInfoHash torrentMeta) pid
253 253 putStrLn "trying to connect"
254 sock <- connectToPeer addr 254 sock <- connectToPeer addr
255
256 putStrLn "trying to handshake"
255 phs <- handshake sock chs `onException` close sock 257 phs <- handshake sock chs `onException` close sock
256 258
259 cbf <- readTVarIO clientBitfield
260 sendAll sock (encode (Bitfield cbf))
261
257 let enabled = decodeExts (enabledCaps caps (handshakeCaps phs)) 262 let enabled = decodeExts (enabledCaps caps (handshakeCaps phs))
258 ps <- PeerSession addr ss enabled 263 ps <- PeerSession addr ss enabled
259 <$> registerTimeout (eventManager clientSession) 264 <$> registerTimeout (eventManager clientSession)
@@ -281,8 +286,11 @@ fullBF = liftM haveAll getPieceCount
281singletonBF :: (MonadReader PeerSession m) => PieceIx -> m Bitfield 286singletonBF :: (MonadReader PeerSession m) => PieceIx -> m Bitfield
282singletonBF ix = liftM (BF.singleton ix) getPieceCount 287singletonBF ix = liftM (BF.singleton ix) getPieceCount
283 288
284getPeerBF :: (MonadIO m, MonadReader PeerSession m) => m Bitfield 289adjustBF :: (MonadReader PeerSession m) => Bitfield -> m Bitfield
285getPeerBF = asks swarmSession >>= liftIO . readTVarIO . clientBitfield 290adjustBF bf = (`adjustSize` bf) `liftM` getPieceCount
291
292getClientBF :: (MonadIO m, MonadReader PeerSession m) => m Bitfield
293getClientBF = asks swarmSession >>= liftIO . readTVarIO . clientBitfield
286 294
287--data Signal = 295--data Signal =
288--nextBroadcast :: P2P (Maybe Signal) 296--nextBroadcast :: P2P (Maybe Signal)
@@ -317,7 +325,7 @@ updateOutcoming PeerSession {..} =
317sendKA :: Socket -> IO () 325sendKA :: Socket -> IO ()
318sendKA sock {- SwarmSession {..} -} = do 326sendKA sock {- SwarmSession {..} -} = do
319 print "I'm sending keep alive." 327 print "I'm sending keep alive."
320 sendAll sock (encode BT.KeepAlive) 328-- sendAll sock (encode BT.KeepAlive)
321-- let mgr = eventManager clientSession 329-- let mgr = eventManager clientSession
322-- updateTimeout mgr 330-- updateTimeout mgr
323 print "Done.." 331 print "Done.."
diff --git a/src/Network/BitTorrent/Peer.hs b/src/Network/BitTorrent/Peer.hs
index 9aa924d3..6e5db0e0 100644
--- a/src/Network/BitTorrent/Peer.hs
+++ b/src/Network/BitTorrent/Peer.hs
@@ -531,8 +531,12 @@ peerSockAddr = SockAddrInet <$> (g . peerPort) <*> (htonl . peerIP)
531-- | Tries to connect to peer using reasonable default parameters. 531-- | Tries to connect to peer using reasonable default parameters.
532connectToPeer :: PeerAddr -> IO Socket 532connectToPeer :: PeerAddr -> IO Socket
533connectToPeer p = do 533connectToPeer p = do
534 putStrLn "socket"
534 sock <- socket AF_INET Stream Network.Socket.defaultProtocol 535 sock <- socket AF_INET Stream Network.Socket.defaultProtocol
536
537 putStrLn "connect"
535 connect sock (peerSockAddr p) 538 connect sock (peerSockAddr p)
539 putStrLn "connected"
536 return sock 540 return sock
537 541
538-- | Pretty print peer address in human readable form. 542-- | Pretty print peer address in human readable form.
diff --git a/src/Network/BitTorrent/Tracker.hs b/src/Network/BitTorrent/Tracker.hs
index 275b5422..cb776431 100644
--- a/src/Network/BitTorrent/Tracker.hs
+++ b/src/Network/BitTorrent/Tracker.hs
@@ -94,8 +94,8 @@ genericReq ses pr = TRequest {
94 } 94 }
95 95
96 96
97-- | The first request to the tracker that should be created is 'startedReq'. 97-- | The first request to the tracker that should be created is
98-- It includes necessary 'Started' event field. 98-- 'startedReq'. It includes necessary 'Started' event field.
99-- 99--
100startedReq :: TConnection -> Progress -> TRequest 100startedReq :: TConnection -> Progress -> TRequest
101startedReq ses pr = (genericReq ses pr) { 101startedReq ses pr = (genericReq ses pr) {