wildduck/imap-core/lib/length-limiter.js
2020-01-11 15:04:26 +02:00

77 lines
2.4 KiB
JavaScript

'use strict';
const streams = require('stream');
const Transform = streams.Transform;
// make sure that a stream piped to this transform stream
// always emits a fixed amounts of bytes. Either by truncating
// input or emitting padding characters
class LengthLimiter extends Transform {
constructor(expectedLength, padding, startFrom, byteCounter) {
super();
this.expectedLength = expectedLength;
this.padding = padding || ' ';
this.byteCounter = byteCounter || 0;
this.startFrom = startFrom || 0;
this.finished = false;
Transform.call(this);
}
_transform(chunk, encoding, done) {
if (encoding !== 'buffer') {
chunk = Buffer.from(chunk, encoding);
}
if (!chunk || !chunk.length || this.finished) {
return done();
}
// not yet at allowed position
if (chunk.length + this.byteCounter <= this.startFrom) {
// ignore
this.byteCounter += chunk.length;
return done();
}
// start emitting at middle of chunk
if (this.byteCounter < this.startFrom) {
// split the chunk and ignore the first part
chunk = chunk.slice(this.startFrom - this.byteCounter);
this.byteCounter += this.startFrom - this.byteCounter;
}
// can emit full chunk
if (chunk.length + this.byteCounter <= this.expectedLength) {
this.byteCounter += chunk.length;
this.push(chunk);
if (this.byteCounter >= this.expectedLength) {
this.finished = true;
this.emit('done', false);
}
return setImmediate(done);
}
// stop emitting in the middle of chunk
let buf = chunk.slice(0, this.expectedLength - this.byteCounter);
let remaining = chunk.slice(this.expectedLength - this.byteCounter);
this.push(buf);
this.finished = true;
this.emit('done', remaining);
return setImmediate(done);
}
_flush(done) {
if (!this.finished) {
// add padding if incoming stream stopped too early
if (this.expectedLength > this.byteCounter) {
let buf = Buffer.from(this.padding.repeat(this.expectedLength - this.byteCounter));
this.push(buf);
}
this.finished = true;
}
done();
}
}
module.exports = LengthLimiter;