/ lib / server / parse-websocket.js
parse-websocket.js
 1  import { bin2hex } from 'uint8-util'
 2  
 3  import common from '../common.js'
 4  
 5  export default function (socket, opts, params) {
 6    if (!opts) opts = {}
 7    params = JSON.parse(params) // may throw
 8  
 9    params.type = 'ws'
10    params.socket = socket
11    if (params.action === 'announce') {
12      params.action = common.ACTIONS.ANNOUNCE
13  
14      if (typeof params.info_hash !== 'string' || params.info_hash.length !== 20) {
15        throw new Error('invalid info_hash')
16      }
17      params.info_hash = bin2hex(params.info_hash)
18  
19      if (typeof params.peer_id !== 'string' || params.peer_id.length !== 20) {
20        throw new Error('invalid peer_id')
21      }
22      params.peer_id = bin2hex(params.peer_id)
23  
24      if (params.answer) {
25        if (typeof params.to_peer_id !== 'string' || params.to_peer_id.length !== 20) {
26          throw new Error('invalid `to_peer_id` (required with `answer`)')
27        }
28        params.to_peer_id = bin2hex(params.to_peer_id)
29      }
30  
31      params.left = Number(params.left)
32      if (Number.isNaN(params.left)) params.left = Infinity
33  
34      params.numwant = Math.min(
35        Number(params.offers && params.offers.length) || 0, // no default - explicit only
36        common.MAX_ANNOUNCE_PEERS
37      )
38      params.compact = -1 // return full peer objects (used for websocket responses)
39    } else if (params.action === 'scrape') {
40      params.action = common.ACTIONS.SCRAPE
41  
42      if (typeof params.info_hash === 'string') params.info_hash = [params.info_hash]
43      if (Array.isArray(params.info_hash)) {
44        params.info_hash = params.info_hash.map(binaryInfoHash => {
45          if (typeof binaryInfoHash !== 'string' || binaryInfoHash.length !== 20) {
46            throw new Error('invalid info_hash')
47          }
48          return bin2hex(binaryInfoHash)
49        })
50      }
51    } else {
52      throw new Error(`invalid action in WS request: ${params.action}`)
53    }
54  
55    // On first parse, save important data from `socket.upgradeReq` and delete it
56    // to reduce memory usage.
57    if (socket.upgradeReq) {
58      if (opts.trustProxy) {
59        if (socket.upgradeReq.headers['x-forwarded-for']) {
60          const [realIp] = socket.upgradeReq.headers['x-forwarded-for'].split(',')
61          socket.ip = realIp.trim()
62        } else {
63          socket.ip = socket.upgradeReq.connection.remoteAddress
64        }
65      } else {
66        socket.ip = socket.upgradeReq.connection.remoteAddress.replace(common.REMOVE_IPV4_MAPPED_IPV6_RE, '') // force ipv4
67      }
68  
69      socket.port = socket.upgradeReq.connection.remotePort
70      if (socket.port) {
71        socket.addr = `${common.IPV6_RE.test(socket.ip) ? `[${socket.ip}]` : socket.ip}:${socket.port}`
72      }
73  
74      socket.headers = socket.upgradeReq.headers
75  
76      // Delete `socket.upgradeReq` when it is no longer needed to reduce memory usage
77      socket.upgradeReq = null
78    }
79  
80    params.ip = socket.ip
81    params.port = socket.port
82    params.addr = socket.addr
83    params.headers = socket.headers
84  
85    return params
86  }