Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

hijackresponse

Package Overview
Dependencies
Maintainers
2
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hijackresponse - npm Package Compare versions

Comparing version 3.0.0 to 4.0.0

.eslintignore

156

lib/hijackResponse.js

@@ -1,115 +0,125 @@

var Readable = require('stream').Readable
var Readable = require("stream").Readable;
module.exports = function hijackResponse (res, cb) {
var writeHead = res.writeHead
var write = res.write
var end = res.end
var originalResponse = res
var hijacking = true
var hijackedResponse = new Readable()
hijackedResponse.__proto__ = originalResponse // eslint-disable-line no-proto
hijackedResponse.emit = hijackedResponse.emit
module.exports = function hijackResponse(res, cb) {
var writeHead = res.writeHead;
var write = res.write;
var end = res.end;
var originalResponse = res;
var hijacking = true;
var hijackedResponse = new Readable();
hijackedResponse.__proto__ = originalResponse; // eslint-disable-line no-proto
var readableMethods = Object.keys(Readable.prototype)
readableMethods.forEach(function (method) {
hijackedResponse[method] = Readable.prototype[method].bind(hijackedResponse)
})
var readableMethods = Object.keys(Readable.prototype);
hijackedResponse._read = function () {}
// emit is not included when doing Object.keys on the prototype, but we need
// it.
readableMethods.push("emit");
res.write = function (rawChunk, encoding) {
if (!res.headersSent && res.writeHead !== writeHead) res._implicitHeader()
readableMethods.forEach(function(method) {
hijackedResponse[method] = Readable.prototype[method].bind(
hijackedResponse
);
});
hijackedResponse._read = function() {};
res.write = function(rawChunk, encoding) {
if (!res.headersSent && res.writeHead !== writeHead) res._implicitHeader();
if (hijacking) {
var chunk = rawChunk
if (rawChunk !== null && !Buffer.isBuffer(chunk) && encoding !== 'buffer') {
var chunk = rawChunk;
if (
rawChunk !== null &&
!Buffer.isBuffer(chunk) &&
encoding !== "buffer"
) {
if (!encoding) {
chunk = new Buffer(rawChunk)
chunk = new Buffer(rawChunk);
} else {
chunk = new Buffer(rawChunk, encoding)
chunk = new Buffer(rawChunk, encoding);
}
}
hijackedResponse.push(chunk)
hijackedResponse.push(chunk);
} else {
write.call(originalResponse, rawChunk, encoding)
write.call(originalResponse, rawChunk, encoding);
}
}
};
res.end = function (chunk, encoding) {
res.end = function(chunk, encoding) {
if (chunk) {
res.write(chunk, encoding)
res.write(chunk, encoding);
} else if (!res.headersSent && res.writeHead !== writeHead) {
res._implicitHeader()
res._implicitHeader();
}
if (hijacking) {
hijackedResponse.push(null)
hijackedResponse.push(null);
} else {
end.call(originalResponse)
end.call(originalResponse);
}
}
};
var resEmit = res.emit
res.emit = function (eventName) {
if (eventName === 'close') {
hijackedResponse.emit('close')
var resEmit = res.emit;
res.emit = function(eventName) {
if (eventName === "close") {
hijackedResponse.emit("close");
}
return resEmit.apply(this, arguments)
}
return resEmit.apply(this, arguments);
};
hijackedResponse.destroyHijacked = function () {
res.write = res.end = function () {}
hijackedResponse._readableState.buffer = []
return resEmit.call(res, 'close')
}
hijackedResponse.destroyHijacked = function() {
res.write = res.end = function() {};
hijackedResponse._readableState.buffer = [];
return resEmit.call(res, "close");
};
hijackedResponse.write = function (chunk, encoding) {
write.call(originalResponse, chunk, encoding)
}
hijackedResponse.write = function(chunk, encoding) {
write.call(originalResponse, chunk, encoding);
};
hijackedResponse.end = function (chunk, encoding) {
hijackedResponse.end = function(chunk, encoding) {
if (chunk) {
write.call(originalResponse, chunk, encoding)
write.call(originalResponse, chunk, encoding);
}
if (hijacking) {
end.call(originalResponse)
end.call(originalResponse);
} else {
// If unhijacked, delay end-event so pipes don't close too early giving
// you a chance to have error handlers work.
setImmediate(function () {
end.call(originalResponse)
})
setImmediate(function() {
end.call(originalResponse);
});
}
}
};
hijackedResponse.__defineGetter__('statusCode', function () {
return originalResponse.statusCode
})
hijackedResponse.__defineGetter__("statusCode", function() {
return originalResponse.statusCode;
});
hijackedResponse.__defineSetter__('statusCode', function (statusCode) {
originalResponse.statusCode = statusCode
})
hijackedResponse.__defineSetter__("statusCode", function(statusCode) {
originalResponse.statusCode = statusCode;
});
res.writeHead = function (statusCode, statusMessage, headers) {
if (typeof headers === 'undefined' && typeof statusMessage === 'object') {
headers = statusMessage
statusMessage = undefined
res.writeHead = function(statusCode, statusMessage, headers) {
if (typeof headers === "undefined" && typeof statusMessage === "object") {
headers = statusMessage;
statusMessage = undefined;
}
if (statusCode) {
res.statusCode = statusCode
res.statusCode = statusCode;
}
if (headers) {
for (var headerName in headers) {
res.setHeader(headerName, headers[headerName])
res.setHeader(headerName, headers[headerName]);
}
}
res.writeHead = writeHead
cb(null, hijackedResponse)
}
res.writeHead = writeHead;
cb(null, hijackedResponse);
};
hijackedResponse.unhijack = function () {
hijacking = false
res.write = write
res.end = end
return originalResponse
}
}
hijackedResponse.unhijack = function() {
hijacking = false;
res.write = write;
res.end = end;
return originalResponse;
};
};
{
"name": "hijackresponse",
"version": "3.0.0",
"version": "4.0.0",
"description": "Hijack HttpResponses",

@@ -8,22 +8,22 @@ "main": "lib/hijackResponse.js",

"bufferedstream": "3.1.1",
"compression": "1.6.1",
"compression": "^1.7.0",
"coveralls": "2.11.4",
"errorhandler": "1.4.2",
"eslint": "^5.15.0",
"eslint-config-pretty-standard": "^2.0.1",
"eslint-plugin-import": "^2.16.0",
"express": "4.13.3",
"http-proxy-middleware": "0.17.4",
"istanbul": "0.3.20",
"lodash": "3.10.1",
"mocha": "2.3.2",
"mocha": "^6.0.2",
"nyc": "^13.3.0",
"passerror": "1.1.0",
"sinon": "1.17.3",
"standard": "5.3.0",
"unexpected": "10.13.0",
"unexpected-express": "8.2.0",
"unexpected-sinon": "10.2.0"
"prettier": "^1.16.4",
"unexpected": "^11.1.1",
"unexpected-express": "^11.1.2"
},
"scripts": {
"coverage": "istanbul --include-all-sources cover _mocha",
"lint": "standard",
"coverage": "nyc mocha && nyc report --reporter=html",
"coverage:ci": "nyc mocha && nyc report --reporter=lcov",
"lint": "eslint . && prettier --check '**/*.js'",
"test": "mocha",
"travis": "npm run lint && npm run coverage",
"submit-coveralls": "<coverage/lcov.info coveralls"

@@ -38,5 +38,5 @@ },

"contributors": [
"Andreas Lind <andreas@one.com>"
"Andreas Lind <andreaslindpetersen@gmail.com>"
],
"license": "ISC"
}

@@ -11,2 +11,4 @@ # hijackresponse

Require node v6 or later.
This module is the spiritual successor to

@@ -38,28 +40,26 @@ [express-hijackresponse](https://github.com/papandreou/express-hijackresponse)

```js
var express = require('express');
var hijackResponse = require('hijackresponse');
var express = require("express");
var hijackResponse = require("hijackresponse");
var app = express();
app.use(function (req, res, next) {
hijackResponse(res, function (err, res) {
if (err) {
res.unhijack(); // Make the original res object work again
return next(err);
}
app.use(function(req, res, next) {
hijackResponse(res, function(err, res) {
if (err) {
res.unhijack(); // Make the original res object work again
return next(err);
}
// Don't hijack HTML responses:
if (/^text\/html(?:;$)/.test(res.getHeader('Content-Type'))) {
return res.unhijack();
}
// Don't hijack HTML responses:
if (/^text\/html(?:;$)/.test(res.getHeader("Content-Type"))) {
return res.unhijack();
}
res.setHeader('X-Hijacked', 'yes!');
res.removeHeader('Content-Length');
res.setHeader("X-Hijacked", "yes!");
res.removeHeader("Content-Length");
res
.pipe(transformStream)
.pipe(res);
});
// next() must be called explicitly, even when hijacking the response:
next();
res.pipe(transformStream).pipe(res);
});
// next() must be called explicitly, even when hijacking the response:
next();
});

@@ -66,0 +66,0 @@ ```

@@ -1,291 +0,334 @@

/* global describe, it, beforeEach, afterEach */
var expect = require('./unexpected-with-plugins')
var passError = require('passerror')
var http = require('http')
var hijackResponse = require('../')
var sinon = require('sinon')
const createTestServer = require("./helpers/test-server");
const expect = require("unexpected");
const hijackResponse = require("../lib/hijackResponse");
const stream = require("stream");
describe('hijackResponse', function () {
it('should be able to hijack a reponse and rewrite it', function () {
return expect(function (res, handleError) {
hijackResponse(res, passError(handleError, function (res) {
var chunks = []
res.on('data', function (chunk) {
chunks.push(chunk)
})
res.on('end', function () {
var result = Buffer.concat(chunks).toString('utf-8').toUpperCase()
res.write(result, 'utf-8')
res.end()
})
}))
describe("hijackResponse", () => {
it("should be able to hijack a reponse and rewrite it", () => {
const request = createTestServer((req, res) => {
hijackResponse(res, (err, res) => {
let chunks = [];
res.setHeader('Content-Type', 'text/plain')
res.write('foo')
res.end()
}, 'to yield response', 'FOO')
})
it('should be able to pipe hijacked res into it self.', function () {
return expect(function (res, handleError) {
hijackResponse(res, passError(handleError, function (res) {
res.pipe(res)
}))
res.on("data", chunk => chunks.push(chunk));
res.setHeader('Content-Type', 'text/plain')
res.write('foo')
res.write('bar')
res.end()
}, 'to yield response', 'foobar')
})
it('should be able to hijack an already hijacked response', function () {
return expect(function (res, handleError) {
hijackResponse(res, passError(handleError, function (res) {
hijackResponse(res, passError(handleError, function (res) {
var chunks = []
res.on('data', function (chunk) {
chunks.push(chunk)
}).on('end', function () {
res.setHeader('X-qux', 'hijacked')
res.write(Buffer.concat(chunks))
res.end('qux')
})
}))
res.on("end", () => {
const stringifiedResponse = Buffer.concat(chunks).toString("utf-8");
res.end(stringifiedResponse.toUpperCase());
});
});
res.setHeader('X-bar', 'hijacked')
res.on('data', function (chunk) {
res.write(chunk)
}).on('end', function () {
res.write('bar')
res.end()
})
}))
res.setHeader("Content-Type", "text/plain");
res.end("foo");
});
res.setHeader('Content-Type', 'text/plain')
res.write('foo')
res.end()
}, 'to yield response', {
return expect(request(), "when fulfilled", "to satisfy", { body: "FOO" });
});
it("should pipe through a transform stream", () => {
const request = createTestServer((req, res) => {
hijackResponse(res, (err, res) => {
const uppercaseStream = new stream.Transform({
transform(chunk, encoding, callback) {
if (encoding !== "utf-8") {
chunk = Buffer.from(chunk).toString("utf-8");
}
chunk = chunk.toUpperCase();
callback(null, chunk);
}
});
res.pipe(uppercaseStream).pipe(res);
});
res.setHeader("Content-Type", "text/plain");
res.write("foo");
res.end("bar");
});
return expect(request(), "when fulfilled", "to satisfy", {
body: "FOOBAR"
});
});
it("should be able to pipe hijacked res into it self.", () => {
const request = createTestServer((req, res) => {
hijackResponse(res, (err, res) => res.pipe(res));
res.setHeader("Content-Type", "text/plain");
res.write("foo");
res.write("bar");
res.end();
});
return expect(request(), "when fulfilled", "to satisfy", {
body: "foobar"
});
});
it("should be able to hijack an already hijacked response", () => {
const request = createTestServer((req, res) => {
hijackResponse(res, (err, res) => {
hijackResponse(res, (err, res) => {
const chunks = [];
res
.on("data", chunk => chunks.push(chunk))
.on("end", () => {
res.setHeader("X-qux", "hijacked");
res.write(Buffer.concat(chunks));
res.end("qux");
});
});
res.setHeader("X-bar", "hijacked");
res
.on("data", chunk => res.write(chunk))
.on("end", () => {
res.write("bar");
res.end();
});
});
res.setHeader("Content-Type", "text/plain");
res.write("foo");
res.end();
});
return expect(request(), "when fulfilled", "to satisfy", {
headers: {
'X-qux': 'hijacked',
'X-bar': 'hijacked'
"content-type": "text/plain",
"x-bar": "hijacked",
"x-qux": "hijacked"
},
body: 'foobarqux'
})
})
it('should be able to hijack an already hijacked response when piping', function () {
function appendToStream (value) {
var Transform = require('stream').Transform
var appendTo = new Transform({})
appendTo._transform = function (chunk, encoding, cb) {
this.push(chunk)
cb()
}
appendTo._flush = function (cb) {
this.push(new Buffer(value))
cb()
}
return appendTo
}
return expect(function (res, handleError) {
hijackResponse(res, passError(handleError, function (res) {
hijackResponse(res, passError(handleError, function (res) {
res.pipe(appendToStream('qux')).pipe(res)
}))
res.pipe(appendToStream('baz')).pipe(res)
}))
body: "foobarqux"
});
});
res.setHeader('Content-Type', 'text/plain')
it("should be able to hijack an already hijacked response when piping", () => {
const appendToStream = value =>
new stream.Transform({
transform(chunk, encoding, cb) {
this.push(chunk);
cb();
},
flush(cb) {
this.push(Buffer.from(value));
cb();
}
});
var num = 0
function tick () {
res.write('foo')
num += 1
if (num < 5) return setImmediate(tick)
res.end('bar')
}
tick()
}, 'to yield response', 'foofoofoofoofoobarbazqux')
})
const request = createTestServer((req, res) => {
hijackResponse(res, (err, res) => {
hijackResponse(res, (err, res) => {
res.pipe(appendToStream("qux")).pipe(res);
});
res.pipe(appendToStream("baz")).pipe(res);
});
it('should write the last chunk', function () {
return expect(function (res, handleError) {
hijackResponse(res, passError(handleError, function (res) {
res.end('foobar')
}))
res.setHeader("Content-Type", "text/plain");
res.setHeader('content-type', 'text/plain')
res.writeHead(200)
}, 'to yield response', 'foobar')
})
describe('res.writeHead should trigger the hijackResponse callback', function () {
it('when called without anything', function () {
return expect(function (res, handleError) {
hijackResponse(res, passError(handleError, function (res) {
res.end('foobar')
}))
let num = 0;
const tick = () => {
res.write("foo");
num += 1;
if (num < 5) return setImmediate(tick);
res.end("bar");
};
tick();
});
res.setHeader('content-type', 'text/plain')
res.writeHead()
}, 'to yield response', 'foobar')
})
it('when called with only a status code', function () {
return expect(function (res, handleError) {
hijackResponse(res, passError(handleError, function (res) {
res.end('foobar')
}))
return expect(request(), "when fulfilled", "to satisfy", {
body: "foofoofoofoofoobarbazqux"
});
});
res.setHeader('content-type', 'text/plain')
res.writeHead(200)
}, 'to yield response', 'foobar')
})
it('when called with status code and headers', function () {
return expect(function (res, handleError) {
hijackResponse(res, passError(handleError, function (res) {
res.end('foobar')
}))
it("should write the last chunk", () => {
const request = createTestServer((req, res) => {
hijackResponse(res, (err, res) => res.end("foobar"));
res.setHeader("content-type", "text/plain");
res.writeHead(200);
});
return expect(request(), "when fulfilled", "to satisfy", {
body: "foobar"
});
});
describe("res.writeHead should trigger the hijackResponse callback", () => {
it("when called without anything", () => {
const request = createTestServer((req, res) => {
hijackResponse(res, (err, res) => res.end("foobar"));
res.setHeader("content-type", "text/plain");
res.writeHead();
});
return expect(request(), "when fulfilled", "to satisfy", {
body: "foobar"
});
});
it("when called with only a status code", () => {
const request = createTestServer((req, res) => {
hijackResponse(res, (err, res) => res.end("foobar"));
res.setHeader("content-type", "text/plain");
res.writeHead(200);
});
return expect(request(), "when fulfilled", "to satisfy", {
body: "foobar"
});
});
it("when called with status code and headers", () => {
const request = createTestServer((req, res) => {
hijackResponse(res, (err, res) => res.end("foobar"));
res.writeHead(200, {
'content-type': 'text/plain'
})
}, 'to yield response', 'foobar')
})
})
describe('res.write', function () {
it('should work when called with a buffer', function () {
return expect(function (res, handleError) {
hijackResponse(res, passError(handleError, function (res) {
res.pipe(res)
}))
"content-type": "text/plain"
});
});
res.setHeader('content-type', 'text/plain')
res.write(new Buffer('foobar', 'utf-8'))
res.end()
}, 'to yield response', 'foobar')
})
it('should work when called with null', function () {
return expect(function (res, handleError) {
hijackResponse(res, passError(handleError, function (res) {
res.pipe(res)
}))
return expect(request(), "when fulfilled", "to satisfy", {
body: "foobar"
});
});
});
res.setHeader('content-type', 'text/plain')
res.write(new Buffer('foobar', 'utf-8'))
res.write(null)
}, 'to yield response', 'foobar')
})
it('should work when called with a string', function () {
return expect(function (res, handleError) {
hijackResponse(res, passError(handleError, function (res) {
res.pipe(res)
}))
describe("res.write", () => {
it("should work when called with a buffer", () => {
const request = createTestServer((req, res) => {
hijackResponse(res, (err, res) => res.pipe(res));
res.setHeader('content-type', 'text/plain')
res.write('foobar')
res.end()
}, 'to yield response', 'foobar')
})
it('should work when called with a string and an encoding', function () {
return expect(function (res, handleError) {
hijackResponse(res, passError(handleError, function (res) {
res.pipe(res)
}))
res.setHeader("content-type", "text/plain");
res.write(new Buffer("foobar", "utf-8"));
res.end();
});
res.setHeader('content-type', 'text/plain')
res.write('foobar', 'utf-8')
res.end()
}, 'to yield response', 'foobar')
})
})
describe('res.end', function () {
it('should call res._implicitHeader if it havent been called before', function () {
return expect(function (res, handleError) {
hijackResponse(res, passError(handleError, function (res) {
res.pipe(res)
}))
res.end()
}, 'to yield response', 200)
})
})
describe('res.unhijack', function () {
it('should allow the original data through if unhijacked', function () {
return expect(function (res, handleError) {
hijackResponse(res, passError(handleError, function (res) {
res.unhijack()
}))
res.setHeader('content-type', 'text/plain')
setTimeout(function () {
res.write('foobar')
res.end()
}, 10)
}, 'to yield response', 'foobar')
})
})
describe('against a real server', function () {
var handleRequest
var server
var serverAddress
var serverHostname
var serverUrl
beforeEach(function () {
handleRequest = undefined
server = http.createServer(function (req, res) {
res.sendDate = false
handleRequest(req, res)
}).listen(0)
serverAddress = server.address()
serverHostname = serverAddress.address === '::' ? 'localhost' : serverAddress.address
serverUrl = 'http://' + serverHostname + ':' + serverAddress.port + '/'
})
afterEach(function () {
server.close()
})
it('should emit the close event on the hijacked response', function () {
return expect.promise(function (run) {
handleRequest = run(function (req, res) {
hijackResponse(res, run(function (err, res) {
expect(err, 'to be falsy')
res.on('close', run(function () {}))
}))
res.end('yaddayadda')
})
var request = http.get(serverUrl)
request.end('foo')
setTimeout(function () {
request.abort()
}, 10)
request.on('error', run(function (err) {
expect(err, 'to have message', 'socket hang up')
}))
})
})
})
describe('#destroyHijacked', function () {
it('should prevent hijackedRes from emitting more data', function () {
return expect(function (res, handleError) {
var closeSpy = sinon.spy()
hijackResponse(res, passError(handleError, function (res) {
setTimeout(function () { // Wait for .write('foo') to trigger writeHead and push
sinon.spy(res, 'emit')
res.destroyHijacked()
setTimeout(function () {
expect(res._readableState.buffer, 'to equal', [])
expect(res.emit, 'to have calls satisfying', [])
expect(closeSpy, 'to have calls satisfying', function () {
closeSpy()
})
res.end()
}, 1)
}, 1)
}))
return expect(request(), "when fulfilled", "to satisfy", {
body: "foobar"
});
});
res.on('close', closeSpy)
res.write('foo')
setTimeout(function () {
res.write('bar')
}, 0)
}, 'to yield response', {
it("should work when called with null", () => {
const request = createTestServer((req, res) => {
hijackResponse(res, (err, res) => res.pipe(res));
res.setHeader("content-type", "text/plain");
res.write(Buffer.from("foobar"));
res.write(null);
});
return expect(request(), "when fulfilled", "to satisfy", {
body: "foobar"
});
});
it("should work when called with a string", () => {
const request = createTestServer((req, res) => {
hijackResponse(res, (err, res) => res.pipe(res));
res.setHeader("content-type", "text/plain");
res.write("foobar");
res.end();
});
return expect(request(), "when fulfilled", "to satisfy", {
body: "foobar"
});
});
it("should work when called with a string and an encoding", () => {
const request = createTestServer((req, res) => {
hijackResponse(res, (err, res) => res.pipe(res));
res.setHeader("content-type", "text/plain");
res.write("foobar", "utf-8");
res.end();
});
return expect(request(), "when fulfilled", "to satisfy", {
body: "foobar"
});
});
});
describe("res.end", () => {
it("should call res._implicitHeader if it havent been called before", () => {
const request = createTestServer((req, res) => {
hijackResponse(res, (err, res) => res.pipe(res));
res.end();
});
return expect(request(), "when fulfilled", "to satisfy", {
statusCode: 200
});
});
});
describe("res.unhijack", () => {
it("should allow the original data through if unhijacked", () => {
const request = createTestServer((req, res) => {
hijackResponse(res, (err, res) => res.unhijack());
res.setHeader("content-type", "text/plain");
setTimeout(() => res.end("foobar"), 10);
});
return expect(request(), "when fulfilled", "to satisfy", {
body: "foobar"
});
});
});
describe("#destroyHijacked", function() {
it("should prevent hijackedRes from emitting more data", function() {
let closeCalledCount = 0;
const closeSpy = () => {
closeCalledCount += 1;
};
const emits = [];
const spyEmit = res => {
const origEmit = res.emit;
res.emit = (...args) => {
emits.push(args);
origEmit.apply(res, args);
};
return () => {
res.emit = origEmit;
};
};
const request = createTestServer((req, res) => {
hijackResponse(res, (err, res) => {
// Wait for .write('foo') to trigger writeHead and push
const restoreEmit = spyEmit(res);
setTimeout(() => {
res.destroyHijacked();
setTimeout(() => {
restoreEmit();
res.end();
}, 1);
}, 1);
});
res.on("close", closeSpy);
res.write("foo");
setTimeout(() => res.write("bar"), 0);
});
return expect(request(), "when fulfilled", "to satisfy", {
statusCode: 200,
unchunkedBody: expect.it('to equal', new Buffer([])).or('to be undefined')
})
})
})
})
rawBody: Buffer.concat([])
}).then(() => {
return expect({ closeCalledCount, emits }, "to satisfy", {
closeCalledCount: 1,
emits: []
});
});
});
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc