diff options
Diffstat (limited to 'src/Network/BitTorrent/Exchange/Status.hs')
-rw-r--r-- | src/Network/BitTorrent/Exchange/Status.hs | 102 |
1 files changed, 78 insertions, 24 deletions
diff --git a/src/Network/BitTorrent/Exchange/Status.hs b/src/Network/BitTorrent/Exchange/Status.hs index ae323e09..42766428 100644 --- a/src/Network/BitTorrent/Exchange/Status.hs +++ b/src/Network/BitTorrent/Exchange/Status.hs | |||
@@ -1,80 +1,134 @@ | |||
1 | -- | | ||
2 | -- Copyright : (c) Sam Truzjan 2013 | ||
3 | -- License : BSD3 | ||
4 | -- Maintainer : pxqr.sta@gmail.com | ||
5 | -- Stability : experimental | ||
6 | -- Portability : portable | ||
7 | -- | ||
8 | -- Each P2P connection endpoint should keep track status of both | ||
9 | -- sides. | ||
10 | -- | ||
1 | {-# LANGUAGE TemplateHaskell #-} | 11 | {-# LANGUAGE TemplateHaskell #-} |
2 | module Network.BitTorrent.Exchange.Status | 12 | module Network.BitTorrent.Exchange.Status |
3 | ( -- * Peer status | 13 | ( -- * Peer status |
4 | PeerStatus(..) | 14 | PeerStatus(..) |
5 | , choking | 15 | , choking |
6 | , interested | 16 | , interested |
17 | |||
18 | -- ** Query | ||
7 | , updateStatus | 19 | , updateStatus |
20 | , statusUpdates | ||
8 | 21 | ||
9 | -- * Session status | 22 | -- * Session status |
10 | , SessionStatus(..) | 23 | , SessionStatus(..) |
11 | , clientStatus | 24 | , clientStatus |
12 | , peerStatus | 25 | , remoteStatus |
13 | 26 | ||
14 | -- ** Query | 27 | -- ** Query |
15 | , canUpload | 28 | , canUpload |
16 | , canDownload | 29 | , canDownload |
17 | 30 | ||
18 | -- * Extra | 31 | -- * Extra |
19 | , inverseStatus | ||
20 | , defaultUnchokeSlots | 32 | , defaultUnchokeSlots |
33 | , defaultRechokeInterval | ||
21 | ) where | 34 | ) where |
22 | 35 | ||
23 | import Control.Lens | 36 | import Control.Lens |
24 | import Data.Aeson.TH | 37 | import Data.Aeson.TH |
25 | import Data.List as L | ||
26 | import Data.Default | 38 | import Data.Default |
39 | import Data.List as L | ||
40 | import Data.Maybe | ||
41 | import Data.Monoid | ||
42 | import Text.PrettyPrint as PP hiding ((<>)) | ||
43 | import Text.PrettyPrint.Class | ||
27 | 44 | ||
28 | import Network.BitTorrent.Exchange.Message | 45 | import Network.BitTorrent.Exchange.Message |
29 | 46 | ||
47 | {----------------------------------------------------------------------- | ||
48 | -- Peer status | ||
49 | -----------------------------------------------------------------------} | ||
30 | 50 | ||
31 | -- | | 51 | -- | Connections contain two bits of state on either end: choked or |
32 | data PeerStatus = PeerStatus { | 52 | -- not, and interested or not. |
53 | data PeerStatus = PeerStatus | ||
54 | { -- | Choking is a notification that no data will be sent until | ||
55 | -- unchoking happens. | ||
33 | _choking :: !Bool | 56 | _choking :: !Bool |
57 | |||
58 | -- | | ||
34 | , _interested :: !Bool | 59 | , _interested :: !Bool |
35 | } deriving (Show, Eq) | 60 | } deriving (Show, Eq, Ord) |
36 | 61 | ||
37 | $(makeLenses ''PeerStatus) | 62 | $(makeLenses ''PeerStatus) |
38 | $(deriveJSON defaultOptions { fieldLabelModifier = L.tail } ''PeerStatus) | 63 | $(deriveJSON defaultOptions { fieldLabelModifier = L.tail } ''PeerStatus) |
39 | 64 | ||
65 | instance Pretty PeerStatus where | ||
66 | pretty PeerStatus {..} = | ||
67 | pretty (Choking _choking) <+> "and" <+> pretty (Interested _interested) | ||
68 | |||
69 | -- | Connections start out choked and not interested. | ||
40 | instance Default PeerStatus where | 70 | instance Default PeerStatus where |
41 | def = PeerStatus True False | 71 | def = PeerStatus True False |
42 | 72 | ||
73 | instance Monoid PeerStatus where | ||
74 | mempty = def | ||
75 | mappend a b = PeerStatus | ||
76 | { _choking = _choking a && _choking b | ||
77 | , _interested = _interested a || _interested b | ||
78 | } | ||
79 | |||
80 | -- | Can be used to update remote peer status using incoming 'Status' | ||
81 | -- message. | ||
43 | updateStatus :: StatusUpdate -> PeerStatus -> PeerStatus | 82 | updateStatus :: StatusUpdate -> PeerStatus -> PeerStatus |
44 | updateStatus Choke = choking .~ True | 83 | updateStatus (Choking b) = choking .~ b |
45 | updateStatus Unchoke = choking .~ False | 84 | updateStatus (Interested b) = interested .~ b |
46 | updateStatus Interested = interested .~ True | ||
47 | updateStatus NotInterested = interested .~ False | ||
48 | 85 | ||
86 | -- | Can be used to generate outcoming messages. | ||
49 | statusUpdates :: PeerStatus -> PeerStatus -> [StatusUpdate] | 87 | statusUpdates :: PeerStatus -> PeerStatus -> [StatusUpdate] |
50 | statusUpdates a b = undefined | 88 | statusUpdates a b = catMaybes $ |
51 | 89 | [ if _choking a == _choking b then Nothing | |
52 | -- | | 90 | else Just $ Choking $ _choking b |
53 | data SessionStatus = SessionStatus { | 91 | , if _interested a == _interested b then Nothing |
54 | _clientStatus :: !PeerStatus | 92 | else Just $ Interested $ _interested b |
55 | , _peerStatus :: !PeerStatus | 93 | ] |
94 | |||
95 | {----------------------------------------------------------------------- | ||
96 | -- Session status | ||
97 | -----------------------------------------------------------------------} | ||
98 | |||
99 | -- | Status of the both endpoints. | ||
100 | data SessionStatus = SessionStatus | ||
101 | { _clientStatus :: !PeerStatus | ||
102 | , _remoteStatus :: !PeerStatus | ||
56 | } deriving (Show, Eq) | 103 | } deriving (Show, Eq) |
57 | 104 | ||
58 | $(makeLenses ''SessionStatus) | 105 | $(makeLenses ''SessionStatus) |
59 | $(deriveJSON defaultOptions { fieldLabelModifier = L.tail } ''SessionStatus) | 106 | $(deriveJSON defaultOptions { fieldLabelModifier = L.tail } ''SessionStatus) |
60 | 107 | ||
108 | instance Pretty SessionStatus where | ||
109 | pretty SessionStatus {..} = | ||
110 | "this " <+> pretty _clientStatus $$ | ||
111 | "remote" <+> pretty _remoteStatus | ||
112 | |||
113 | -- | Connections start out choked and not interested. | ||
61 | instance Default SessionStatus where | 114 | instance Default SessionStatus where |
62 | def = SessionStatus def def | 115 | def = SessionStatus def def |
63 | 116 | ||
64 | -- | Can the /client/ transfer to the /peer/? | 117 | -- | Can the client transfer to the remote peer? |
65 | canUpload :: SessionStatus -> Bool | 118 | canUpload :: SessionStatus -> Bool |
66 | canUpload SessionStatus {..} | 119 | canUpload SessionStatus {..} |
67 | = _interested _peerStatus && not (_choking _clientStatus) | 120 | = _interested _remoteStatus && not (_choking _clientStatus) |
68 | 121 | ||
69 | -- | Can the /client/ transfer from the /peer/? | 122 | -- | Can the client transfer from the remote peer? |
70 | canDownload :: SessionStatus -> Bool | 123 | canDownload :: SessionStatus -> Bool |
71 | canDownload SessionStatus {..} | 124 | canDownload SessionStatus {..} |
72 | = _interested _clientStatus && not (_choking _peerStatus) | 125 | = _interested _clientStatus && not (_choking _remoteStatus) |
73 | |||
74 | inverseStatus :: SessionStatus -> SessionStatus | ||
75 | inverseStatus SessionStatus {..} = SessionStatus _peerStatus _clientStatus | ||
76 | 126 | ||
77 | -- | Indicates how many peers are allowed to download from the client | 127 | -- | Indicates how many peers are allowed to download from the client |
78 | -- by default. | 128 | -- by default. |
79 | defaultUnchokeSlots :: Int | 129 | defaultUnchokeSlots :: Int |
80 | defaultUnchokeSlots = 4 \ No newline at end of file | 130 | defaultUnchokeSlots = 4 |
131 | |||
132 | -- | | ||
133 | defaultRechokeInterval :: Int | ||
134 | defaultRechokeInterval = 10 * 1000 * 1000 \ No newline at end of file | ||