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