uspring
Version:
A very fast Webserver which has interface like springboot
92 lines (91 loc) • 4.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
var openStreams = 0;
var streamIndex = 0;
/* Helper function to pipe the ReadaleStream over an Http responses */
function pipeStreamOverResponse(res, readStream, totalSize) {
/* Careful! If Node.js would emit error before the first res.tryEnd, res will hang and never time out */
/* For this demo, I skipped checking for Node.js errors, you are free to PR fixes to this example */
readStream.on('data', function (chunk) {
/* We only take standard V8 units of data */
var ab = toArrayBuffer(chunk);
/* Store where we are, globally, in our response */
var lastOffset = res.getWriteOffset();
/* Streaming a chunk returns whether that chunk was sent, and if that chunk was last */
var _a = res.tryEnd(ab, totalSize), ok = _a[0], done = _a[1];
/* Did we successfully send last chunk? */
if (done) {
onAbortedOrFinishedResponse(res, readStream);
}
else if (!ok) {
/* If we could not send this chunk, pause */
readStream.pause();
/* Save unsent chunk for when we can send it */
res.ab = ab;
res.abOffset = lastOffset;
/* Register async handlers for drainage */
res.onWritable(function (offset) {
/* Here the timeout is off, we can spend as much time before calling tryEnd we want to */
/* On failure the timeout will start */
var _a = res.tryEnd(res.ab.slice(offset - res.abOffset), totalSize), ok = _a[0], done = _a[1];
if (done) {
onAbortedOrFinishedResponse(res, readStream);
}
else if (ok) {
/* We sent a chunk and it was not the last one, so let's resume reading.
* Timeout is still disabled, so we can spend any amount of time waiting
* for more chunks to send. */
readStream.resume();
}
/* We always have to return true/false in onWritable.
* If you did not send anything, return true for success. */
return ok;
});
}
}).on('error', function () {
/* Todo: handle errors of the stream, probably good to simply close the response */
console.error('Unhandled read error from Node.js, you need to handle this!');
res.close();
});
/* If you plan to asyncronously respond later on, you MUST listen to onAborted BEFORE returning */
res.onAborted(function () {
onAbortedOrFinishedResponse(res, readStream);
});
}
exports.pipeStreamOverResponse = pipeStreamOverResponse;
/* Helper function converting Node.js buffer to ArrayBuffer */
function toArrayBuffer(buffer) {
return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
}
exports.toArrayBuffer = toArrayBuffer;
/* Either onAborted or simply finished request */
function onAbortedOrFinishedResponse(res, readStream) {
if (res.id == -1) {
console.error("ERROR! onAbortedOrFinishedResponse called twice for the same res!");
}
else {
// console.log('Stream was closed, openStreams: ' + --openStreams);
console.timeEnd(res.id);
}
/* Mark this response already accounted for */
res.id = -1;
}
exports.onAbortedOrFinishedResponse = onAbortedOrFinishedResponse;
function streamToString(stream) {
var chunks = [];
return new Promise(function (resolve, reject) {
stream.on('data', function (chunk) { return chunks.push(chunk); });
stream.on('error', reject);
stream.on('end', function () { return resolve(Buffer.concat(chunks).toString('utf8')); });
});
}
exports.streamToString = streamToString;
function streamToBuffer(stream) {
var chunks = [];
return new Promise(function (resolve, reject) {
stream.on('data', function (chunk) { return chunks.push(chunk); });
stream.on('error', reject);
stream.on('end', function () { return resolve(Buffer.concat(chunks)); });
});
}
exports.streamToBuffer = streamToBuffer;