How to Conduct Stress/Load Testing for a Node.js Gateway with Requests from Different IP Addresses?

43 Views Asked by At

Learning and Implementing Gateways with Node.js

I am learning about gateways and working on their implementation using Node.js. One specific feature I am focusing on is rate limiting, which I intend to implement myself. This rate limiter utilizes IP addresses to keep track of the number of requests.

To ensure the functionality is correctly implemented, I need to conduct thorough testing. In particular, I am looking for a way to make multiple requests with different IP addresses to test the rate limiting locally.

Consider a simple (not accurate to what I intend to implement) rate limiter.

const cash = {};
const limit = 50;
const rateLimiter = async (req, res, next) => {
    if (!cash[req.ip]) cash[req.ip] = { count: 0, time: [] };
    
    if (cash[req.ip].count >= limit) {
        if (Date.now() - cash[req.ip].time[0] < 60000) {
            return res.send("Limit reached");
        } else {
            for (const t of cash[req.ip].time) {
                if (Date.now() - t > 60000) break;
                cash[req.ip].time.shift(); // Remove the oldest entry
                cash[req.ip].count--;
            }
        }
    }

    cash[req.ip].count++;
    cash[req.ip].time.push(Date.now());
    next();
};

Manipulating IP Address in Axios or Node-Fetch for Custom Requests

I want to run a script where I need to make custom requests to a server using Axios or Node-Fetch. I want to perform some experiments and need to manipulate the IP address that is being sent in the request object.

Here is a simplified version of my current code using Axios:

const axios = require("axios");
const http = require("http");

function getRandomPrivateIP() {
    const octet3 = Math.floor(Math.random() * 256);
    const octet4 = Math.floor(Math.random() * 256);

    return `192.168.${octet3}.${octet4}`;
}

function createAxiosInstanceWithIP(ipAddress) {
    const agent = new http.Agent({
        localAddress: ipAddress,
    });

    return axios.create({ httpAgent: agent, httpsAgent: agent });
}

const url = "http://localhost:8080/gateway";
const numberOfRequests = 1000;

for (let i = 0; i < numberOfRequests; i++) {
    const customIPAddress = getRandomPrivateIP(); // Invoke the function
    const axiosInstance = createAxiosInstanceWithIP(customIPAddress);

    axiosInstance
        .get(url)
        .then((response) => {
            // Handle the response
            console.log(`Request ${i + 1} successful`);
        })
        .catch((error) => {
            // Handle errors
            console.error(`Request ${i + 1} failed: ${error.message}`);
        });
}

Above code throws error:

Request 1000 failed: bind EINVAL 192.168.72.253

Is there a way to manipulate the IP address in the request object to have more control over the experiment I am trying to perform? I have explored Apache server and other dedicated VPS for benchmarking, but I would like to customize the IP address for my requests within the script.

Furthermore, if there are instances where I may be making mistakes or need to approach situations with a different mindset, let me know.

1

There are 1 best solutions below

0
Aditya On

I found the solution, it is as follows

The localAddress option in the http.Agent is used for binding the socket to a local interface's IP address, but it does not affect the outgoing IP address in the HTTP request. To achieve what I wanted, we need to use a custom DNS resolver to resolve the host to a specific IP.

Here is an updated version of the code:

function createAxiosInstance(ipAddress) {
    const agent = new http.Agent({
        localAddress: ipAddress,
        lookup: (hostname, options, callback) => {
            callback(null, ipAddress, 4);
        },
    });

    return axios.create({ httpAgent: agent, httpsAgent: agent });
}

This code includes a custom DNS resolver function in the lookup option of the http.Agent, which resolves the hostname to the specified IP address.