UNPKG

3.61 kBJavaScriptView Raw
1'use strict'
2
3var Transform = require('stream').Transform
4var util = require('util')
5
6exports = module.exports = function(opts) {
7 return new Decoder(opts)
8}
9exports.decode = exports
10
11var Decoder = exports.Decoder = function(opts) {
12 this.opts = util._extend({
13 lengthSize: 4,
14 maxSize: 0,
15 unbuffered: false
16 }, opts)
17
18 this.getLength = this.opts.getLength || createGetLengthMethod(this.opts.lengthSize)
19 this.buffer = null
20 this.frameLength = -1
21 this.framePos = 0
22
23 Transform.call(this, opts)
24}
25
26util.inherits(Decoder, Transform)
27
28Decoder.prototype._transform = function(chunk, enc, cont) {
29 while (chunk.length > 0) {
30 var start = this.opts.lengthSize
31
32 if (this.buffer) {
33 chunk = Buffer.concat([this.buffer, chunk])
34 this.buffer = null
35 }
36
37 if (this.frameLength < 0) {
38 if (chunk.length < this.opts.lengthSize) {
39 this.buffer = chunk
40 return cont()
41 }
42
43 this.frameLength = this.getLength(chunk)
44
45 if (this.frameLength < 0) {
46 return cont(new Error('Message length is less than zero'))
47 }
48
49 // prevent denial-of-service attacks
50 if (this.opts.maxSize > 0 && this.frameLength > this.opts.maxSize) {
51 return cont(new Error('Message is larger than the allowed maximum of ' + this.opts.maxSize))
52 }
53 } else if (this.opts.unbuffered) {
54 start = 0
55 }
56
57 var end = start + this.frameLength - this.framePos
58
59 if (this.opts.unbuffered) {
60 end = Math.min(end, chunk.length)
61 } else if (chunk.length < end) {
62 this.buffer = chunk
63 return cont()
64 }
65
66 var buf = chunk.slice(start, end)
67
68 buf.framePos = this.framePos
69 buf.frameLength = this.frameLength
70
71 this.framePos += end - start
72 buf.frameEnd = this.framePos === this.frameLength
73
74 if (buf.frameEnd) {
75 this.frameLength = -1
76 this.framePos = 0
77 }
78
79 this.push(buf)
80
81 if (chunk.length > end) {
82 chunk = chunk.slice(end)
83 } else {
84 return cont()
85 }
86 }
87 cont()
88}
89
90exports.encode = function(opts) {
91 return new Encoder(opts)
92}
93
94var Encoder = exports.Encoder = function(opts) {
95 this.opts = util._extend({ lengthSize: 4 }, opts)
96
97 this.setLength = this.opts.setLength || createSetLengthMethod(this.opts.lengthSize)
98
99 Transform.call(this, opts)
100}
101
102util.inherits(Encoder, Transform)
103
104Encoder.prototype._transform = function(message, enc, cont) {
105 var length = Buffer.alloc(this.opts.lengthSize)
106 this.setLength(length, message.length)
107 this._pushFrameData([length, message])
108 cont()
109}
110
111Encoder.prototype._pushFrameData = function(bufs) {
112 this.push(Buffer.concat(bufs))
113}
114
115// backwards compatibility
116exports.prefix = exports.encode
117exports.FrameStream = Decoder
118exports.LengthPrefixStream = Encoder
119
120function createGetLengthMethod(lengthSize) {
121 switch (lengthSize) {
122 case 1:
123 return function(buffer) {
124 return buffer.readUInt8(0)
125 }
126 case 2:
127 return function(buffer) {
128 return buffer.readUInt16BE(0)
129 }
130 case 4:
131 return function(buffer) {
132 return buffer.readUInt32BE(0)
133 }
134 default:
135 throw new Error('Invalid frame length size')
136 }
137}
138
139function createSetLengthMethod(lengthSize) {
140 switch (lengthSize) {
141 case 1:
142 return function(buffer, value) {
143 return buffer.writeUInt8(value, 0)
144 }
145 case 2:
146 return function(buffer, value) {
147 return buffer.writeUInt16BE(value, 0)
148 }
149 case 4:
150 return function(buffer, value) {
151 return buffer.writeUInt32BE(value, 0)
152 }
153 default:
154 throw new Error('Invalid frame length size')
155 }
156}