diff options
Diffstat (limited to 'src/Network/BitTorrent')
-rw-r--r-- | src/Network/BitTorrent/Tracker/Wai.hs | 148 |
1 files changed, 138 insertions, 10 deletions
diff --git a/src/Network/BitTorrent/Tracker/Wai.hs b/src/Network/BitTorrent/Tracker/Wai.hs index 29e0e952..df56e378 100644 --- a/src/Network/BitTorrent/Tracker/Wai.hs +++ b/src/Network/BitTorrent/Tracker/Wai.hs | |||
@@ -12,19 +12,30 @@ module Network.BitTorrent.Tracker.Wai | |||
12 | ( -- * Configuration | 12 | ( -- * Configuration |
13 | TrackerSettings (..) | 13 | TrackerSettings (..) |
14 | 14 | ||
15 | -- * Tracker | ||
16 | , Tracker | ||
17 | , newTracker | ||
18 | , closeTracker | ||
19 | , withTracker | ||
20 | |||
15 | -- * Application | 21 | -- * Application |
16 | , tracker | 22 | , tracker |
17 | ) where | 23 | ) where |
18 | 24 | ||
19 | import Control.Applicative | 25 | import Control.Applicative |
26 | import Control.Concurrent.STM | ||
27 | import Control.Exception | ||
28 | import Control.Monad.Trans | ||
20 | import Control.Monad.Trans.Resource | 29 | import Control.Monad.Trans.Resource |
21 | import Data.BEncode as BE | 30 | import Data.BEncode as BE |
22 | import Data.ByteString | ||
23 | import Data.Default | 31 | import Data.Default |
32 | import Data.HashMap.Strict as HM | ||
24 | import Data.List as L | 33 | import Data.List as L |
34 | import Data.Maybe | ||
25 | import Network.HTTP.Types | 35 | import Network.HTTP.Types |
26 | import Network.Wai | 36 | import Network.Wai |
27 | 37 | ||
38 | import Data.Torrent.InfoHash | ||
28 | import Data.Torrent.Progress | 39 | import Data.Torrent.Progress |
29 | import Network.BitTorrent.Tracker.Message | 40 | import Network.BitTorrent.Tracker.Message |
30 | 41 | ||
@@ -79,17 +90,134 @@ instance Default TrackerSettings where | |||
79 | , noPeerId = False | 90 | , noPeerId = False |
80 | } | 91 | } |
81 | 92 | ||
93 | {----------------------------------------------------------------------- | ||
94 | -- Swarm | ||
95 | -----------------------------------------------------------------------} | ||
96 | |||
97 | type PeerSet = [()] | ||
98 | |||
99 | data Swarm = Swarm | ||
100 | { leechers :: !PeerSet | ||
101 | , seeders :: !PeerSet | ||
102 | , downloaded :: {-# UNPACK #-} !Int | ||
103 | } | ||
82 | 104 | ||
105 | instance Default Swarm where | ||
106 | def = Swarm | ||
107 | { leechers = [] | ||
108 | , seeders = [] | ||
109 | , downloaded = 0 | ||
110 | } | ||
111 | {- | ||
112 | started :: PeerInfo -> Swarm -> Swarm | ||
113 | started info Swarm {..} = Swarm | ||
114 | { leechers = insert info leechers | ||
115 | , seeders = delete info seeders | ||
116 | , downloaded = downloaded | ||
117 | } | ||
118 | |||
119 | regular :: PeerInfo -> Swarm -> Swarm | ||
120 | regular info Swarm {..} = undefined | ||
121 | |||
122 | stopped :: PeerInfo -> Swarm -> Swarm | ||
123 | stopped info Swarm {..} = Swarm | ||
124 | { leechers = delete info leechers | ||
125 | , seeders = delete info seeders | ||
126 | , downloaded = downloaded | ||
127 | } | ||
128 | |||
129 | completed :: PeerInfo -> Swarm -> Swarm | ||
130 | completed info Swarm {..} = Swarm | ||
131 | { leechers = delete info leechers | ||
132 | , seeders = insert info seeders | ||
133 | , downloaded = succ downloaded | ||
134 | } | ||
135 | |||
136 | event :: Maybe Event -> Swarm -> Swarm | ||
137 | event = undefined | ||
138 | -} | ||
139 | --peerList :: TrackerSettings -> Swarm -> PeerList IP | ||
140 | peerList TrackerSettings {..} Swarm {..} = undefined --envelope peers | ||
141 | where | ||
142 | envelope = if compactPeerList then CompactPeerList else PeerList | ||
143 | peers = [] | ||
144 | |||
145 | announceInfo :: TrackerSettings -> Swarm -> AnnounceInfo | ||
146 | announceInfo settings @ TrackerSettings {..} swarm @ Swarm {..} = AnnounceInfo | ||
147 | { respComplete = Just (L.length seeders) | ||
148 | , respIncomplete = Just (L.length leechers) | ||
149 | , respInterval = reannounceInterval | ||
150 | , respMinInterval = reannounceMinInterval | ||
151 | , respPeers = undefined -- peerList settings swarm | ||
152 | , respWarning = Nothing | ||
153 | } | ||
154 | |||
155 | scrapeEntry :: Swarm -> ScrapeEntry | ||
156 | scrapeEntry Swarm {..} = ScrapeEntry | ||
157 | { siComplete = L.length seeders | ||
158 | , siDownloaded = downloaded | ||
159 | , siIncomplete = L.length leechers | ||
160 | , siName = Nothing | ||
161 | } | ||
83 | 162 | ||
84 | {----------------------------------------------------------------------- | 163 | {----------------------------------------------------------------------- |
85 | -- Handlers | 164 | -- Tracker state |
86 | -----------------------------------------------------------------------} | 165 | -----------------------------------------------------------------------} |
87 | 166 | ||
88 | getAnnounceR :: TrackerSettings -> AnnounceRequest -> ResourceT IO AnnounceInfo | 167 | type Table = HashMap InfoHash Swarm |
89 | getAnnounceR = undefined | 168 | |
169 | withSwarm :: TVar Table -> InfoHash -> (Maybe Swarm -> STM (a, Swarm)) -> STM a | ||
170 | withSwarm tableRef infohash action = do | ||
171 | table <- readTVar tableRef | ||
172 | (res, swarm') <- action (HM.lookup infohash table) | ||
173 | writeTVar tableRef (HM.insert infohash swarm' table) | ||
174 | return res | ||
175 | |||
176 | scrapeInfo :: ScrapeQuery -> Table -> [ScrapeEntry] | ||
177 | scrapeInfo query table = do | ||
178 | infohash <- query | ||
179 | swarm <- maybeToList $ HM.lookup infohash table | ||
180 | return $ scrapeEntry swarm | ||
181 | |||
182 | data TrackerState = TrackerState | ||
183 | { swarms :: !(TVar Table) | ||
184 | } | ||
185 | |||
186 | newState :: IO TrackerState | ||
187 | newState = TrackerState <$> newTVarIO HM.empty | ||
188 | |||
189 | data Tracker = Tracker | ||
190 | { options :: !TrackerSettings | ||
191 | , state :: !TrackerState | ||
192 | } | ||
193 | |||
194 | newTracker :: TrackerSettings -> IO Tracker | ||
195 | newTracker opts = Tracker opts <$> newState | ||
196 | |||
197 | closeTracker :: Tracker -> IO () | ||
198 | closeTracker _ = return () | ||
199 | |||
200 | withTracker :: TrackerSettings -> (Tracker -> IO a) -> IO a | ||
201 | withTracker opts = bracket (newTracker opts) closeTracker | ||
202 | |||
203 | {----------------------------------------------------------------------- | ||
204 | -- Handlers | ||
205 | -----------------------------------------------------------------------} | ||
90 | 206 | ||
91 | getScrapeR :: TrackerSettings -> ScrapeQuery -> ResourceT IO ScrapeInfo | 207 | getAnnounceR :: Tracker -> AnnounceRequest -> ResourceT IO AnnounceInfo |
92 | getScrapeR = undefined | 208 | getAnnounceR Tracker {..} AnnounceRequest {..} = do |
209 | return undefined | ||
210 | {- | ||
211 | atomically $ do | ||
212 | withSwarm (swarms state) (reqInfoHash announceQuery) $ \ mswarm -> | ||
213 | case mswarm of | ||
214 | Nothing -> return undefined | ||
215 | Just s -> return undefined | ||
216 | -} | ||
217 | getScrapeR :: Tracker -> ScrapeQuery -> ResourceT IO ScrapeInfo | ||
218 | getScrapeR Tracker {..} query = do | ||
219 | table <- liftIO $ readTVarIO (swarms state) | ||
220 | return $ undefined $ scrapeInfo query table | ||
93 | 221 | ||
94 | {----------------------------------------------------------------------- | 222 | {----------------------------------------------------------------------- |
95 | -- Routing | 223 | -- Routing |
@@ -106,19 +234,19 @@ scrapeResponse info = responseLBS ok200 headers $ BE.encode info | |||
106 | headers = [(hContentType, scrapeType)] | 234 | headers = [(hContentType, scrapeType)] |
107 | 235 | ||
108 | -- content-type: "text/plain"! | 236 | -- content-type: "text/plain"! |
109 | tracker :: TrackerSettings -> Application | 237 | tracker :: Tracker -> Application |
110 | tracker settings @ TrackerSettings {..} Request {..} | 238 | tracker t @ (Tracker TrackerSettings {..} _) Request {..} |
111 | | requestMethod /= methodGet | 239 | | requestMethod /= methodGet |
112 | = return $ responseLBS methodNotAllowed405 [] "" | 240 | = return $ responseLBS methodNotAllowed405 [] "" |
113 | 241 | ||
114 | | rawPathInfo == announcePath = do | 242 | | rawPathInfo == announcePath = do |
115 | case parseAnnounceRequest $ queryToSimpleQuery queryString of | 243 | case parseAnnounceRequest $ queryToSimpleQuery queryString of |
116 | Right query -> announceResponse <$> getAnnounceR settings query | 244 | Right query -> announceResponse <$> getAnnounceR t query |
117 | Left msg -> return $ responseLBS (parseFailureStatus msg) [] "" | 245 | Left msg -> return $ responseLBS (parseFailureStatus msg) [] "" |
118 | 246 | ||
119 | | rawPathInfo == scrapePath = do | 247 | | rawPathInfo == scrapePath = do |
120 | case Right $ parseScrapeQuery $ queryToSimpleQuery queryString of -- TODO | 248 | case Right $ parseScrapeQuery $ queryToSimpleQuery queryString of -- TODO |
121 | Right query -> scrapeResponse <$> getScrapeR settings query | 249 | Right query -> scrapeResponse <$> getScrapeR t query |
122 | Left msg -> return $ responseLBS badRequest400 [] "" | 250 | Left msg -> return $ responseLBS badRequest400 [] "" |
123 | 251 | ||
124 | | otherwise = undefined --badPath | 252 | | otherwise = undefined --badPath |