Configure Custom HTTP Parser with Axios in Node.js

This article explains how to configure and use a custom or relaxed HTTP parser with Axios in Node.js applications. It covers using custom http.Agent options to bypass strict parsing errors, processing raw HTTP streams with third-party parsers, and building a custom Axios adapter for low-level socket parsing.

Method 1: Using Node's Insecure HTTP Parser with Axios

Node.js uses llhttp by default, which enforces strict HTTP specification compliance. If an API returns non-compliant headers, Node.js throws errors like HPE_INVALID_HEADER_TOKEN. You can configure Axios to use Node's relaxed parser via http.Agent and https.Agent.

const axios = require('axios');
const http = require('http');
const https = require('https');

// Create custom agents with insecureHTTPParser enabled
const httpAgent = new http.Agent({ insecureHTTPParser: true });
const httpsAgent = new https.Agent({ insecureHTTPParser: true });

// Create an Axios instance using the custom agents
const client = axios.create({
  httpAgent,
  httpsAgent
});

// Make requests with relaxed parsing rules
async function makeRequest() {
  try {
    const response = await client.get('https://example.com/api/data');
    console.log(response.data);
  } catch (error) {
    console.error('Request failed:', error.message);
  }
}

makeRequest();

Alternatively, you can enable this globally across the entire Node.js process by launching your script with the CLI flag:

node --insecure-http-parser app.js

Method 2: Parsing Raw Streams with a Custom HTTP Parser

If you need a completely custom parsing implementation (such as http-parser-js), configure Axios to return a raw readable stream and pipe it into your parser.

const axios = require('axios');
const { HTTPParser } = require('http-parser-js');

async function fetchWithCustomParser(url) {
  const response = await axios.get(url, {
    responseType: 'stream'
  });

  const parser = new HTTPParser(HTTPParser.RESPONSE);

  parser[HTTPParser.kOnHeadersComplete] = (info) => {
    console.log('Parsed Headers:', info.headers);
  };

  parser[HTTPParser.kOnBody] = (chunk, offset, length) => {
    const bodyChunk = chunk.slice(offset, offset + length).toString();
    console.log('Parsed Body Chunk:', bodyChunk);
  };

  parser[HTTPParser.kOnMessageComplete] = () => {
    console.log('Parsing complete.');
  };

  response.data.on('data', (chunk) => {
    parser.execute(chunk, 0, chunk.length);
  });

  response.data.on('end', () => {
    parser.finish();
  });
}

fetchWithCustomParser('https://jsonplaceholder.typicode.com/todos/1');

Method 3: Implementing a Custom Axios Adapter

For full control over network sockets and response parsing, you can implement a custom Axios adapter.

const axios = require('axios');
const net = require('net');
const tls = require('tls');
const url = require('url');

const customParserAdapter = (config) => {
  return new Promise((resolve, reject) => {
    const parsedUrl = url.parse(config.url);
    const isHttps = parsedUrl.protocol === 'https:';
    const port = parsedUrl.port || (isHttps ? 443 : 80);
    const host = parsedUrl.hostname;

    const transport = isHttps ? tls : net;

    const socket = transport.connect(port, host, () => {
      // Build raw HTTP request
      const requestData = `${config.method.toUpperCase()} ${parsedUrl.path || '/'} HTTP/1.1\r\n` +
                          `Host: ${host}\r\n` +
                          `Connection: close\r\n\r\n`;
      socket.write(requestData);
    });

    let rawData = '';

    socket.on('data', (chunk) => {
      rawData += chunk.toString();
    });

    socket.on('end', () => {
      // Apply custom parsing logic to the raw socket output
      const [headerBlock, ...bodyParts] = rawData.split('\r\n\r\n');
      const body = bodyParts.join('\r\n\r\n');

      const response = {
        data: body,
        status: 200,
        statusText: 'OK',
        headers: { rawHeaders: headerBlock },
        config,
        request: socket
      };

      resolve(response);
    });

    socket.on('error', (err) => {
      reject(err);
    });
  });
};

// Use the custom adapter with Axios
const customClient = axios.create({
  adapter: customParserAdapter
});

customClient.get('https://example.com')
  .then(res => console.log('Response via custom adapter:', res.data))
  .catch(err => console.error(err));