@@ -129,6 +129,80 @@ proc retrieveCid(
129129 if not stream.isNil:
130130 await stream.close ()
131131
132+ proc retrieveInfoHash (
133+ node: CodexNodeRef , infoHash: MultiHash , resp: HttpResponseRef
134+ ): Future [RestApiResponse ] {.async .} =
135+ # # Download torrent from the node in a streaming
136+ # # manner
137+ # #
138+ var stream: LPStream
139+
140+ var bytes = 0
141+ try :
142+ without stream =? (await node.retrieveTorrent (infoHash)), error:
143+ if error of BlockNotFoundError :
144+ resp.status = Http404
145+ return await resp.sendBody (" " )
146+ else :
147+ resp.status = Http500
148+ return await resp.sendBody (error.msg)
149+
150+ # It is ok to fetch again the manifest because it will hit the cache
151+ without infoHashCid =? Cid .init (CIDv1 , InfoHashV1Codec , infoHash).mapFailure, err:
152+ error " Unable to create CID for BitTorrent info hash" , err = err.msg
153+ resp.status = Http404
154+ return await resp.sendBody (err.msg)
155+
156+ without torrentManifest =? (await node.fetchTorrentManifest (infoHashCid)), err:
157+ error " Unable to fetch Torrent Manifest" , err = err.msg
158+ resp.status = Http404
159+ return await resp.sendBody (err.msg)
160+
161+ without codexManifest =? (
162+ await node.fetchManifest (torrentManifest.codexManifestCid)
163+ ), err:
164+ error " Unable to fetch Codex Manifest for torrent info hash" , err = err.msg
165+ resp.status = Http404
166+ return await resp.sendBody (err.msg)
167+
168+ if codexManifest.mimetype.isSome:
169+ resp.setHeader (" Content-Type" , codexManifest.mimetype.get ())
170+ else :
171+ resp.addHeader (" Content-Type" , " application/octet-stream" )
172+
173+ if codexManifest.filename.isSome:
174+ resp.setHeader (
175+ " Content-Disposition" ,
176+ " attachment; filename=\" " & codexManifest.filename.get () & " \" " ,
177+ )
178+ else :
179+ resp.setHeader (" Content-Disposition" , " attachment" )
180+
181+ await resp.prepareChunked ()
182+
183+ while not stream.atEof:
184+ var
185+ buff = newSeqUninitialized [byte ](int (NBytes 1024 * 16 ))
186+ len = await stream.readOnce (addr buff[0 ], buff.len)
187+
188+ buff.setLen (len)
189+ if buff.len <= 0 :
190+ break
191+
192+ bytes += buff.len
193+
194+ await resp.sendChunk (addr buff[0 ], buff.len)
195+ await resp.finish ()
196+ codex_api_downloads.inc ()
197+ except CatchableError as exc:
198+ warn " Error streaming blocks" , exc = exc.msg
199+ resp.status = Http500
200+ return await resp.sendBody (" " )
201+ finally :
202+ info " Sent bytes for torrent" , infoHash = $ infoHash, bytes
203+ if not stream.isNil:
204+ await stream.close ()
205+
132206proc buildCorsHeaders (
133207 httpMethod: string , allowedOrigin: Option [string ]
134208): seq [(string , string )] =
@@ -339,13 +413,20 @@ proc initDataApi(node: CodexNodeRef, repoStore: RepoStore, router: var RestRoute
339413 without infoHash =? infoHash.mapFailure, error:
340414 return RestApiResponse .error (Http400 , error.msg, headers = headers)
341415
416+ if infoHash.mcodec != Sha1HashCodec :
417+ return RestApiResponse .error (
418+ Http400 , " Only torrents version 1 are currently supported!" , headers = headers
419+ )
420+
342421 if corsOrigin =? allowedOrigin:
343422 resp.setCorsHeaders (" GET" , corsOrigin)
344423 resp.setHeader (" Access-Control-Headers" , " X-Requested-With" )
345424
346425 trace " torrent requested: " , multihash = $ infoHash
347426
348- return RestApiResponse .response (Http200 )
427+ await node.retrieveInfoHash (infoHash, resp = resp)
428+
429+ # return RestApiResponse.response(Http200)
349430
350431 router.api (MethodGet , " /api/codex/v1/data/{cid}/network/manifest" ) do (
351432 cid: Cid , resp: HttpResponseRef
0 commit comments