summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bench/serialization.hs2
-rw-r--r--network-bittorrent.cabal2
-rw-r--r--src/Network/Torrent.hs6
-rw-r--r--src/Network/Torrent/PeerWire.hs100
4 files changed, 105 insertions, 5 deletions
diff --git a/bench/serialization.hs b/bench/serialization.hs
index 31b5b0bf..e7770a1c 100644
--- a/bench/serialization.hs
+++ b/bench/serialization.hs
@@ -6,7 +6,7 @@ import Control.DeepSeq
6import Criterion.Main 6import Criterion.Main
7import Data.ByteString (ByteString) 7import Data.ByteString (ByteString)
8import Data.Serialize 8import Data.Serialize
9import Network.Torrent.PWP 9import Network.Torrent
10 10
11 11
12instance NFData BlockIx where 12instance NFData BlockIx where
diff --git a/network-bittorrent.cabal b/network-bittorrent.cabal
index c8bcb955..4aa6123f 100644
--- a/network-bittorrent.cabal
+++ b/network-bittorrent.cabal
@@ -20,7 +20,7 @@ library
20 exposed-modules: Data.Torrent 20 exposed-modules: Data.Torrent
21 , Network.Torrent 21 , Network.Torrent
22 , Network.Torrent.THP 22 , Network.Torrent.THP
23 , Network.Torrent.PWP 23 , Network.Torrent.PeerWire
24 , Network.Torrent.PeerID 24 , Network.Torrent.PeerID
25 , Network.Torrent.Handshake 25 , Network.Torrent.Handshake
26 other-modules: 26 other-modules:
diff --git a/src/Network/Torrent.hs b/src/Network/Torrent.hs
index c1f659b7..30d19424 100644
--- a/src/Network/Torrent.hs
+++ b/src/Network/Torrent.hs
@@ -1,11 +1,11 @@
1module Network.Torrent 1module Network.Torrent
2 ( module Network.Torrent.PeerID 2 ( module Network.Torrent.PeerID
3 , module Network.Torrent.THP 3 , module Network.Torrent.THP
4 , module Network.Torrent.PeerWire
4 , module Network.Torrent.Handshake 5 , module Network.Torrent.Handshake
5 , module Network.Torrent.PWP
6 ) where 6 ) where
7 7
8import Network.Torrent.PWP 8import Network.Torrent.PeerID
9import Network.Torrent.THP 9import Network.Torrent.THP
10import Network.Torrent.PeerWire
10import Network.Torrent.Handshake 11import Network.Torrent.Handshake
11import Network.Torrent.PeerID \ No newline at end of file
diff --git a/src/Network/Torrent/PeerWire.hs b/src/Network/Torrent/PeerWire.hs
new file mode 100644
index 00000000..b1fae9f2
--- /dev/null
+++ b/src/Network/Torrent/PeerWire.hs
@@ -0,0 +1,100 @@
1{-# LANGUAGE DoAndIfThenElse #-}
2module Network.Torrent.PeerWire
3 ( Message(..), Block(..), BlockIx(..),
4 ) where
5
6import Control.Applicative
7import Data.ByteString (ByteString)
8import qualified Data.ByteString as B
9
10import Data.Serialize
11
12data BlockIx = BlockIx {
13 ixPiece :: {-# UNPACK #-} !Int -- ^ Zero-based piece index.
14 , ixOffset :: {-# UNPACK #-} !Int -- ^ Zero-based byte offset within the piece.
15 , ixLength :: {-# UNPACK #-} !Int -- ^ Block size starting from offset.
16 } deriving (Show, Eq)
17
18data Block = Block {
19 blkPiece :: Int -- ^ Zero-based piece index.
20 , blkOffset :: Int -- ^ Zero-based byte offset within the piece.
21 , blkData :: ByteString -- ^ Payload.
22 } deriving (Show, Eq)
23
24-- TODO comment message constructors
25data Message = KeepAlive
26 | Choke
27 | Unchoke
28 | Interested
29 | NotInterested
30 | Have Int
31 | Bitfield ByteString
32 | Request BlockIx
33 | Piece Block
34 | Cancel BlockIx
35 | Port Int
36 deriving (Show, Eq)
37
38getInt :: Get Int
39getInt = fromIntegral <$> getWord32be
40{-# INLINE getInt #-}
41
42putInt :: Putter Int
43putInt = putWord32be . fromIntegral
44{-# INLINE putInt #-}
45
46instance Serialize BlockIx where
47 {-# SPECIALIZE instance Serialize BlockIx #-}
48 get = BlockIx <$> getInt <*> getInt <*> getInt
49 {-# INLINE get #-}
50
51 put ix = do putInt (ixPiece ix)
52 putInt (ixOffset ix)
53 putInt (ixLength ix)
54 {-# INLINE put #-}
55
56instance Serialize Message where
57 get = do
58 len <- getInt
59 lookAhead $ ensure len
60 if len == 0 then return KeepAlive
61 else do
62 mid <- getWord8
63 case mid of
64 0 -> return Choke
65 1 -> return Unchoke
66 2 -> return Interested
67 3 -> return NotInterested
68 4 -> Have <$> getInt
69 5 -> Bitfield <$> getBytes (pred len)
70 6 -> Request <$> get
71 7 -> Piece <$> getBlock (len - 9)
72 8 -> Cancel <$> get
73 9 -> (Port . fromIntegral) <$> getWord16be
74 _ -> fail $ "unknown message ID: " ++ show mid
75
76 where
77 getBlock :: Int -> Get Block
78 getBlock len = Block <$> getInt <*> getInt <*> getBytes len
79 {-# INLINE getBlock #-}
80
81 put KeepAlive = putInt 0
82 put Choke = putInt 1 >> putWord8 0
83 put Unchoke = putInt 1 >> putWord8 1
84 put Interested = putInt 1 >> putWord8 2
85 put NotInterested = putInt 1 >> putWord8 3
86 put (Have i) = putInt 5 >> putWord8 4 >> putInt i
87 put (Bitfield b) = putInt l >> putWord8 5 >> putByteString b
88 where l = succ (B.length b)
89 {-# INLINE l #-}
90 put (Request blk) = putInt 13 >> putWord8 6 >> put blk
91 put (Piece blk) = putInt l >> putWord8 7 >> putBlock
92 where l = 9 + B.length (blkData blk)
93 {-# INLINE l #-}
94 putBlock = do putInt (blkPiece blk)
95 putInt (blkOffset blk)
96 putByteString (blkData blk)
97 {-# INLINE putBlock #-}
98
99 put (Cancel blk) = putInt 13 >> putWord8 8 >> put blk
100 put (Port p ) = putInt 3 >> putWord8 9 >> putWord16be (fromIntegral p) \ No newline at end of file