magnet-protocol.js
1 import { Readable } from 'stream' 2 3 const INFO_HASH_MATCH = /^urn:btih:([a-f0-9]{40})$/ig 4 const PUBLIC_KEY_MATCH = /^urn:btpk:([a-f0-9]{64})$/ig 5 6 export default async function createHandler () { 7 return function magnetHandler (req, sendResponse) { 8 try { 9 const parsed = new URL(req.url) 10 11 const xt = parsed.searchParams.get('xt') 12 const xs = parsed.searchParams.get('xs') 13 14 if (xs) { 15 const match = PUBLIC_KEY_MATCH.exec(xs) 16 if (!match) { 17 return sendError('Magnet has no bittorrent infohash') 18 } 19 const publicKey = match[1] 20 const final = `bittorrent://${publicKey}` 21 sendFinal(final) 22 } else if (xt) { 23 const match = INFO_HASH_MATCH.exec(xt) 24 if (!match) { 25 return sendError('Magnet has no bittorrent infohash') 26 } 27 const infohash = match[1] 28 const final = `bittorrent://${infohash}/` 29 sendFinal(final) 30 } else { 31 return sendError('Magnet link has no `xt` or `xs` parameter') 32 } 33 } catch (e) { 34 sendError(e.stack) 35 } 36 37 function sendFinal (Location) { 38 sendResponse({ 39 statusCode: 308, 40 headers: { 41 Location 42 }, 43 data: intoStream('') 44 }) 45 } 46 47 function sendError (message) { 48 sendResponse({ 49 statusCode: 400, 50 headers: { 51 'content-type': 'text/html' 52 }, 53 data: intoStream(message) 54 }) 55 } 56 } 57 } 58 59 function intoStream (data) { 60 return new Readable({ 61 read () { 62 this.push(data) 63 this.push(null) 64 } 65 }) 66 }