qws
Version:
An HTML5 Web Sockets Server Module
234 lines (218 loc) • 5.29 kB
JavaScript
// Generated by CoffeeScript 1.6.3
var Frame, crypto, inflate, opcodes, opcodesMap, pack, unmask, unpack, zlib;
zlib = require('zlib');
crypto = require('crypto');
unmask = function(data, mask) {
var d1, i, mod, _i, _ref;
d1 = new Buffer(data.length);
for (i = _i = 0, _ref = data.length; _i < _ref; i = _i += 4) {
d1[i + 3] = data[i + 3] ^ mask[3];
d1[i + 2] = data[i + 2] ^ mask[2];
d1[i + 1] = data[i + 1] ^ mask[1];
d1[i] = data[i] ^ mask[0];
}
mod = data.length % 4;
if (mod > 0) {
d1[i] = data[i] ^ mask[0];
}
if (mod > 1) {
d1[i + 1] = data[i + 1] ^ mask[0];
}
if (mod > 2) {
d1[i + 2] = data[i + 2] ^ mask[0];
}
return d1;
};
inflate = function(frame, stream, cb) {
var b, len;
if (!frame.rsv1) {
return cb(null, frame);
}
stream.once('error', function(err) {
return cb(err);
});
stream.once('data', function(data) {
frame.data = data;
return cb(null, frame);
});
len = frame.data.length;
b = new Buffer(len + 4);
b[len] = 0x00;
b[len + 1] = 0x00;
b[len + 2] = 0xff;
b[len + 3] = 0xff;
frame.data.copy(b);
stream.write(b);
};
opcodes = ['continue', 'text', 'binary', 'non-control 3', 'non-control 4', 'non-control 5', 'non-control 6', ' non-control 7', 'close', 'ping', 'pong', 'control B', 'control C', 'control D', 'control E', ' control F'];
unpack = function(buf, frame) {
var b1, b2, idx;
if (frame) {
if (buf.length > frame.left) {
frame.data.push(buf.slice(0, frame.left));
buf = buf.slice(frame.left);
frame.left = 0;
} else {
frame.data.push(buf);
frame.left -= buf.length;
buf = null;
}
} else {
b1 = buf[0];
b2 = buf[1];
frame = {
fin: 0 < (b1 & 0x80),
rsv1: 0 < (b1 & 0x40),
rsv2: 0 < (b1 & 0x20),
rsv3: 0 < (b1 & 0x10),
opcode: opcodes[b1 & 0xf],
mask: 0 < (b2 & 128),
length: b2 & 127,
data: [],
left: 0,
done: false
};
idx = 2;
if (frame.length > 0) {
if (frame.length === 126) {
frame.length = buf.readUInt16BE(2, true);
idx = 4;
} else if (frame.length === 127) {
frame.length = buf.readUInt32BE(2, true) * 0xffffffff + buf.readUInt32BE(6, true);
idx = 10;
}
if (frame.mask) {
frame.maskKey = buf.slice(idx, idx += 4);
}
frame.data.push(buf.slice(idx, idx += frame.length));
frame.left = frame.length - frame.data[0].length;
}
if (buf.length > idx) {
buf = buf.slice(idx);
} else {
buf = null;
}
}
if (frame.left === 0) {
if (frame.length > 0) {
frame.data = Buffer.concat(frame.data);
if (frame.mask && frame.length > 0) {
frame.data = unmask(frame.data, frame.maskKey);
}
} else {
frame.data = null;
}
frame.done = true;
delete frame.left;
}
return [frame, buf];
};
pack = function(frame, data) {
var b1, b2, buf, dlen, lenFix, plen;
b1 = opcodesMap[frame.opcode];
if (frame.rsv3) {
b1 |= 0x10;
}
if (frame.rsv2) {
b1 |= 0x20;
}
if (frame.rsv1) {
b1 |= 0x40;
}
if (frame.fin) {
b1 |= 0x80;
}
dlen = data != null ? data.length : 0;
if (dlen > 0xffff) {
lenFix = 10;
plen = 127;
} else if (dlen >= 126) {
lenFix = 4;
plen = 126;
} else {
lenFix = 2;
plen = dlen;
}
if (dlen === 0) {
frame.mask = false;
}
b2 = plen;
if (frame.mask) {
b2 |= 0x80;
buf = new Buffer(lenFix + 4 + dlen);
frame.maskKey = crypto.randomBytes(4);
frame.maskKey.copy(buf, lenFix);
lenFix += 4;
data = unmask(data, frame.maskKey);
} else {
buf = new Buffer(lenFix + dlen);
}
buf[0] = b1;
buf[1] = b2;
if (dlen > 0xffff) {
buf.writeUInt32BE(Math.floor(dlen / 0xffffffff), 2, true);
buf.writeUInt32BE(dlen % 0xffffffff, 6, true);
} else if (dlen >= 126) {
buf.writeUInt16BE(dlen, 2, true);
}
if (dlen > 0) {
data.copy(buf, lenFix);
}
return buf;
};
opcodesMap = {
'continue': 0,
'text': 1,
'binary': 2,
'close': 8,
'ping': 9,
'pong': 10
};
Frame = (function() {
function Frame(prop) {
var k;
this.minDeflateLength = 32;
this.opcode = 'text';
this.fin = false;
this.rsv1 = false;
this.rsv2 = false;
this.rsv3 = false;
this.mask = false;
this.data = null;
for (k in prop) {
if (prop) {
this[k] = prop[k];
}
}
}
Frame.prototype.pack = function(deflate, cb) {
var data,
_this = this;
if (opcodesMap[this.opcode] == null) {
return cb(new Error("Opcode Not Found \"" + this.opcode + "\""));
}
if (this.data == null) {
data = null;
} else if (this.data instanceof Buffer) {
data = this.data;
} else {
data = new Buffer(typeof this.data === 'string' ? this.data : this.data.toString());
}
if (deflate && data.length < this.minDeflateLength) {
deflate = false;
}
if (deflate) {
this.rsv1 = true;
zlib.deflateRaw(data, function(err, data) {
cb(null, pack(_this, data));
});
} else {
this.rsv1 = false;
cb(null, pack(this, data));
}
};
return Frame;
})();
exports.unpack = unpack;
exports.inflate = inflate;
exports.Frame = Frame;