frame-stream
Advanced tools
Comparing version 0.2.0 to 0.3.0
@@ -9,3 +9,3 @@ 'use strict' | ||
gulp.task('test', function() { | ||
return gulp.src('test/test.js') | ||
return gulp.src('test/*.js') | ||
.pipe(mocha({ | ||
@@ -23,2 +23,2 @@ ui: 'tdd', | ||
.pipe(eslint.failOnError()) | ||
}) | ||
}) |
@@ -11,8 +11,14 @@ 'use strict' | ||
var FrameStream = exports.FrameStream = function(opts) { | ||
this.opts = util._extend({ lengthSize: 4, maxSize: 0 }, opts) | ||
this.opts = util._extend({ | ||
lengthSize: 4, | ||
maxSize: 0, | ||
unbuffered: false | ||
}, opts) | ||
this.getLength = this.opts.getLength || createGetLengthMethod(this.opts.lengthSize) | ||
this.buffer = null | ||
this.frameLength = 0 | ||
this.framePos = 0 | ||
Transform.call(this) | ||
Transform.call(this, opts) | ||
} | ||
@@ -22,37 +28,61 @@ | ||
FrameStream.prototype._transform = function transform(chunk, enc, cont) { | ||
if (this.buffer) { | ||
chunk = Buffer.concat([this.buffer, chunk]) | ||
this.buffer = null | ||
} | ||
FrameStream.prototype._transform = function(chunk, enc, cont) { | ||
while (chunk.length > 0) { | ||
var start = 0 | ||
if (chunk.length < this.opts.lengthSize) { | ||
this.buffer = chunk | ||
return cont() | ||
} | ||
if (this.framePos === 0) { | ||
if (this.buffer) { | ||
chunk = Buffer.concat([this.buffer, chunk]) | ||
this.buffer = null | ||
} | ||
var length = this.getLength(chunk) | ||
if (chunk.length < this.opts.lengthSize) { | ||
this.buffer = chunk | ||
return cont() | ||
} | ||
if (length < 0) { | ||
return cont(new Error('Message length is less than zero')) | ||
} | ||
this.frameLength = this.getLength(chunk) | ||
// prevent denial-of-service attacks | ||
if (this.opts.maxSize > 0 && length > this.opts.maxSize) { | ||
return cont(new Error('Message is larger than the allowed maximum of ' + this.opts.maxSize)) | ||
} | ||
if (this.frameLength < 0) { | ||
return cont(new Error('Message length is less than zero')) | ||
} | ||
var end = this.opts.lengthSize + length | ||
// prevent denial-of-service attacks | ||
if (this.opts.maxSize > 0 && this.frameLength > this.opts.maxSize) { | ||
return cont(new Error('Message is larger than the allowed maximum of ' + this.opts.maxSize)) | ||
} | ||
if (chunk.length < end) { | ||
this.buffer = chunk | ||
return cont() | ||
} | ||
start = this.opts.lengthSize | ||
} | ||
this.push(chunk.slice(this.opts.lengthSize, end)) | ||
var end = start + this.frameLength - this.framePos | ||
if (chunk.length > end) { | ||
transform.call(this, chunk.slice(end), enc, cont) | ||
} else { | ||
cont() | ||
if (this.opts.unbuffered) { | ||
end = Math.min(end, chunk.length) | ||
} else { | ||
if (chunk.length < end) { | ||
this.buffer = chunk | ||
return cont() | ||
} | ||
} | ||
var buf = chunk.slice(start, end) | ||
buf.framePos = this.framePos | ||
buf.frameLength = this.frameLength | ||
this.framePos += end - start | ||
buf.frameEnd = this.framePos === this.frameLength | ||
if (buf.frameEnd) { | ||
this.framePos = 0 | ||
} | ||
this.push(buf) | ||
if (chunk.length > end) { | ||
chunk = chunk.slice(end) | ||
} else { | ||
return cont() | ||
} | ||
} | ||
@@ -118,2 +148,2 @@ } | ||
} | ||
} | ||
} |
{ | ||
"name": "frame-stream", | ||
"version": "0.2.0", | ||
"description": "Stream of Length-prefix message framing for Node.js streams.", | ||
"version": "0.3.0", | ||
"description": "Length-prefixed message framing for Node.js streams.", | ||
"keywords": ["socket", "tcp", "framing", "streams"], | ||
@@ -6,0 +6,0 @@ "main": "lib/", |
# frame-stream | ||
Stream of Length-prefix message framing for Node.js streams. | ||
Length-prefixed message framing for Node.js streams. | ||
[![NPM][npm]](https://npmjs.org/package/frame-stream) | ||
[![Build Status](travis)](http://travis-ci.org/rkusa/frame-stream) | ||
[![NPM][npm]](https://npmjs.org/package/frame-stream) [![Build Status][travis]](http://travis-ci.org/rkusa/frame-stream) | ||
Some protocols, e.g. TCP, do not not guarantee to keep message boundaries. One common approach to distinguish such messages is *Length Prefixing*, which prepends each message with its length. | ||
Some protocols, e.g. TCP, do not not guarantee to keep message boundaries. One common approach to distinguish such messages is *Length Prefixing*, which prepends each message with its length. `frame-stream` accepts a stream with such length-prefixed messages and returns each frame on its own. | ||
@@ -35,3 +34,7 @@ ## Usage | ||
- **getLength** - The function used to read the prepended message size. This function defaults to `readInt8()`, `readInt16BE()` or `readInt32BE()` according to the `lengthSize`. | ||
- **maxSize** (default: 0)- The maximum allowed message size. This can be used to prevent denial-of-service attacks (`0` = turned off). | ||
- **maxSize** (default: 0) - The maximum allowed message size. This can be used to prevent denial-of-service attacks (`0` = turned off). | ||
- **unbuffered** (default: `false`) - Return parts of a message as they arrive, rather than buffering them up until the last part arrives. Useful when you know messages will be large. Each part will be a `Buffer` with the following extra properties: | ||
- **framePos** - The index in the message where this part starts. Parts will be returned in order. | ||
- **frameLength** - The total message size. | ||
- **frameEnd** - A boolean indicating whether this is the last part of the message. | ||
@@ -53,3 +56,3 @@ ### frame.prefix(opts) | ||
var prefixer = new frame.prefix() | ||
var prefixer = frame.prefix() | ||
@@ -74,3 +77,3 @@ prefixer.pipe(socket) | ||
[npm]: https://badge.fury.io/js/frame-stream.svg | ||
[travis]: https://secure.travis-ci.org/rkusa/frame-stream.svg | ||
[npm]: http://img.shields.io/npm/v/frame-stream.svg?style=flat | ||
[travis]: http://img.shields.io/travis/rkusa/frame-stream.svg?style=flat |
10303
169
77