hastily
Version:
express middleware to simulate fastly cdn
175 lines • 12.8 kB
JavaScript
;
import onHeaders from 'on-headers';
import { createLogger } from './logging';
const log = createLogger('splice');
/**
* @hidden
*/
export default function splice(req, res, next, makeStream) {
log.debug({ res }, 'splicing hastily into response for %s', req.originalUrl);
let ended = false;
let length;
const listeners = [];
let stream;
const resEnd = res.end.bind(res);
const resOn = res.on.bind(res);
const resWrite = res.write.bind(res);
const resFlush = typeof res.flush === 'function' ? res.flush.bind(res) : () => null;
const tryImplicitHeader = (instance) => {
try {
instance._implicitHeader();
return true;
}
catch (e) {
return false;
}
};
// flush
res.flush = function flush() {
log.debug('response.flush() called');
if (stream) {
stream.flush();
}
else {
resFlush();
}
};
// proxy
res.write = function write(chunk, encoding) {
log.debug('outgoing response.write() called with chunk of length %s', chunk.length);
if (ended) {
log.debug('response.write(): ended flag is true, returning');
return false;
}
if (!this._header) {
log.debug('response.write(): this._header is false, calling this._implicitHeader()');
if (!tryImplicitHeader(this)) {
return false;
}
}
if (stream) {
log.debug('res.write() has access to stream! writing buffer');
return stream.write(Buffer.from(chunk, encoding));
}
log.debug('res.write() has no access to stream yet. calling underlying response');
return resWrite.call(this, chunk, encoding);
};
res.end = function end(chunk, encoding) {
log.debug('outgoing response.end() called');
if (ended) {
log.debug('response.end(): ended is already true, returning');
return false;
}
if (!this._header) {
log.debug('response.end(): this._header is false, checking content length');
if (!this.getHeader('Content-Length')) {
length = chunkLength(chunk, encoding);
log.debug('response.end(): no Content-Length, %s bytes and counting', length);
}
log.debug('response.end(): calling this._implicitHeader()');
if (tryImplicitHeader(this)) {
log.debug('tryImplicitHeader succeeded');
}
}
if (!stream) {
log.debug('response.end(): stream never became available');
return resEnd.call(this, chunk, encoding);
}
log.debug('response.end(): stream is available, flushing? %s', chunk);
// mark ended
ended = true;
// write Buffer for Node.js 0.8
if (chunk) {
log.debug('chunk of length %s exists in .end, writing it to stream', chunk.length);
stream.end(Buffer.from(chunk, encoding));
}
else {
log.debug({ res }, 'no chunk exists in .end, ending stream clean');
stream.end();
}
};
function addBufferedListener(type, listener) {
log.debug('res.on called for "%s" event', type);
if (!listeners || type !== 'drain') {
log.debug('%s listeners, eventType %s, calling underlying res.on', listeners.length, type);
return resOn.call(this, type, listener);
}
if (stream) {
log.debug('res.on() has access to stream, passing listener');
return stream.on(type, listener);
}
log.debug('stream does not exist; buffering listeners for future stream');
listeners.push([type, listener]);
return this;
}
res.on = addBufferedListener;
const unsplice = () => {
res.on = resOn;
res.end = resEnd;
res.write = resWrite;
addListeners(res, resOn, listeners);
listeners.length = 0;
};
onHeaders(res, function onResponseHeaders() {
try {
if (ended) {
return;
}
stream = makeStream();
if (!stream) {
// request is filtered
unsplice();
return;
}
else {
stream.on('error', () => {
log.debug('stream error, unsplicing');
unsplice();
});
}
// add buffered listeners to stream
addListeners(stream, stream.on, listeners);
// header fields
res.removeHeader('Content-Length');
// compression
stream.on('data', function onStreamData(chunk) {
if (resWrite(chunk) === false) {
stream.pause();
}
});
stream.on('end', resEnd);
resOn.call(res, 'drain', function onResponseDrain() {
stream.resume();
});
}
catch (e) {
console.error(e);
res.statusCode = 400;
res.statusMessage = e.message;
resEnd();
ended = true;
}
});
next();
}
/**
* Add bufferred listeners to stream
* @private
*/
function addListeners(stream, on, listeners) {
for (const listener of listeners) {
on.apply(stream, listener);
}
}
/**
* Get the length of a given chunk
*/
function chunkLength(chunk, encoding) {
if (!chunk) {
return 0;
}
return !Buffer.isBuffer(chunk)
? Buffer.byteLength(chunk, encoding)
: chunk.length;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BsaWNlLXJlc3BvbnNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2xpYi9zcGxpY2UtcmVzcG9uc2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWSxDQUFDO0FBR2IsT0FBTyxTQUFTLE1BQU0sWUFBWSxDQUFDO0FBQ25DLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFRekMsTUFBTSxHQUFHLEdBQUcsWUFBWSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0FBRW5DOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE9BQU8sVUFBVSxNQUFNLENBQzVCLEdBQVksRUFDWixHQUFxQixFQUNyQixJQUF3QixFQUN4QixVQUFxQztJQUVyQyxHQUFHLENBQUMsS0FBSyxDQUFDLEVBQUUsR0FBRyxFQUFFLEVBQUUsdUNBQXVDLEVBQUUsR0FBRyxDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQzdFLElBQUksS0FBSyxHQUFHLEtBQUssQ0FBQztJQUNsQixJQUFJLE1BQU0sQ0FBQztJQUNYLE1BQU0sU0FBUyxHQUFlLEVBQUUsQ0FBQztJQUNqQyxJQUFJLE1BQTJCLENBQUM7SUFFaEMsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDakMsTUFBTSxLQUFLLEdBQUcsR0FBRyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDL0IsTUFBTSxRQUFRLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDckMsTUFBTSxRQUFRLEdBQ1osT0FBTyxHQUFHLENBQUMsS0FBSyxLQUFLLFVBQVUsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQztJQUVyRSxNQUFNLGlCQUFpQixH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