summaryrefslogtreecommitdiff
path: root/src/Network/BitTorrent/Exchange
diff options
context:
space:
mode:
authorSam Truzjan <pxqr.sta@gmail.com>2013-11-27 13:32:19 +0400
committerSam Truzjan <pxqr.sta@gmail.com>2013-11-27 13:32:19 +0400
commit2bd418d50f7f0dd5ff1db7e65a7727ed22edb4fe (patch)
treea69cd99e816ccdf64ec7afd80f6ea94f012dfa75 /src/Network/BitTorrent/Exchange
parentc95d9bb584d928c2aaa9b96b0be6ff70e75ce1fd (diff)
Use Pretty class in exchange protocol
Diffstat (limited to 'src/Network/BitTorrent/Exchange')
-rw-r--r--src/Network/BitTorrent/Exchange/Protocol.hs98
1 files changed, 51 insertions, 47 deletions
diff --git a/src/Network/BitTorrent/Exchange/Protocol.hs b/src/Network/BitTorrent/Exchange/Protocol.hs
index 4db1e315..7af99335 100644
--- a/src/Network/BitTorrent/Exchange/Protocol.hs
+++ b/src/Network/BitTorrent/Exchange/Protocol.hs
@@ -29,17 +29,20 @@
29{-# OPTIONS -fno-warn-orphans #-} 29{-# OPTIONS -fno-warn-orphans #-}
30module Network.BitTorrent.Exchange.Protocol 30module Network.BitTorrent.Exchange.Protocol
31 ( -- * Initial handshake 31 ( -- * Initial handshake
32 Handshake(..), ppHandshake 32 Handshake(..)
33 , handshake, handshakeCaps 33 , handshake
34 , recvHandshake, sendHandshake 34 , handshakeCaps
35 , recvHandshake
36 , sendHandshake
35 37
36 -- ** Defaults 38 -- ** Defaults
37 , defaultHandshake, defaultBTProtocol, defaultReserved 39 , defaultHandshake
40 , defaultBTProtocol
41 , defaultReserved
38 , handshakeMaxSize 42 , handshakeMaxSize
39 43
40 -- * Regular messages 44 -- * Regular messages
41 , Message(..) 45 , Message(..)
42 , ppMessage
43 46
44 -- * control 47 -- * control
45 , PeerStatus(..) 48 , PeerStatus(..)
@@ -58,31 +61,29 @@ import Control.Applicative
58import Control.Exception 61import Control.Exception
59import Control.Monad 62import Control.Monad
60import Control.Lens 63import Control.Lens
61
62import Data.Aeson.TH 64import Data.Aeson.TH
63import Data.ByteString (ByteString)
64import qualified Data.ByteString as B
65import qualified Data.ByteString.Char8 as BC
66import qualified Data.ByteString.Lazy as Lazy
67import Data.Default
68import Data.List as L
69import Data.Word
70
71import Data.Binary as B 65import Data.Binary as B
72import Data.Binary.Get as B 66import Data.Binary.Get as B
73import Data.Binary.Put as B 67import Data.Binary.Put as B
68import Data.ByteString as BS
69import Data.ByteString.Char8 as BC
70import Data.ByteString.Lazy as BL
71import Data.Default
72import Data.List as L
74import Data.Serialize as S 73import Data.Serialize as S
75 74import Data.Word
76import Text.PrettyPrint
77
78import Network 75import Network
79import Network.Socket.ByteString 76import Network.Socket.ByteString
77import Text.PrettyPrint
78import Text.PrettyPrint.Class
80 79
81import Data.Torrent.Bitfield 80import Data.Torrent.Bitfield
82import Data.Torrent.Block 81import Data.Torrent.Block
82import Data.Torrent.InfoHash
83import Data.Torrent 83import Data.Torrent
84import Network.BitTorrent.Extension 84import Network.BitTorrent.Extension
85import Network.BitTorrent.Peer 85import Network.BitTorrent.Core.PeerId
86import Network.BitTorrent.Core.PeerAddr
86 87
87 88
88getInt :: S.Get Int 89getInt :: S.Get Int
@@ -110,7 +111,7 @@ putIntB = B.putWord32be . fromIntegral
110-- 111--
111data Handshake = Handshake { 112data Handshake = Handshake {
112 -- | Identifier of the protocol. 113 -- | Identifier of the protocol.
113 hsProtocol :: ByteString 114 hsProtocol :: BS.ByteString
114 115
115 -- | Reserved bytes used to specify supported BEP's. 116 -- | Reserved bytes used to specify supported BEP's.
116 , hsReserved :: Capabilities 117 , hsReserved :: Capabilities
@@ -131,7 +132,7 @@ data Handshake = Handshake {
131 132
132instance Serialize Handshake where 133instance Serialize Handshake where
133 put hs = do 134 put hs = do
134 S.putWord8 (fromIntegral (B.length (hsProtocol hs))) 135 S.putWord8 (fromIntegral (BS.length (hsProtocol hs)))
135 S.putByteString (hsProtocol hs) 136 S.putByteString (hsProtocol hs)
136 S.putWord64be (hsReserved hs) 137 S.putWord64be (hsReserved hs)
137 S.put (hsInfoHash hs) 138 S.put (hsInfoHash hs)
@@ -144,14 +145,14 @@ instance Serialize Handshake where
144 <*> S.get 145 <*> S.get
145 <*> S.get 146 <*> S.get
146 147
148instance Pretty Handshake where
149 pretty Handshake {..}
150 = text (BC.unpack hsProtocol) <+> pretty (clientInfo hsPeerId)
151
147-- | Extract capabilities from a peer handshake message. 152-- | Extract capabilities from a peer handshake message.
148handshakeCaps :: Handshake -> Capabilities 153handshakeCaps :: Handshake -> Capabilities
149handshakeCaps = hsReserved 154handshakeCaps = hsReserved
150 155
151-- | Format handshake in human readable form.
152ppHandshake :: Handshake -> Doc
153ppHandshake Handshake {..} =
154 text (BC.unpack hsProtocol) <+> ppClientInfo (clientInfo hsPeerId)
155 156
156-- | Get handshake message size in bytes from the length of protocol 157-- | Get handshake message size in bytes from the length of protocol
157-- string. 158-- string.
@@ -163,7 +164,7 @@ handshakeMaxSize :: Int
163handshakeMaxSize = handshakeSize 255 164handshakeMaxSize = handshakeSize 255
164 165
165-- | Default protocol string "BitTorrent protocol" as is. 166-- | Default protocol string "BitTorrent protocol" as is.
166defaultBTProtocol :: ByteString 167defaultBTProtocol :: BS.ByteString
167defaultBTProtocol = "BitTorrent protocol" 168defaultBTProtocol = "BitTorrent protocol"
168 169
169-- | Default reserved word is 0. 170-- | Default reserved word is 0.
@@ -181,14 +182,14 @@ sendHandshake sock hs = sendAll sock (S.encode hs)
181recvHandshake :: Socket -> IO Handshake 182recvHandshake :: Socket -> IO Handshake
182recvHandshake sock = do 183recvHandshake sock = do
183 header <- recv sock 1 184 header <- recv sock 1
184 unless (B.length header == 1) $ 185 unless (BS.length header == 1) $
185 throw $ userError "Unable to receive handshake header." 186 throw $ userError "Unable to receive handshake header."
186 187
187 let protocolLen = B.head header 188 let protocolLen = BS.head header
188 let restLen = handshakeSize protocolLen - 1 189 let restLen = handshakeSize protocolLen - 1
189 190
190 body <- recv sock restLen 191 body <- recv sock restLen
191 let resp = B.cons protocolLen body 192 let resp = BS.cons protocolLen body
192 either (throwIO . userError) return $ S.decode resp 193 either (throwIO . userError) return $ S.decode resp
193 194
194-- | Handshaking with a peer specified by the second argument. 195-- | Handshaking with a peer specified by the second argument.
@@ -211,6 +212,7 @@ handshake sock hs = do
211-- extension then the client MUST close the connection. 212-- extension then the client MUST close the connection.
212-- 213--
213data Message = KeepAlive 214data Message = KeepAlive
215 -- TODO data PeerStatusUpdate = Choke | Unchoke | Interested | NotInterested
214 | Choke 216 | Choke
215 | Unchoke 217 | Unchoke
216 | Interested 218 | Interested
@@ -232,7 +234,7 @@ data Message = KeepAlive
232 | Request !BlockIx 234 | Request !BlockIx
233 235
234 -- | Response for a request for a block. 236 -- | Response for a request for a block.
235 | Piece !Block 237 | Piece !(Block BL.ByteString)
236 238
237 -- | Used to cancel block requests. It is typically 239 -- | Used to cancel block requests. It is typically
238 -- used during "End Game". 240 -- used during "End Game".
@@ -240,6 +242,7 @@ data Message = KeepAlive
240 242
241 | Port !PortNumber 243 | Port !PortNumber
242 244
245 -- TODO data FastMessage = HaveAll | HaveNone | ...
243 -- | BEP 6: Then peer have all pieces it might send the 246 -- | BEP 6: Then peer have all pieces it might send the
244 -- 'HaveAll' message instead of 'Bitfield' 247 -- 'HaveAll' message instead of 'Bitfield'
245 -- message. Used to save bandwidth. 248 -- message. Used to save bandwidth.
@@ -265,6 +268,19 @@ data Message = KeepAlive
265 | AllowedFast !PieceIx 268 | AllowedFast !PieceIx
266 deriving (Show, Eq) 269 deriving (Show, Eq)
267 270
271instance Default Message where
272 def = KeepAlive
273 {-# INLINE def #-}
274
275-- | Payload bytes are omitted.
276instance Pretty Message where
277 pretty (Bitfield _) = "Bitfield"
278 pretty (Piece blk) = "Piece" <+> pretty blk
279 pretty (Cancel i ) = "Cancel" <+> pretty i
280 pretty (SuggestPiece pix) = "Suggest" <+> int pix
281 pretty (RejectRequest i ) = "Reject" <+> pretty i
282 pretty msg = text (show msg)
283
268instance Serialize Message where 284instance Serialize Message where
269 get = do 285 get = do
270 len <- getInt 286 len <- getInt
@@ -294,7 +310,7 @@ instance Serialize Message where
294 ++ "remaining available bytes: " ++ show rm 310 ++ "remaining available bytes: " ++ show rm
295 311
296 where 312 where
297 getBlock :: Int -> S.Get Block 313 getBlock :: Int -> S.Get (Block BL.ByteString)
298 getBlock len = Block <$> getInt <*> getInt <*> S.getLazyByteString (fromIntegral len) 314 getBlock len = Block <$> getInt <*> getInt <*> S.getLazyByteString (fromIntegral len)
299 {-# INLINE getBlock #-} 315 {-# INLINE getBlock #-}
300 316
@@ -307,11 +323,11 @@ instance Serialize Message where
307 put (Have i) = putInt 5 >> S.putWord8 0x04 >> putInt i 323 put (Have i) = putInt 5 >> S.putWord8 0x04 >> putInt i
308 put (Bitfield bf) = putInt l >> S.putWord8 0x05 >> S.putLazyByteString b 324 put (Bitfield bf) = putInt l >> S.putWord8 0x05 >> S.putLazyByteString b
309 where b = toBitmap bf 325 where b = toBitmap bf
310 l = succ (fromIntegral (Lazy.length b)) 326 l = succ (fromIntegral (BL.length b))
311 {-# INLINE l #-} 327 {-# INLINE l #-}
312 put (Request blk) = putInt 13 >> S.putWord8 0x06 >> S.put blk 328 put (Request blk) = putInt 13 >> S.putWord8 0x06 >> S.put blk
313 put (Piece blk) = putInt l >> S.putWord8 0x07 >> putBlock 329 put (Piece blk) = putInt l >> S.putWord8 0x07 >> putBlock
314 where l = 9 + fromIntegral (Lazy.length (blkData blk)) 330 where l = 9 + fromIntegral (BL.length (blkData blk))
315 {-# INLINE l #-} 331 {-# INLINE l #-}
316 putBlock = do putInt (blkPiece blk) 332 putBlock = do putInt (blkPiece blk)
317 putInt (blkOffset blk) 333 putInt (blkOffset blk)
@@ -351,7 +367,7 @@ instance Binary Message where
351 0x11 -> AllowedFast <$> getIntB 367 0x11 -> AllowedFast <$> getIntB
352 _ -> fail $ "unknown message ID: " ++ show mid 368 _ -> fail $ "unknown message ID: " ++ show mid
353 where 369 where
354 getBlock :: Int -> B.Get Block 370 getBlock :: Int -> B.Get (Block BL.ByteString)
355 getBlock len = Block <$> getIntB <*> getIntB 371 getBlock len = Block <$> getIntB <*> getIntB
356 <*> B.getLazyByteString (fromIntegral len) 372 <*> B.getLazyByteString (fromIntegral len)
357 {-# INLINE getBlock #-} 373 {-# INLINE getBlock #-}
@@ -364,11 +380,11 @@ instance Binary Message where
364 put (Have i) = putIntB 5 >> B.putWord8 0x04 >> putIntB i 380 put (Have i) = putIntB 5 >> B.putWord8 0x04 >> putIntB i
365 put (Bitfield bf) = putIntB l >> B.putWord8 0x05 >> B.putLazyByteString b 381 put (Bitfield bf) = putIntB l >> B.putWord8 0x05 >> B.putLazyByteString b
366 where b = toBitmap bf 382 where b = toBitmap bf
367 l = succ (fromIntegral (Lazy.length b)) 383 l = succ (fromIntegral (BL.length b))
368 {-# INLINE l #-} 384 {-# INLINE l #-}
369 put (Request blk) = putIntB 13 >> B.putWord8 0x06 >> B.put blk 385 put (Request blk) = putIntB 13 >> B.putWord8 0x06 >> B.put blk
370 put (Piece blk) = putIntB l >> B.putWord8 0x07 >> putBlock 386 put (Piece blk) = putIntB l >> B.putWord8 0x07 >> putBlock
371 where l = 9 + fromIntegral (Lazy.length (blkData blk)) 387 where l = 9 + fromIntegral (BL.length (blkData blk))
372 {-# INLINE l #-} 388 {-# INLINE l #-}
373 putBlock = do putIntB (blkPiece blk) 389 putBlock = do putIntB (blkPiece blk)
374 putIntB (blkOffset blk) 390 putIntB (blkOffset blk)
@@ -383,18 +399,6 @@ instance Binary Message where
383 put (RejectRequest i ) = putIntB 13 >> B.putWord8 0x10 >> B.put i 399 put (RejectRequest i ) = putIntB 13 >> B.putWord8 0x10 >> B.put i
384 put (AllowedFast i ) = putIntB 5 >> B.putWord8 0x11 >> putIntB i 400 put (AllowedFast i ) = putIntB 5 >> B.putWord8 0x11 >> putIntB i
385 401
386-- | Format messages in human readable form. Note that output is
387-- compact and suitable for logging: only useful information but not
388-- payload bytes.
389--
390ppMessage :: Message -> Doc
391ppMessage (Bitfield _) = "Bitfield"
392ppMessage (Piece blk) = "Piece" <+> ppBlock blk
393ppMessage (Cancel i ) = "Cancel" <+> ppBlockIx i
394ppMessage (SuggestPiece pix) = "Suggest" <+> int pix
395ppMessage (RejectRequest i ) = "Reject" <+> ppBlockIx i
396ppMessage msg = text (show msg)
397
398{----------------------------------------------------------------------- 402{-----------------------------------------------------------------------
399 Peer Status 403 Peer Status
400-----------------------------------------------------------------------} 404-----------------------------------------------------------------------}