lib/mockHttp.js
/*
 * on every request(), invoke the handler with (req, res, done) and let the handler
 * manage the "session".  The handler is invoked immediately after req is returned to
 * the caller.  The caller`s request callback is invoked with res when
 * res.once('mockResponse') fires.
 * Note: the handler gets client-side objects ClientRequest and IncomingMessage objects,
 * not the server-side IncomingMessage and ServerResponse.
 */
on every request(), invoke the handler with (req, res, done) and let the handler
manage the "session".  The handler is invoked immediately after req is returned to
the caller.  The caller`s request callback is invoked with res when
res.once('mockResponse') fires.
Note: the handler gets client-side objects ClientRequest and IncomingMessage objects,
not the server-side IncomingMessage and ServerResponse.
on every request () invoke the handler with ( req res done ) and let the handler manage the " session " . The handler is invoked immediately after req is returned to the caller . The caller ` s request callback is invoked with res when res . once ( ' mockResponse ' ) fires . Note : the handler gets client - side objects ClientRequest and IncomingMessage objects not the server - side IncomingMessage and ServerResponse .
function mockRequest( httpHandler, defaultOptions ) {
    return function _mockRequestCall( uri, callback ) {
        var options = typeof uri === 'string' ? Url.parse(uri) : uri;
        var socket = new stream.Stream();
        var options = util._extend(util._extend({
            createConnection: function(port, host, options) {
                return socket;
            }
        }, defaultOptions), options);
        var protocol = options.protocol;
        options.protocol = 'http:';
        var req = new http.ClientRequest(options, _noop);
        var res = new http.IncomingMessage(socket);
        options.protocol = protocol;
        req._mockWrites = new Array();
        req.abort = function() {
            req.__destroyed = true;
            req._mockWrites.splice(0);
            setImmediate(function() { req.emit('abort'); req.emit('error', makeError('ECONNRESET', 'Error: socket hang up')); req.emit('close'); });
        }
        req.socket = new (stream.Writable || stream.Stream)();
        req.socket.setTimeout = _noop;
        req.socket.destroy = req.abort; 
        req._options = options;
        var reqSocket = req.socket;
        process.nextTick(function() { req.socket = reqSocket });
        if (parseInt(process.versions.node) < 12) req._headers = {};
        res.headers = {};
        for (var k in uri.headers) req.setHeader(k.toLowerCase(), uri.headers[k]);
        if (uri.auth) req.setHeader('authorization', 'Basic ' + fromBuf(uri.auth).toString('base64'));
        req.once('mockResponse', function(handlerRes) {
            if (!handlerRes) handlerRes = res;
            handlerRes.url = req.url;
            handlerRes.method = req.method;
            handlerRes.socket = socket;
            if (handlerRes.httpVersion === null) {
                handlerRes.httpVersionMajor = '1';
                handlerRes.httpVersionMinor = '1';
                handlerRes.httpVersion = '1.1';
            }
            if (!handlerRes.headers) handlerRes.headers = {};
            req.once('mockResponseDone', function() {
                handlerRes.push(null);
            })
            callback(handlerRes);
        })
        setImmediate(function() {
            req.url = options.path || options.pathname + (options.query || '') + (options.hash || '');
            req.method = options.method || 'GET';
            httpHandler(req, res);
        })
        req.write = function(data, encoding) {
            if (req.__destroyed) return;
            req._mockWrites.push([data, encoding]);
            req.emit('_mockWrite', data, encoding);
        }
        req.end = function(data, encoding) {
            if (req.__destroyed) return;
            if (data) req.write(data, encoding);
            req._mockWrites.push(null);
            req.emit('_mockWrite', null);
        }
        return req;
    }
}
function mockRequest ( httpHandler , defaultOptions ) { return function _mockRequestCall ( uri , callback ) { var options = typeof uri === 'string' ? Url . parse ( uri ) : uri ; var socket = new stream . Stream ( ) ; var options = util . _extend ( util . _extend ( { createConnection : function ( port , host , options ) { return socket ; } } , defaultOptions ) , options ) ; var protocol = options . protocol ; options . protocol = 'http:' ; var req = new http . ClientRequest ( options , _noop ) ; var res = new http . IncomingMessage ( socket ) ; options . protocol = protocol ; req . _mockWrites = new Array ( ) ; req . abort = function ( ) { req . __destroyed = true ; req . _mockWrites . splice ( 0 ) ; setImmediate ( function ( ) { req . emit ( 'abort' ) ; req . emit ( 'error' , makeError ( 'ECONNRESET' , 'Error: socket hang up' ) ) ; req . emit ( 'close' ) ; } ) ; } req . socket = new ( stream . Writable || stream . Stream ) ( ) ; req . socket . setTimeout = _noop ; req . socket . destroy = req . abort ; req . _options = options ; var reqSocket = req . socket ; process . nextTick ( function ( ) { req . socket = reqSocket } ) ; if ( parseInt ( process . versions . node ) < 12 ) req . _headers = { } ; res . headers = { } ; for ( var k in uri . headers ) req . setHeader ( k . toLowerCase ( ) , uri . headers [ k ] ) ; if ( uri . auth ) req . setHeader ( 'authorization' , 'Basic ' + fromBuf ( uri . auth ) . toString ( 'base64' ) ) ; req . once ( 'mockResponse' , function ( handlerRes ) { if ( ! handlerRes ) handlerRes = res ; handlerRes . url = req . url ; handlerRes . method = req . method ; handlerRes . socket = socket ; if ( handlerRes . httpVersion === null ) { handlerRes . httpVersionMajor = '1' ; handlerRes . httpVersionMinor = '1' ; handlerRes . httpVersion = '1.1' ; } if ( ! handlerRes . headers ) handlerRes . headers = { } ; req . once ( 'mockResponseDone' , function ( ) { handlerRes . push ( null ) ; } ) callback ( handlerRes ) ; } ) setImmediate ( function ( ) { req . url = options . path || options . pathname + ( options . query || '' ) + ( options . hash || '' ) ; req . method = options . method || 'GET' ; httpHandler ( req , res ) ; } ) req . write = function ( data , encoding ) { if ( req . __destroyed ) return ; req . _mockWrites . push ( [ data , encoding ] ) ; req . emit ( '_mockWrite' , data , encoding ) ; } req . end = function ( data , encoding ) { if ( req . __destroyed ) return ; if ( data ) req . write ( data , encoding ) ; req . _mockWrites . push ( null ) ; req . emit ( '_mockWrite' , null ) ; } return req ; } }
on every request(), invoke the handler with (req, res, done) and let the handler
 manage the "session".
on every request () invoke the handler with ( req res done ) and let the handler manage the " session " .
// NOTE: Url.parse downcases the path! ie 'PathName' => 'pathname' // mock up the socket, req, res // TODO: procol 'https:' breaks new ClientRequest; how does nodejs https create it? // support req.abort() // callback is optional, but http seems to always pass one in // if (!callback) callback = _noop; // node-v0.8 does not have stream.Writable... but seems ok with just Stream? /* istanbul ignore next */ // mock only, do not actually destroy sockets // preserve the request options, we might need them later // provide a callable ClientRequest / IncomingMessage setTimeout method // simulate timeouts with .delay + .throw / .emit('error', new Error('ETIMEDOUT')) // req.socket will be set on nextTick to a Stream, change it back to our mock socket // init the standard fields // node-v12 and up deprecate accessing OutgoingMessage._headers, writable/enumerable/configurable all false // However, http.ClientRequest() pre-initializes it with 'host' and maybe 'content-length' // Object.defineProperty avoids the deprecation warning, but breaks setHeader: headers dont get set /* istanbul ignore next */ // NOTE: in node-v8 can no longer set or delete in req._headers[], must use req.setHeader() // set the expected res fields to appear like // statusCode, statusMessage get set //handlerRes.statusCode = 200; //handlerRes.statusMessage = 'OK'; // also: rawHeaders, rawTrailers,  // FIXME: res.statusCode should be known in the callback, but is // actually set by the mock actions and will only be available on 'mockResponseDone' // be sure to flush the data and force an 'end' event // start the mock implementation when the request is made, it should gather the writes and act on .end() // actual response emulation is up to the httpHandler fake server, eg mockHttpServer // req.url is set inside the response callback, but not in caller request object // We cheat and use the same req object for both contexts. // TODO: mock more http.ServerResponse response methods, for familiarity // write, end, addTrailers, getHeader, setHeader, headersSent, statusCode, statusMessage, writeHead, // Set headers, statusCode, statusMessage // capture the request payload so we can restart it if necessary