Inspecting Raw Sockets in Axios for Node.js
Axios abstracts low-level network communication in Node.js, but
certain tasks—such as measuring precise network latency, checking TLS
certificate details, or diagnosing connection issues—require access to
the underlying network socket. In Node.js, Axios relies on the built-in
http and https modules, which provide access
to net.Socket and tls.TLSSocket instances.
This article covers the primary methods to capture and inspect the raw
socket used by Axios.
Method 1: Accessing the Socket via the Response Object
When an HTTP request completes successfully, Axios attaches the
underlying http.ClientRequest object to
response.request. You can access the socket directly from
this object.
const axios = require('axios');
async function inspectSocketFromResponse() {
const response = await axios.get('https://example.com');
const socket = response.request.socket;
console.log('Local Port:', socket.localPort);
console.log('Remote Address:', socket.remoteAddress);
console.log('Encrypted:', socket.encrypted);
if (socket.encrypted) {
console.log('TLS Protocol:', socket.getProtocol());
console.log('Peer Certificate:', socket.getPeerCertificate());
}
}
inspectSocketFromResponse();If the request fails, the same request object is
attached to error.request, allowing you to inspect the
socket during network errors.
Method 2:
Listening to the 'socket' Event on the Request
If you need to inspect or attach listeners to the socket
before the request finishes (for example, to track DNS
resolution or connection handshakes), you can capture the
http.ClientRequest instance early using the
socket event.
Axios exposes the underlying request instance in request interceptors or through transport hooks:
const axios = require('axios');
const instance = axios.create();
instance.interceptors.request.use((config) => {
// Axios creates the request internally, but you can hook into
// the transport request creation via custom adapters or agent events.
return config;
});To directly listen to the native 'socket' event, handle
the request instance as soon as it is assigned:
const https = require('https');
const axios = require('axios');
const agent = new https.Agent();
axios.get('https://example.com', { httpsAgent: agent, transport: {
request: (options, callback) => {
const req = https.request(options, callback);
req.on('socket', (socket) => {
console.log('Socket assigned to request');
socket.on('lookup', () => console.log('DNS lookup complete'));
socket.on('connect', () => console.log('TCP connect complete'));
socket.on('secureConnect', () => console.log('TLS handshake complete'));
});
return req;
}
}});Method 3: Using a Custom HTTP/HTTPS Agent
Overriding the createConnection method of an
http.Agent or https.Agent gives you full
visibility into raw socket creation before it is handed off to the HTTP
parser.
const axios = require('axios');
const https = require('https');
const tls = require('tls');
class CustomAgent extends https.Agent {
createConnection(options, callback) {
const socket = tls.connect(options, () => {
console.log('Connected to:', options.host);
callback(null, socket);
});
socket.on('data', (chunk) => {
console.log('Raw Bytes Received:', chunk.length);
});
return socket;
}
}
async function run() {
const httpsAgent = new CustomAgent();
await axios.get('https://example.com', { httpsAgent });
}
run();This method is ideal for logging raw binary data, tracking byte counts, or injecting customized socket-level timeouts and keep-alive settings.