Skip to content

Commit

Permalink
Fix and improve stream implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
fbbdev committed Jul 15, 2023
1 parent c34d321 commit 439fdb2
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 103 deletions.
24 changes: 10 additions & 14 deletions lib/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ function Request(conn, id, role, keepalive) {
this.destroy();
});

this.on('close', this.errorStream._close.bind(this.errorStream));
this.on('close', this.errorStream.destroy.bind(this.errorStream, null));
}
util.inherits(Request, IOStream);

Expand Down Expand Up @@ -138,6 +138,8 @@ Request.prototype._createReqRes = function () {

this._req.complete = this._stdinComplete && this._dataComplete;

this.pause();

this.on('data', function (data) {
if (this._req) {
if (!this._req.push(data)) {
Expand All @@ -152,8 +154,6 @@ Request.prototype._createReqRes = function () {
}
});

this.pause();

if (this._role === fcgi.records.BeginRequest.roles.AUTHORIZER) {
this._res = new AuthorizerResponse(this._req);
} else {
Expand Down Expand Up @@ -238,12 +238,6 @@ Object.defineProperties(Request.prototype, {
get: function () {
return parseInt(this.params.REMOTE_PORT) || 0;
}
},

"destroyed": {
get: function () {
return !this._open;
}
}
});

Expand All @@ -255,11 +249,13 @@ Request.prototype.address = function () {
};
};

Request.prototype.destroy = function (err) {
if (this._open) {
this._close(err ? true : false);
this._conn.endRequest(this._id, err ? 1 : 0);
}
Request.prototype._destroy = function (err, callback) {
var self = this;
IOStream.prototype._destroy.call(this, err, function () {
self._conn.endRequest(self._id, err ? 1 : 0, undefined, function () {
callback(err);
});
});
}

Request.prototype.ref = function () {
Expand Down
63 changes: 35 additions & 28 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,18 +126,25 @@ function connectionListener(socket) {
var stream = new fcgi.FastCGIStream(socket),
requests = {};

function endRequest(id, status, reason) {
function endRequest(id, status, reason, callback) {
if (reason === undefined) {
reason = fcgi.records.EndRequest.protocolStatus.REQUEST_COMPLETE;
}

if (socket.writable && !socket.destroyed) {
var closeConn = (id in requests && !requests[id]._keepAlive);
delete requests[id];

stream.writeRecord(
id, new fcgi.records.EndRequest(status, reason));
id, new fcgi.records.EndRequest(status, reason),
function (err) {
if (closeConn && !socket.destroyed) {
socket.end();
}

if (id in requests && !requests[id]._keepAlive) {
socket.end();
}
if (callback)
callback(err);
});
}

delete requests[id];
Expand Down Expand Up @@ -177,10 +184,10 @@ function connectionListener(socket) {
});

socket.once('error', function (err) {
this.on('error', function () {})
socket.on('error', function () {});

if (!self.emit('clientError', err, this)) {
this.destroy(err);
if (!self.emit('clientError', err, socket)) {
socket.destroy();
}
});

Expand Down Expand Up @@ -216,46 +223,46 @@ function connectionListener(socket) {
switch (record.role) {
case fcgi.records.BeginRequest.roles.RESPONDER:
if (self.listeners('request').length < 1) {
endRequest(id, 1, fcgi.records.EndRequest.protocolStatus.UNKNOWN_ROLE);

if (!keepAlive) {
socket.end();
}
endRequest(id, 1, fcgi.records.EndRequest.protocolStatus.UNKNOWN_ROLE, function () {
if (!keepAlive && !socket.destroyed) {
socket.end();
}
});

return;
}
break;

case fcgi.records.BeginRequest.roles.AUTHORIZER:
if (self.listeners('authorize').length < 1) {
endRequest(id, 1, fcgi.records.EndRequest.protocolStatus.UNKNOWN_ROLE);

if (!keepAlive) {
socket.end();
}
endRequest(id, 1, fcgi.records.EndRequest.protocolStatus.UNKNOWN_ROLE, function () {
if (!keepAlive && !socket.destroyed) {
socket.end();
}
});

return;
}
break;

case fcgi.records.BeginRequest.roles.FILTER:
if (self.listeners('filter').length < 1) {
endRequest(id, 1, fcgi.records.EndRequest.protocolStatus.UNKNOWN_ROLE);

if (!keepAlive) {
socket.end();
}
endRequest(id, 1, fcgi.records.EndRequest.protocolStatus.UNKNOWN_ROLE, function () {
if (!keepAlive && !socket.destroyed) {
socket.end();
}
});

return;
}

break;
default:
endRequest(id, 1, fcgi.records.EndRequest.protocolStatus.UNKNOWN_ROLE);

if (!keepAlive) {
socket.end();
}
endRequest(id, 1, fcgi.records.EndRequest.protocolStatus.UNKNOWN_ROLE, function () {
if (!keepAlive && !socket.destroyed) {
socket.end();
}
});

return;
}
Expand Down
104 changes: 43 additions & 61 deletions lib/streams.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ function InputStream() {
util.inherits(InputStream, stream.Readable);

InputStream.prototype._data = function (chunk) {
if (this.closed || this.destroyed)
return;

if (this._canPush) {
this._canPush = this.push(chunk);
} else {
Expand All @@ -55,6 +58,12 @@ InputStream.prototype._read = function (size) {
while (this._buffer.length && (this._canPush = this.push(this._buffer.shift())));
};

InputStream.prototype._destroy = function (err, callback) {
this._buffer = [];
this._canPush = false;
callback(err);
}

/**
* function OutputStream(conn, type)
* Writable stream interface for FastCGI output streams
Expand All @@ -68,66 +77,42 @@ function OutputStream(conn, id, recordType) {
this._conn = conn;
this._id = id;

this._open = true;

this.on('finish', function () {
this._conn.stream.writeRecord(
this._id,
new this.recordType());
});
this._finalized = false;
}
util.inherits(OutputStream, stream.Writable);

OutputStream.prototype._close = function (hadError) {
if (this._open) {
this._open = false;
this.emit('close', hadError ? true : false);
}
};

OutputStream.prototype._write = function (chunk, encoding, callback) {
var chunks = [];

if (!Buffer.isBuffer(chunk)) {
chunk = new Buffer(chunk, encoding);
}

if (chunk.length <= 0) {
callback.call(this);
return;
}

var start = 0, end = 65535;
while (end < chunk.length) {
this._conn.stream.writeRecord(
this._id, new this.recordType(chunk.slice(start, end)));

start = end;
end += 65535;
var start = 0;
var self = this;

function writeSubChunk(err) {
if (err || start >= chunk.length) {
callback(err);
return;
}

self._conn.stream.writeRecord(
self._id,
new self.recordType(chunk.subarray(start, Math.min(start += 65535, chunk.length))),
writeSubChunk);
}

this._conn.stream.writeRecord(
this._id,
new this.recordType(chunk.slice(start)),
callback.bind(this));
writeSubChunk();
};

OutputStream.prototype.write = function () {
if (!this._open) {
this.emit('error', new Error("Output stream is not open"));
return;
}

return stream.Writable.prototype.write.apply(this, arguments);
OutputStream.prototype._final = function (callback) {
this._finalized = true;
this._conn.stream.writeRecord(this._id, new this.recordType(), callback);
}

OutputStream.prototype.end = function () {
if (!this._open) {
this.emit('error', new Error("Output stream is not open"));
return;
OutputStream.prototype._destroy = function (err, callback) {
if (!this._finalized) {
this._conn.stream.writeRecord(this._id, new this.recordType(), function () {
callback(err);
});
} else {
callback(err);
}

return stream.Writable.prototype.end.apply(this, arguments);
}

/**
Expand All @@ -138,28 +123,25 @@ OutputStream.prototype.end = function () {
function IOStream(conn, id, recordType) {
stream.Duplex.call(this);

this.recordType = recordType || fcgi.records.StdOut;

this._buffer = [];
this._canPush = false;

this.recordType = recordType || fcgi.records.StdOut;

this._conn = conn;
this._id = id;

this._open = true;

this.on('finish', function () {
this._conn.stream.writeRecord(
this._id,
new this.recordType());
});
this._finalized = false;
}
util.inherits(IOStream, stream.Duplex);

IOStream.prototype._data = InputStream.prototype._data;
IOStream.prototype._read = InputStream.prototype._read;

IOStream.prototype._close = OutputStream.prototype._close;
IOStream.prototype._write = OutputStream.prototype._write;
IOStream.prototype.write = OutputStream.prototype.write;
IOStream.prototype.end = OutputStream.prototype.end;
IOStream.prototype._final = OutputStream.prototype._final;

IOStream.prototype._destroy = function (err, callback) {
InputStream.prototype._destroy.call(this, err,
OutputStream.prototype._destroy.bind(this, err, callback));
}

0 comments on commit 439fdb2

Please sign in to comment.