The callback listens for the response event. If no proxy service is set, the net module is used to create a connection with the server. But where is the response event sent?
Let's take a look at the important onSocket function.
ClientRequest.prototype.onSocket=function(socket) {process.nextTick(onSocketNT,this, socket);};functiononSocketNT(req, socket) {if (req.aborted) {// If we were aborted while waiting for a socket, skip the whole thing.socket.emit('free'); } else {tickOnSocket(req, socket); }}
The onSocket function must be an asynchronous function. If the request fails, the free event is sent. Otherwise, it goes to tickOnSocket.
functiontickOnSocket(req, socket) {var parser =parsers.alloc();req.socket = socket;req.connection = socket;parser.reinitialize(HTTPParser.RESPONSE);parser.socket = socket;parser.incoming =null;parser.outgoing = req;req.parser = parser;socket.parser = parser;socket._httpMessage = req;// Setup "drain" propagation.httpSocketSetup(socket);// Propagate headers limit from request object to parserif (typeofreq.maxHeadersCount ==='number') {parser.maxHeaderPairs =req.maxHeadersCount <<1; } else {// Set default value because parser may be reused from FreeListparser.maxHeaderPairs =2000; }parser.onIncoming = parserOnIncomingClient;socket.removeListener('error', freeSocketErrorListener);socket.on('error', socketErrorListener);socket.on('data', socketOnData);socket.on('end', socketOnEnd);socket.on('close', socketCloseListener);req.emit('socket', socket);}
Like the HTTP server, a parser is requested from the pool to parse the HTTP protocol. At this point, the connection has been established, so the error event callback is reset.
Data callback is also set, and the socket event is sent to parserOnIncomingClient.
// clientfunctionparserOnIncomingClient(res, shouldKeepAlive) {var socket =this.socket;var req =socket._httpMessage;// propagate "domain" setting...if (req.domain &&!res.domain) {debug('setting "res.domain"');res.domain =req.domain; }debug('AGENT incoming response!');if (req.res) {// We already have a response object, this means the server// sent a double response.socket.destroy();return; }req.res = res;var isHeadResponse =req.method ==='HEAD';// ...req.res = res;res.req = req;// add our listener first, so that we guarantee socket cleanupres.on('end', responseOnEnd);var handled =req.emit('response', res);// If the user did not listen for the 'response' event, then they// can't possibly read the data, so we ._dump() it into the void// so that the socket doesn't hang there in a paused state.if (!handled)res._dump();return isHeadResponse;}
// Send the response event here, and the req object is also attached to the res object. This way, req and res reference each other.
The user's callback is finally called.
Summary
The above only outlines the main line of the http client. In practice, we rarely use this module, but instead use third-party npm packages, such as: