All files codec.js

100% Statements 50/50
100% Branches 22/22
100% Functions 3/3
100% Lines 50/50

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114  1x 1x   1x             12x               10x 10x   10x   6x 6x   4x   2x 2x 2x 2x       2x 2x   2x   2x   16x 16x       10x   10x   5x   5x   5x 79498x   5x     10x           130x 130x 130x         130x     130x     130x 130x   130x 9x   121x   121x   5x   5x 79498x       116x 13699x     121x 121x 121x       121x 112x 6216x      
const
crypto    = require('crypto'),
encoding  = require('encoding')
 
module.exports = Object.freeze(
{
  GUID : '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
 
  signature: function(key)
  {
    // https://tools.ietf.org/html/rfc6455#page-6
    return crypto.createHash('sha1').update(key + this.GUID).digest('base64')
  },
 
  encode : function(data, masked)
  {
    let header
 
    const
    payload = Buffer.from(data),
    length  = payload.length
 
    if(length <= 125)
    {
      header    = new Buffer(2)
      header[1] = length
    }
    else if(length <= 65535)
    {
      header    = new Buffer(4)
      header[1] = 126
      header[2] = (length >> 8) & 255
      header[3] = (length     ) & 255
    }
    else
    {
      header    = new Buffer(10)
      header[1] = 127
 
      const unit = 256
 
      for(let i = 9, left = length; i > 1 && left > 0; i--)
      {
        header[i] = left % unit
        left /= unit
      }
    }
 
    header[0] = 129
 
    if(masked)
    {
      header[1] += 128
 
      const mask = crypto.randomBytes(4)
 
      for(let i = 0; i < payload.length; ++i)
        payload[i] = payload[i] ^ mask[i % 4]
 
      header = Buffer.concat([header, mask])
    }
 
    return Buffer.concat([header, payload])
  },
 
  decode : function * (buffer)
  {
    const
    pack    = buffer[1] & 127,
    hasMask = buffer[1] & 128,
    iniMask = pack == 126
              ? 4
              : ( pack == 127
                ? 10
                : 2 ),
    endMask = hasMask
              ? iniMask + 4
              : iniMask,
    length  = pack < 126
            ? pack
            : buffer.readUIntBE(2, iniMask - 2),
    end     = length + endMask,
    payload = buffer.slice(endMask, end)
 
    if(buffer.length < end)
      return
 
    let msg = ''
 
    if(hasMask)
    {
      const mask = buffer.slice(iniMask, endMask)
 
      for (let i = 0; i < payload.length; i++)
        msg += String.fromCharCode(payload[i] ^ mask[i % 4])
    }
    else
    {
      for (let i = 0; i < payload.length; i++)
        msg += String.fromCharCode(payload[i])
    }
 
    msg    = encoding.convert(msg, 'Latin_1').toString()
    buffer = buffer.slice(end)
    yield { msg, buffer }
 
    // if the data is larger then what is specified, it's likely that two
    // messages has merged.
    if(buffer.length)
      for(const out of this.decode(buffer))
        yield out
  }
})