diff options
-rw-r--r-- | src/Network/BitTorrent/Peer.hs | 88 | ||||
-rw-r--r-- | src/Network/BitTorrent/Sessions/Types.lhs | 58 | ||||
-rw-r--r-- | src/Network/BitTorrent/Tracker.hs | 5 | ||||
-rw-r--r-- | src/Network/BitTorrent/Tracker/Protocol.hs | 32 | ||||
m--------- | sub/bencoding | 0 |
5 files changed, 96 insertions, 87 deletions
diff --git a/src/Network/BitTorrent/Peer.hs b/src/Network/BitTorrent/Peer.hs index a4d026dc..d951a1cd 100644 --- a/src/Network/BitTorrent/Peer.hs +++ b/src/Network/BitTorrent/Peer.hs | |||
@@ -28,6 +28,7 @@ | |||
28 | -- capabilities (such as supported enchancements), this should be | 28 | -- capabilities (such as supported enchancements), this should be |
29 | -- done using 'Network.BitTorrent.Extension'! | 29 | -- done using 'Network.BitTorrent.Extension'! |
30 | -- | 30 | -- |
31 | {-# LANGUAGE ViewPatterns #-} | ||
31 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} | 32 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} |
32 | {-# LANGUAGE StandaloneDeriving #-} | 33 | {-# LANGUAGE StandaloneDeriving #-} |
33 | {-# LANGUAGE TemplateHaskell #-} | 34 | {-# LANGUAGE TemplateHaskell #-} |
@@ -56,6 +57,19 @@ module Network.BitTorrent.Peer | |||
56 | , connectToPeer | 57 | , connectToPeer |
57 | , ppPeer | 58 | , ppPeer |
58 | 59 | ||
60 | -- * Peer progress | ||
61 | , Progress (..) | ||
62 | , left | ||
63 | , uploaded | ||
64 | , downloaded | ||
65 | |||
66 | , startProgress | ||
67 | |||
68 | , downloadedProgress | ||
69 | , enqueuedProgress | ||
70 | , uploadedProgress | ||
71 | , dequeuedProgress | ||
72 | |||
59 | -- * Client version detection | 73 | -- * Client version detection |
60 | -- ** Info | 74 | -- ** Info |
61 | , ClientInfo(..), clientInfo, ppClientInfo, unknownClient | 75 | , ClientInfo(..), clientInfo, ppClientInfo, unknownClient |
@@ -70,18 +84,19 @@ module Network.BitTorrent.Peer | |||
70 | 84 | ||
71 | 85 | ||
72 | import Control.Applicative | 86 | import Control.Applicative |
87 | import Control.Lens | ||
73 | import Data.Aeson | 88 | import Data.Aeson |
74 | import Data.Aeson.TH | 89 | import Data.Aeson.TH |
75 | import Data.BEncode | 90 | import Data.BEncode |
76 | import Data.Bits | 91 | import Data.Bits |
77 | import Data.Char | ||
78 | import Data.List as L | ||
79 | import Data.Word | ||
80 | import Data.ByteString (ByteString) | 92 | import Data.ByteString (ByteString) |
81 | import qualified Data.ByteString as B | 93 | import qualified Data.ByteString as B |
82 | import qualified Data.ByteString.Char8 as BC | 94 | import qualified Data.ByteString.Char8 as BC |
83 | import qualified Data.ByteString.Lazy as BL | 95 | import qualified Data.ByteString.Lazy as BL |
84 | import qualified Data.ByteString.Lazy.Builder as B | 96 | import qualified Data.ByteString.Lazy.Builder as B |
97 | import Data.Char | ||
98 | import Data.List as L | ||
99 | import Data.Word | ||
85 | import Data.Foldable (foldMap) | 100 | import Data.Foldable (foldMap) |
86 | import Data.Monoid ((<>)) | 101 | import Data.Monoid ((<>)) |
87 | import Data.Serialize | 102 | import Data.Serialize |
@@ -577,3 +592,70 @@ ppPeer p @ PeerAddr {..} = case peerID of | |||
577 | Nothing -> paddr | 592 | Nothing -> paddr |
578 | where | 593 | where |
579 | paddr = text (show (peerSockAddr p)) | 594 | paddr = text (show (peerSockAddr p)) |
595 | |||
596 | {----------------------------------------------------------------------- | ||
597 | Progress | ||
598 | -----------------------------------------------------------------------} | ||
599 | |||
600 | -- TODO: Use Word64? | ||
601 | -- TODO: Use atomic bits? | ||
602 | |||
603 | -- | 'Progress' contains upload/download/left stats about | ||
604 | -- current client state and used to notify the tracker. | ||
605 | -- | ||
606 | -- Progress data is considered as dynamic within one client | ||
607 | -- session. This data also should be shared across client application | ||
608 | -- sessions (e.g. files), otherwise use 'startProgress' to get initial | ||
609 | -- 'Progress'. | ||
610 | -- | ||
611 | data Progress = Progress | ||
612 | { _downloaded :: !Integer -- ^ Total amount of bytes downloaded; | ||
613 | , _left :: !Integer -- ^ Total amount of bytes left; | ||
614 | , _uploaded :: !Integer -- ^ Total amount of bytes uploaded. | ||
615 | } deriving (Show, Read, Eq) | ||
616 | |||
617 | $(makeLenses ''Progress) | ||
618 | $(deriveJSON (L.tail) ''Progress) | ||
619 | |||
620 | instance Serialize Progress where | ||
621 | put Progress {..} = do | ||
622 | putWord64be $ fromIntegral _downloaded | ||
623 | putWord64be $ fromIntegral _left | ||
624 | putWord64be $ fromIntegral _uploaded | ||
625 | |||
626 | get = Progress | ||
627 | <$> (fromIntegral <$> getWord64be) | ||
628 | <*> (fromIntegral <$> getWord64be) | ||
629 | <*> (fromIntegral <$> getWord64be) | ||
630 | |||
631 | -- | Initial progress is used when there are no session before. | ||
632 | -- | ||
633 | -- Please note that tracker might penalize client some way if the do | ||
634 | -- not accumulate progress. If possible and save 'Progress' between | ||
635 | -- client sessions to avoid that. | ||
636 | -- | ||
637 | startProgress :: Integer -> Progress | ||
638 | startProgress = Progress 0 0 | ||
639 | |||
640 | -- | Used when the client download some data from /any/ peer. | ||
641 | downloadedProgress :: Int -> Progress -> Progress | ||
642 | downloadedProgress (fromIntegral -> amount) | ||
643 | = (left -~ amount) | ||
644 | . (downloaded +~ amount) | ||
645 | {-# INLINE downloadedProgress #-} | ||
646 | |||
647 | -- | Used when the client upload some data to /any/ peer. | ||
648 | uploadedProgress :: Int -> Progress -> Progress | ||
649 | uploadedProgress (fromIntegral -> amount) = uploaded +~ amount | ||
650 | {-# INLINE uploadedProgress #-} | ||
651 | |||
652 | -- | Used when leecher join client session. | ||
653 | enqueuedProgress :: Integer -> Progress -> Progress | ||
654 | enqueuedProgress amount = left +~ amount | ||
655 | {-# INLINE enqueuedProgress #-} | ||
656 | |||
657 | -- | Used when leecher leave client session. | ||
658 | -- (e.g. user deletes not completed torrent) | ||
659 | dequeuedProgress :: Integer -> Progress -> Progress | ||
660 | dequeuedProgress amount = left -~ amount | ||
661 | {-# INLINE dequeuedProgress #-} | ||
diff --git a/src/Network/BitTorrent/Sessions/Types.lhs b/src/Network/BitTorrent/Sessions/Types.lhs index 3dde1853..7ee7cbec 100644 --- a/src/Network/BitTorrent/Sessions/Types.lhs +++ b/src/Network/BitTorrent/Sessions/Types.lhs | |||
@@ -17,11 +17,6 @@ | |||
17 | > , TorrentLoc (..) | 17 | > , TorrentLoc (..) |
18 | > , TorrentMap | 18 | > , TorrentMap |
19 | > | 19 | > |
20 | > , Progress (..) | ||
21 | > , left, uploaded, downloaded | ||
22 | > , startProgress | ||
23 | > , enqueuedProgress, uploadedProgress, dequeuedProgress | ||
24 | > | ||
25 | > , ClientSession (..) | 20 | > , ClientSession (..) |
26 | > | 21 | > |
27 | > , SwarmSession (..) | 22 | > , SwarmSession (..) |
@@ -65,7 +60,6 @@ | |||
65 | > import Network.BitTorrent.Extension | 60 | > import Network.BitTorrent.Extension |
66 | > import Network.BitTorrent.Peer | 61 | > import Network.BitTorrent.Peer |
67 | > import Network.BitTorrent.Exchange.Protocol as BT | 62 | > import Network.BitTorrent.Exchange.Protocol as BT |
68 | > import Network.BitTorrent.Tracker.Protocol as BT | ||
69 | > import System.Torrent.Storage | 63 | > import System.Torrent.Storage |
70 | 64 | ||
71 | Thread layout | 65 | Thread layout |
@@ -173,58 +167,6 @@ so we need to do this on demand: if a peer asks for a block, we | |||
173 | validate corresponding piece and only after read and send the block | 167 | validate corresponding piece and only after read and send the block |
174 | back. | 168 | back. |
175 | 169 | ||
176 | Progress | ||
177 | ------------------------------------------------------------------------ | ||
178 | |||
179 | Progress data is considered as dynamic within one client session. This | ||
180 | data also should be shared across client application sessions | ||
181 | (e.g. files), otherwise use 'startProgress' to get initial 'Progress'. | ||
182 | |||
183 | > -- | 'Progress' contains upload/download/left stats about | ||
184 | > -- current client state and used to notify the tracker. | ||
185 | > data Progress = Progress { | ||
186 | > _uploaded :: !Integer -- ^ Total amount of bytes uploaded. | ||
187 | > , _downloaded :: !Integer -- ^ Total amount of bytes downloaded. | ||
188 | > , _left :: !Integer -- ^ Total amount of bytes left. | ||
189 | > } deriving (Show, Read, Eq) | ||
190 | > | ||
191 | > $(makeLenses ''Progress) | ||
192 | |||
193 | **TODO:** Use Word64? | ||
194 | |||
195 | **TODO:** Use atomic bits? | ||
196 | |||
197 | Please note that tracker might penalize client some way if the do | ||
198 | not accumulate progress. If possible and save 'Progress' between | ||
199 | client sessions to avoid that. | ||
200 | |||
201 | > -- | Initial progress is used when there are no session before. | ||
202 | > startProgress :: Integer -> Progress | ||
203 | > startProgress = Progress 0 0 | ||
204 | |||
205 | > -- | Used when the client download some data from /any/ peer. | ||
206 | > downloadedProgress :: Int -> Progress -> Progress | ||
207 | > downloadedProgress (fromIntegral -> amount) | ||
208 | > = (left -~ amount) | ||
209 | > . (downloaded +~ amount) | ||
210 | > {-# INLINE downloadedProgress #-} | ||
211 | |||
212 | > -- | Used when the client upload some data to /any/ peer. | ||
213 | > uploadedProgress :: Int -> Progress -> Progress | ||
214 | > uploadedProgress (fromIntegral -> amount) = uploaded +~ amount | ||
215 | > {-# INLINE uploadedProgress #-} | ||
216 | |||
217 | > -- | Used when leecher join client session. | ||
218 | > enqueuedProgress :: Integer -> Progress -> Progress | ||
219 | > enqueuedProgress amount = left +~ amount | ||
220 | > {-# INLINE enqueuedProgress #-} | ||
221 | |||
222 | > -- | Used when leecher leave client session. | ||
223 | > -- (e.g. user deletes not completed torrent) | ||
224 | > dequeuedProgress :: Integer -> Progress -> Progress | ||
225 | > dequeuedProgress amount = left -~ amount | ||
226 | > {-# INLINE dequeuedProgress #-} | ||
227 | |||
228 | Client Sessions | 170 | Client Sessions |
229 | ------------------------------------------------------------------------ | 171 | ------------------------------------------------------------------------ |
230 | 172 | ||
diff --git a/src/Network/BitTorrent/Tracker.hs b/src/Network/BitTorrent/Tracker.hs index e98f1e94..c707cedd 100644 --- a/src/Network/BitTorrent/Tracker.hs +++ b/src/Network/BitTorrent/Tracker.hs | |||
@@ -41,7 +41,6 @@ import Network.URI | |||
41 | 41 | ||
42 | import Data.Torrent.Metainfo | 42 | import Data.Torrent.Metainfo |
43 | import Network.BitTorrent.Peer | 43 | import Network.BitTorrent.Peer |
44 | import Network.BitTorrent.Sessions.Types | ||
45 | import Network.BitTorrent.Tracker.Protocol | 44 | import Network.BitTorrent.Tracker.Protocol |
46 | import Network.BitTorrent.Tracker.HTTP | 45 | import Network.BitTorrent.Tracker.HTTP |
47 | 46 | ||
@@ -73,9 +72,7 @@ genericReq ses pr = AnnounceQuery { | |||
73 | , reqPeerId = tconnPeerId ses | 72 | , reqPeerId = tconnPeerId ses |
74 | , reqPort = tconnPort ses | 73 | , reqPort = tconnPort ses |
75 | 74 | ||
76 | , reqUploaded = _uploaded pr | 75 | , reqProgress = pr |
77 | , reqDownloaded = _downloaded pr | ||
78 | , reqLeft = _left pr | ||
79 | 76 | ||
80 | , reqIP = Nothing | 77 | , reqIP = Nothing |
81 | , reqNumWant = Nothing | 78 | , reqNumWant = Nothing |
diff --git a/src/Network/BitTorrent/Tracker/Protocol.hs b/src/Network/BitTorrent/Tracker/Protocol.hs index e7755a10..3f264aed 100644 --- a/src/Network/BitTorrent/Tracker/Protocol.hs +++ b/src/Network/BitTorrent/Tracker/Protocol.hs | |||
@@ -63,6 +63,7 @@ import Network | |||
63 | import Network.Socket | 63 | import Network.Socket |
64 | 64 | ||
65 | import Network.BitTorrent.Peer | 65 | import Network.BitTorrent.Peer |
66 | import Network.BitTorrent.Sessions.Types | ||
66 | 67 | ||
67 | {----------------------------------------------------------------------- | 68 | {----------------------------------------------------------------------- |
68 | Announce messages | 69 | Announce messages |
@@ -97,14 +98,8 @@ data AnnounceQuery = AnnounceQuery { | |||
97 | -- peers. Normally, tracker should respond with this port when | 98 | -- peers. Normally, tracker should respond with this port when |
98 | -- some peer request the tracker with the same info hash. | 99 | -- some peer request the tracker with the same info hash. |
99 | 100 | ||
100 | , reqUploaded :: !Integer | 101 | , reqProgress :: !Progress |
101 | -- ^ Number of bytes that the peer has uploaded in the swarm. | 102 | -- ^ Current progress of peer doing request. |
102 | |||
103 | , reqDownloaded :: !Integer | ||
104 | -- ^ Number of bytes downloaded in the swarm by the peer. | ||
105 | |||
106 | , reqLeft :: !Integer | ||
107 | -- ^ Number of bytes needed in order to complete download. | ||
108 | 103 | ||
109 | , reqIP :: Maybe HostAddress | 104 | , reqIP :: Maybe HostAddress |
110 | -- ^ The peer IP. Needed only when client communicated with | 105 | -- ^ The peer IP. Needed only when client communicated with |
@@ -213,9 +208,9 @@ instance URLEncode AnnounceQuery where | |||
213 | urlEncode AnnounceQuery {..} = mconcat | 208 | urlEncode AnnounceQuery {..} = mconcat |
214 | [ s "peer_id" %= reqPeerId | 209 | [ s "peer_id" %= reqPeerId |
215 | , s "port" %= reqPort | 210 | , s "port" %= reqPort |
216 | , s "uploaded" %= reqUploaded | 211 | , s "uploaded" %= _uploaded reqProgress |
217 | , s "downloaded" %= reqDownloaded | 212 | , s "left" %= _left reqProgress |
218 | , s "left" %= reqLeft | 213 | , s "downloaded" %= _downloaded reqProgress |
219 | , s "ip" %=? reqIP | 214 | , s "ip" %=? reqIP |
220 | , s "numwant" %=? reqNumWant | 215 | , s "numwant" %=? reqNumWant |
221 | , s "event" %=? reqEvent | 216 | , s "event" %=? reqEvent |
@@ -248,15 +243,12 @@ getEvent = do | |||
248 | 3 -> return $ Just Stopped | 243 | 3 -> return $ Just Stopped |
249 | _ -> fail "unknown event id" | 244 | _ -> fail "unknown event id" |
250 | 245 | ||
246 | |||
251 | instance Serialize AnnounceQuery where | 247 | instance Serialize AnnounceQuery where |
252 | put AnnounceQuery {..} = do | 248 | put AnnounceQuery {..} = do |
253 | put reqInfoHash | 249 | put reqInfoHash |
254 | put reqPeerId | 250 | put reqPeerId |
255 | 251 | put reqProgress | |
256 | putWord64be $ fromIntegral reqDownloaded | ||
257 | putWord64be $ fromIntegral reqLeft | ||
258 | putWord64be $ fromIntegral reqUploaded | ||
259 | |||
260 | putEvent reqEvent | 252 | putEvent reqEvent |
261 | putWord32be $ fromMaybe 0 reqIP | 253 | putWord32be $ fromMaybe 0 reqIP |
262 | putWord32be $ 0 -- TODO what the fuck is "key"? | 254 | putWord32be $ 0 -- TODO what the fuck is "key"? |
@@ -268,9 +260,7 @@ instance Serialize AnnounceQuery where | |||
268 | ih <- get | 260 | ih <- get |
269 | pid <- get | 261 | pid <- get |
270 | 262 | ||
271 | down <- getWord64be | 263 | progress <- get |
272 | left <- getWord64be | ||
273 | up <- getWord64be | ||
274 | 264 | ||
275 | ev <- getEvent | 265 | ev <- getEvent |
276 | ip <- getWord32be | 266 | ip <- getWord32be |
@@ -283,9 +273,7 @@ instance Serialize AnnounceQuery where | |||
283 | reqInfoHash = ih | 273 | reqInfoHash = ih |
284 | , reqPeerId = pid | 274 | , reqPeerId = pid |
285 | , reqPort = port | 275 | , reqPort = port |
286 | , reqUploaded = fromIntegral up | 276 | , reqProgress = progress |
287 | , reqDownloaded = fromIntegral down | ||
288 | , reqLeft = fromIntegral left | ||
289 | , reqIP = if ip == 0 then Nothing else Just ip | 277 | , reqIP = if ip == 0 then Nothing else Just ip |
290 | , reqNumWant = if want == -1 then Nothing else Just (fromIntegral want) | 278 | , reqNumWant = if want == -1 then Nothing else Just (fromIntegral want) |
291 | , reqEvent = ev | 279 | , reqEvent = ev |
diff --git a/sub/bencoding b/sub/bencoding | |||
Subproject 027333ccd04d3627c1fbebdf5d3b736c5d84865 | Subproject 35913c887d8ef8f4e42398755bafb1439cd7c1a | ||