diff --git a/lib/recorder.js b/lib/recorder.js index ba4917883..c38de3557 100644 --- a/lib/recorder.js +++ b/lib/recorder.js @@ -296,89 +296,23 @@ function record(rec_options) { return setEncoding.apply(this, arguments); }; - // Give the actual client a chance to setup its listeners. - // We will use the listener information to figure out - // how we need to feed the intercepted data back to the client. - if (callback) { - callback(res, options, callback); - } - - // Handle clients that listen to 'readable' by intercepting them - // and feeding them the data manually. - var readableListeners = res.listeners('readable'); - if (!_.isEmpty(readableListeners)) { - - debug('handle readable listeners'); - - // We will replace the client's listeners with our own and manually - // invoke them. - _.each(readableListeners, function(listener) { - res.removeListener('readable', listener); - }); - - // Repleace the actual Stream.Readable prototype 'read' function - // so that we can control what the client listener will be reading. - var prototypeRead = Stream.Readable.prototype.read; - var currentReadIndex = 0; - res.read = function() { - - debug(thisRecordingId, 'client reading data on', proto, dataChunks.length); - - // Feed the data to the client through from our collected data chunks. - if (currentReadIndex < dataChunks.length) { - debug('chunk', chunk, 'read'); - var chunk = dataChunks[currentReadIndex]; - ++currentReadIndex; - return chunk; - } else { - debug('no more chunks to read'); - return null; - } - - }; - - // Put our own listener instead of the removed client listener. - var onReadable = function(data) { - debug(thisRecordingId, 'new readable data on', proto); - var chunk; - // Use the prototypeRead function to actually read the data. - while (null !== (chunk = prototypeRead.call(res))) { - debug('read', chunk); - dataChunks.push(chunk); - } - // Manually invoke the user listeners emulating 'readable' event. - _.each(readableListeners, function(listener) { - listener(); - }); - }; - - res.on('readable', onReadable); - - } else { - - // In all other cases we (for now at least) fall back on intercepting - // 'data' events. - debug('fall back on our original implementation'); - - // Since we gave client the chance to setup its listeners - // before us, we need to remove them and setup our own. - var dataListeners = res.listeners('data'); - _.each(dataListeners, function(listener) { - res.removeListener('data', listener); - }); - - var onData = function(data) { - debug(thisRecordingId, 'new data chunk on', proto); + // Replace res.push with our own implementation that stores chunks + var origResPush = res.push; + res.push = function(data) { + if (data) { if (encoding) { data = new Buffer(data, encoding); } dataChunks.push(data); - // Manually invoke the user listeners emulating 'data' event. - _.each(dataListeners, function(listener) { - listener(data); - }); - }; - res.on('data', onData); + } + + return origResPush.call(res, data); + } + + if (callback) { + callback(res, options, callback); + } else { + res.resume(); } debug('finished setting up intercepting'); diff --git a/lib/scope.js b/lib/scope.js index 5f276f9ca..a008572f6 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -322,7 +322,7 @@ function define(nockDefs) { } else { nock = startScope(nscope, options); // If request headers were specified filter by them. - if (reqheaders !== {}) { + if (_.size(reqheaders) > 0) { for (var k in reqheaders) { nock.matchHeader(k, reqheaders[k]); } diff --git a/tests/test_recorder.js b/tests/test_recorder.js index 180ef1c6e..ba8c35d5d 100644 --- a/tests/test_recorder.js +++ b/tests/test_recorder.js @@ -279,7 +279,7 @@ tap.test('records nonstandard ports', function(t) { var RESPONSE_BODY = '012345'; // Create test http server and perform the tests while it's up. - var testServer = http.createServer(function (req, res) { + var testServer = http.createServer(function(req, res) { res.write(RESPONSE_BODY); res.end(); }).listen(8081, function(err) { @@ -474,7 +474,7 @@ tap.test('records and replays gzipped nocks correctly when gzip is returned as a t.ok(false); t.end(); }) - .on('error', function (error, response){ + .on('error', function(error, response) { t.ok(false); t.end(); }) @@ -733,7 +733,7 @@ tap.test('works with clients listening for readable', function(t) { var RESPONSE_BODY = '012345'; // Create test http server and perform the tests while it's up. - var testServer = http.createServer(function (req, res) { + var testServer = http.createServer(function(req, res) { res.write(RESPONSE_BODY); res.end(); }).listen(8081, function(err) { @@ -842,6 +842,51 @@ tap.test('removes query params from from that path and puts them in query()', fu req.end('ABCDEF'); }); +tap.test("respects http.request() consumers", function(t) { + // Create test http server and perform the tests while it's up. + var testServer = http.createServer(function(req, res) { + res.write('foo'); + setTimeout(function() { + res.end('bar'); + }, 25); + }).listen(8082, function(err) { + t.equal(err, undefined); + + nock.restore(); + nock.recorder.clear(); + nock.recorder.rec({ + dont_print: true, + output_objects: true + }); + + + var options = { host:'localhost' + , port:testServer.address().port + , path:'/' } + ; + var req = http.request(options, function (res) { + var buffer = new Buffer(''); + + setTimeout(function () { + res + .on('data', function(data) { + buffer = Buffer.concat([buffer, data]); + }) + .on('end', function() { + nock.restore(); + t.equal(buffer.toString(), 'foobar'); + t.end(); + + // Close the test server, we are done with it. + testServer.close(); + }); + }); + }, 50); + + req.end(); + }); +}); + tap.test("teardown", function(t) { var leaks = Object.keys(global) .splice(globalCount, Number.MAX_VALUE);