diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Data/Torrent.hs | 51 | ||||
-rw-r--r-- | src/Network/BitTorrent/Exchange/Protocol.hs | 6 | ||||
-rw-r--r-- | src/Network/BitTorrent/Tracker.hs | 6 | ||||
-rw-r--r-- | src/Network/BitTorrent/Tracker/Protocol.hs | 33 |
4 files changed, 55 insertions, 41 deletions
diff --git a/src/Data/Torrent.hs b/src/Data/Torrent.hs index 068e9cb6..ad57403e 100644 --- a/src/Data/Torrent.hs +++ b/src/Data/Torrent.hs | |||
@@ -21,6 +21,7 @@ | |||
21 | {-# LANGUAGE FlexibleInstances #-} | 21 | {-# LANGUAGE FlexibleInstances #-} |
22 | {-# LANGUAGE OverloadedStrings #-} | 22 | {-# LANGUAGE OverloadedStrings #-} |
23 | {-# LANGUAGE RecordWildCards #-} | 23 | {-# LANGUAGE RecordWildCards #-} |
24 | {-# LANGUAGE BangPatterns #-} | ||
24 | -- TODO refine interface | 25 | -- TODO refine interface |
25 | module Data.Torrent | 26 | module Data.Torrent |
26 | ( -- * Torrent | 27 | ( -- * Torrent |
@@ -83,14 +84,14 @@ type Time = Text | |||
83 | -- TODO more convenient form of torrent info. | 84 | -- TODO more convenient form of torrent info. |
84 | -- | Metainfo about particular torrent. | 85 | -- | Metainfo about particular torrent. |
85 | data Torrent = Torrent { | 86 | data Torrent = Torrent { |
86 | tInfoHash :: InfoHash | 87 | tInfoHash :: !InfoHash |
87 | -- ^ SHA1 hash of the 'TorrentInfo' of the 'Torrent'. | 88 | -- ^ SHA1 hash of the 'TorrentInfo' of the 'Torrent'. |
88 | 89 | ||
89 | , tAnnounce :: URI | 90 | , tAnnounce :: !URI |
90 | -- ^ The URL of the tracker. | 91 | -- ^ The URL of the tracker. |
91 | 92 | ||
92 | -- NOTE: out of lexicographic order! | 93 | -- NOTE: out of lexicographic order! |
93 | , tInfo :: ContentInfo | 94 | , tInfo :: !ContentInfo |
94 | -- ^ Info about each content file. | 95 | -- ^ Info about each content file. |
95 | 96 | ||
96 | , tAnnounceList :: Maybe [[URI]] | 97 | , tAnnounceList :: Maybe [[URI]] |
@@ -112,9 +113,9 @@ data Torrent = Torrent { | |||
112 | -- the info dictionary in the .torrent metafile. | 113 | -- the info dictionary in the .torrent metafile. |
113 | 114 | ||
114 | , tPublisher :: Maybe URI | 115 | , tPublisher :: Maybe URI |
115 | -- ^ Containing the RSA public key of the publisher of the torrent. | 116 | -- ^ Containing the RSA public key of the publisher of the |
116 | -- Private counterpart of this key that has the authority to allow | 117 | -- torrent. Private counterpart of this key that has the |
117 | -- new peers onto the swarm. | 118 | -- authority to allow new peers onto the swarm. |
118 | 119 | ||
119 | , tPublisherURL :: Maybe URI | 120 | , tPublisherURL :: Maybe URI |
120 | , tSignature :: Maybe ByteString | 121 | , tSignature :: Maybe ByteString |
@@ -144,22 +145,22 @@ simpleTorrent announce info = torrent announce info | |||
144 | -- | Info part of the .torrent file contain info about each content file. | 145 | -- | Info part of the .torrent file contain info about each content file. |
145 | data ContentInfo = | 146 | data ContentInfo = |
146 | SingleFile { | 147 | SingleFile { |
147 | ciLength :: Integer | 148 | ciLength :: !Integer |
148 | -- ^ Length of the file in bytes. | 149 | -- ^ Length of the file in bytes. |
149 | 150 | ||
150 | , ciMD5sum :: Maybe ByteString | 151 | , ciMD5sum :: Maybe ByteString |
151 | -- ^ 32 character long MD5 sum of the file. | 152 | -- ^ 32 character long MD5 sum of the file. |
152 | -- Used by third-party tools, not by bittorrent protocol itself. | 153 | -- Used by third-party tools, not by bittorrent protocol itself. |
153 | 154 | ||
154 | , ciName :: ByteString | 155 | , ciName :: !ByteString |
155 | -- ^ Suggested name of the file single file. | 156 | -- ^ Suggested name of the file single file. |
156 | 157 | ||
157 | 158 | ||
158 | 159 | ||
159 | , ciPieceLength :: Int | 160 | , ciPieceLength :: !Int |
160 | -- ^ Number of bytes in each piece. | 161 | -- ^ Number of bytes in each piece. |
161 | 162 | ||
162 | , ciPieces :: ByteString | 163 | , ciPieces :: !ByteString |
163 | -- ^ Concatenation of all 20-byte SHA1 hash values. | 164 | -- ^ Concatenation of all 20-byte SHA1 hash values. |
164 | 165 | ||
165 | , ciPrivate :: Maybe Bool | 166 | , ciPrivate :: Maybe Bool |
@@ -171,28 +172,28 @@ data ContentInfo = | |||
171 | } | 172 | } |
172 | 173 | ||
173 | | MultiFile { | 174 | | MultiFile { |
174 | ciFiles :: [FileInfo] | 175 | ciFiles :: ![FileInfo] |
175 | -- ^ List of the all files that torrent contains. | 176 | -- ^ List of the all files that torrent contains. |
176 | 177 | ||
177 | , ciName :: ByteString | 178 | , ciName :: !ByteString |
178 | -- | The file path of the directory in which to store all the files. | 179 | -- | The file path of the directory in which to store all the files. |
179 | 180 | ||
180 | , ciPieceLength :: Int | 181 | , ciPieceLength :: !Int |
181 | , ciPieces :: ByteString | 182 | , ciPieces :: !ByteString |
182 | , ciPrivate :: Maybe Bool | 183 | , ciPrivate :: Maybe Bool |
183 | } deriving (Show, Read, Eq) | 184 | } deriving (Show, Read, Eq) |
184 | 185 | ||
185 | 186 | ||
186 | -- | Contain info about one single file. | 187 | -- | Contain info about one single file. |
187 | data FileInfo = FileInfo { | 188 | data FileInfo = FileInfo { |
188 | fiLength :: Integer | 189 | fiLength :: !Integer |
189 | -- ^ Length of the file in bytes. | 190 | -- ^ Length of the file in bytes. |
190 | 191 | ||
191 | , fiMD5sum :: Maybe ByteString | 192 | , fiMD5sum :: Maybe ByteString |
192 | -- ^ 32 character long MD5 sum of the file. | 193 | -- ^ 32 character long MD5 sum of the file. |
193 | -- Used by third-party tools, not by bittorrent protocol itself. | 194 | -- Used by third-party tools, not by bittorrent protocol itself. |
194 | 195 | ||
195 | , fiPath :: [ByteString] | 196 | , fiPath :: ![ByteString] |
196 | -- ^ One or more string elements that together represent the | 197 | -- ^ One or more string elements that together represent the |
197 | -- path and filename. Each element in the list corresponds to | 198 | -- path and filename. Each element in the list corresponds to |
198 | -- either a directory name or (in the case of the last | 199 | -- either a directory name or (in the case of the last |
@@ -323,16 +324,18 @@ blockCount :: Int -- ^ Block size. | |||
323 | -> Int -- ^ Number of blocks. | 324 | -> Int -- ^ Number of blocks. |
324 | blockCount blkSize ci = contentLength ci `sizeInBase` blkSize | 325 | blockCount blkSize ci = contentLength ci `sizeInBase` blkSize |
325 | 326 | ||
326 | -- | File layout specifies the order and the size of each file in the storage. | 327 | -- | File layout specifies the order and the size of each file in the |
327 | -- Note that order of files is highly important since we coalesce all | 328 | -- storage. Note that order of files is highly important since we |
328 | -- the files in the given order to get the linear block address space. | 329 | -- coalesce all the files in the given order to get the linear block |
330 | -- address space. | ||
329 | -- | 331 | -- |
330 | type Layout = [(FilePath, Int)] | 332 | type Layout = [(FilePath, Int)] |
331 | 333 | ||
332 | -- | Extract files layout from torrent info with the given root path. | 334 | -- | Extract files layout from torrent info with the given root path. |
333 | contentLayout :: FilePath -- ^ Root path for the all torrent files. | 335 | contentLayout :: FilePath -- ^ Root path for the all torrent files. |
334 | -> ContentInfo -- ^ Torrent content information. | 336 | -> ContentInfo -- ^ Torrent content information. |
335 | -> Layout -- ^ The all file paths prefixed with the given root. | 337 | -> Layout -- ^ The all file paths prefixed with the |
338 | -- given root. | ||
336 | contentLayout rootPath = filesLayout | 339 | contentLayout rootPath = filesLayout |
337 | where | 340 | where |
338 | filesLayout (SingleFile { ciName = name, ciLength = len }) | 341 | filesLayout (SingleFile { ciName = name, ciLength = len }) |
@@ -356,7 +359,11 @@ isMultiFile _ = False | |||
356 | 359 | ||
357 | -- | Read and decode a .torrent file. | 360 | -- | Read and decode a .torrent file. |
358 | fromFile :: FilePath -> IO Torrent | 361 | fromFile :: FilePath -> IO Torrent |
359 | fromFile = B.readFile >=> either (throwIO . userError) return . decoded | 362 | fromFile path = do |
363 | contents <- B.readFile path | ||
364 | case decoded contents of | ||
365 | Right !t -> return t | ||
366 | Left msg -> throwIO $ userError $ msg ++ " while reading torrent" | ||
360 | 367 | ||
361 | {----------------------------------------------------------------------- | 368 | {----------------------------------------------------------------------- |
362 | Info hash | 369 | Info hash |
diff --git a/src/Network/BitTorrent/Exchange/Protocol.hs b/src/Network/BitTorrent/Exchange/Protocol.hs index 6b97d8d1..d2d3da6c 100644 --- a/src/Network/BitTorrent/Exchange/Protocol.hs +++ b/src/Network/BitTorrent/Exchange/Protocol.hs | |||
@@ -181,7 +181,7 @@ handshake sock hs = do | |||
181 | 181 | ||
182 | case checkIH (S.decode resp) of | 182 | case checkIH (S.decode resp) of |
183 | Right hs' -> return hs' | 183 | Right hs' -> return hs' |
184 | Left msg -> throw $ userError msg | 184 | Left msg -> throwIO $ userError $ msg ++ " in handshake body." |
185 | where | 185 | where |
186 | checkIH (Right hs') | 186 | checkIH (Right hs') |
187 | | hsInfoHash hs /= hsInfoHash hs' | 187 | | hsInfoHash hs /= hsInfoHash hs' |
@@ -234,10 +234,10 @@ ppBlockIx BlockIx {..} = | |||
234 | 234 | ||
235 | data Block = Block { | 235 | data Block = Block { |
236 | -- | Zero-based piece index. | 236 | -- | Zero-based piece index. |
237 | blkPiece :: !PieceLIx | 237 | blkPiece :: {-# UNPACK #-} !PieceLIx |
238 | 238 | ||
239 | -- | Zero-based byte offset within the piece. | 239 | -- | Zero-based byte offset within the piece. |
240 | , blkOffset :: !Int | 240 | , blkOffset :: {-# UNPACK #-} !Int |
241 | 241 | ||
242 | -- | Payload. | 242 | -- | Payload. |
243 | , blkData :: !ByteString | 243 | , blkData :: !ByteString |
diff --git a/src/Network/BitTorrent/Tracker.hs b/src/Network/BitTorrent/Tracker.hs index cb776431..e1f9ff76 100644 --- a/src/Network/BitTorrent/Tracker.hs +++ b/src/Network/BitTorrent/Tracker.hs | |||
@@ -172,6 +172,7 @@ withTracker initProgress conn action = bracket start end (action . fst) | |||
172 | where | 172 | where |
173 | start = do | 173 | start = do |
174 | resp <- askTracker (startedReq conn initProgress) | 174 | resp <- askTracker (startedReq conn initProgress) |
175 | print resp | ||
175 | se <- newSession initProgress (respInterval resp) (respPeers resp) | 176 | se <- newSession initProgress (respInterval resp) (respPeers resp) |
176 | tid <- forkIO (syncSession se) | 177 | tid <- forkIO (syncSession se) |
177 | return (se, tid) | 178 | return (se, tid) |
@@ -179,10 +180,13 @@ withTracker initProgress conn action = bracket start end (action . fst) | |||
179 | syncSession se @ TSession {..} = forever $ do | 180 | syncSession se @ TSession {..} = forever $ do |
180 | waitInterval se | 181 | waitInterval se |
181 | pr <- getProgress se | 182 | pr <- getProgress se |
183 | print "tracker req" | ||
182 | resp <- tryJust isIOException $ do | 184 | resp <- tryJust isIOException $ do |
183 | askTracker (regularReq defaultNumWant conn pr) | 185 | askTracker (regularReq defaultNumWant conn pr) |
186 | print "tracker resp" | ||
184 | case resp of | 187 | case resp of |
185 | Right (OK {..}) -> do | 188 | Right (ok @ OK {..}) -> do |
189 | print ok | ||
186 | writeIORef seInterval respInterval | 190 | writeIORef seInterval respInterval |
187 | writeList2Chan sePeers respPeers | 191 | writeList2Chan sePeers respPeers |
188 | _ -> return () | 192 | _ -> return () |
diff --git a/src/Network/BitTorrent/Tracker/Protocol.hs b/src/Network/BitTorrent/Tracker/Protocol.hs index c94a2dfc..13127e7c 100644 --- a/src/Network/BitTorrent/Tracker/Protocol.hs +++ b/src/Network/BitTorrent/Tracker/Protocol.hs | |||
@@ -67,28 +67,28 @@ data Event = Started | |||
67 | -- to keep track lists of active peer for a particular torrent. | 67 | -- to keep track lists of active peer for a particular torrent. |
68 | -- | 68 | -- |
69 | data TRequest = TRequest { -- TODO peer here -- TODO detach announce | 69 | data TRequest = TRequest { -- TODO peer here -- TODO detach announce |
70 | reqAnnounce :: URI | 70 | reqAnnounce :: !URI |
71 | -- ^ Announce url of the torrent usually obtained from 'Torrent'. | 71 | -- ^ Announce url of the torrent usually obtained from 'Torrent'. |
72 | 72 | ||
73 | , reqInfoHash :: InfoHash | 73 | , reqInfoHash :: !InfoHash |
74 | -- ^ Hash of info part of the torrent usually obtained from | 74 | -- ^ Hash of info part of the torrent usually obtained from |
75 | -- 'Torrent'. | 75 | -- 'Torrent'. |
76 | 76 | ||
77 | , reqPeerID :: PeerID | 77 | , reqPeerID :: !PeerID |
78 | -- ^ ID of the peer doing request. | 78 | -- ^ ID of the peer doing request. |
79 | 79 | ||
80 | , reqPort :: PortNumber | 80 | , reqPort :: !PortNumber |
81 | -- ^ Port to listen to for connections from other | 81 | -- ^ Port to listen to for connections from other |
82 | -- peers. Normally, tracker should respond with this port when | 82 | -- peers. Normally, tracker should respond with this port when |
83 | -- some peer request the tracker with the same info hash. | 83 | -- some peer request the tracker with the same info hash. |
84 | 84 | ||
85 | , reqUploaded :: Integer | 85 | , reqUploaded :: !Integer |
86 | -- ^ Number of bytes that the peer has uploaded in the swarm. | 86 | -- ^ Number of bytes that the peer has uploaded in the swarm. |
87 | 87 | ||
88 | , reqDownloaded :: Integer | 88 | , reqDownloaded :: !Integer |
89 | -- ^ Number of bytes downloaded in the swarm by the peer. | 89 | -- ^ Number of bytes downloaded in the swarm by the peer. |
90 | 90 | ||
91 | , reqLeft :: Integer | 91 | , reqLeft :: !Integer |
92 | -- ^ Number of bytes needed in order to complete download. | 92 | -- ^ Number of bytes needed in order to complete download. |
93 | 93 | ||
94 | , reqIP :: Maybe HostAddress | 94 | , reqIP :: Maybe HostAddress |
@@ -111,25 +111,25 @@ data TRequest = TRequest { -- TODO peer here -- TODO detach announce | |||
111 | data TResponse = | 111 | data TResponse = |
112 | Failure Text -- ^ Failure reason in human readable form. | 112 | Failure Text -- ^ Failure reason in human readable form. |
113 | | OK { | 113 | | OK { |
114 | respWarning :: Maybe Text | 114 | respWarning :: Maybe Text |
115 | -- ^ Human readable warning. | 115 | -- ^ Human readable warning. |
116 | 116 | ||
117 | , respInterval :: Int | 117 | , respInterval :: !Int |
118 | -- ^ Recommended interval to wait between requests. | 118 | -- ^ Recommended interval to wait between requests. |
119 | 119 | ||
120 | , respMinInterval :: Maybe Int | 120 | , respMinInterval :: Maybe Int |
121 | -- ^ Minimal amount of time between requests. A peer /should/ | 121 | -- ^ Minimal amount of time between requests. A peer /should/ |
122 | -- make timeout with at least 'respMinInterval' value, | 122 | -- make timeout with at least 'respMinInterval' value, |
123 | -- otherwise tracker might not respond. If not specified the | 123 | -- otherwise tracker might not respond. If not specified the |
124 | -- same applies to 'respInterval'. | 124 | -- same applies to 'respInterval'. |
125 | 125 | ||
126 | , respComplete :: Maybe Int | 126 | , respComplete :: Maybe Int |
127 | -- ^ Number of peers completed the torrent. (seeders) | 127 | -- ^ Number of peers completed the torrent. (seeders) |
128 | 128 | ||
129 | , respIncomplete :: Maybe Int | 129 | , respIncomplete :: Maybe Int |
130 | -- ^ Number of peers downloading the torrent. (leechers) | 130 | -- ^ Number of peers downloading the torrent. (leechers) |
131 | 131 | ||
132 | , respPeers :: [PeerAddr] | 132 | , respPeers :: ![PeerAddr] |
133 | -- ^ Peers that must be contacted. | 133 | -- ^ Peers that must be contacted. |
134 | } deriving Show | 134 | } deriving Show |
135 | 135 | ||
@@ -227,11 +227,14 @@ askTracker req = do | |||
227 | 227 | ||
228 | rawResp <- simpleHTTP r | 228 | rawResp <- simpleHTTP r |
229 | respBody <- getResponseBody rawResp | 229 | respBody <- getResponseBody rawResp |
230 | print $ respBody | ||
230 | checkResult $ decoded respBody | 231 | checkResult $ decoded respBody |
231 | where | 232 | where |
232 | mkHTTPRequest :: URI -> Request ByteString | 233 | mkHTTPRequest :: URI -> Request ByteString |
233 | mkHTTPRequest uri = Request uri GET [] "" | 234 | mkHTTPRequest uri = Request uri GET [] "" |
234 | 235 | ||
235 | checkResult (Left err) = ioError (userError err) | 236 | checkResult (Left err) |
236 | checkResult (Right (Failure err)) = ioError (userError (show err)) | 237 | = ioError $ userError $ err ++ " in tracker response" |
238 | checkResult (Right (Failure err)) | ||
239 | = ioError $ userError $ show err ++ " in tracker response" | ||
237 | checkResult (Right resp) = return resp | 240 | checkResult (Right resp) = return resp |