UNPKG

hastily

Version:

express middleware to simulate fastly cdn

175 lines 12.8 kB
'use strict'; 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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