TooTallNate / proxy-agents

Node.js HTTP Proxy Agents Monorepo
https://proxy-agents.n8.io
917 stars 238 forks source link

Proxy issue with system proxy and Https Proxy agent #298

Open samuel-deal-tisseo opened 6 months ago

samuel-deal-tisseo commented 6 months ago

If a system defined proxy and an explicit proxy config, requests failed using axios.

I'm not sure if it's an Axios or a httpsAgent issue (or that I don't use those two library correctly).

Example:

Given the following configuration


import { parse as parseURL } from "url";
import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';
import { HttpProxyAgent } from 'http-proxy-agent';

class PatchedHttpsProxyAgent extends HttpsProxyAgent {
    async connect(req, opts) {
        const url = parseURL(req.path);
        url.port = Number(url.port) || (url.protocol.includes('https') ? 443 : 80);
        return super.connect(req, { ...opts, ...url });
    }
}

function proxyObjToStr(proxyConf) {
    return proxyConf.protocol+"://"+proxyConf.host+":"+proxyConf.port.toString();
}

function test_req(url, proxyConf, applyPatch) {
    let request = {
        method: 'get',
        url: url,
        responseType: 'string',
        httpAgent: new HttpProxyAgent(proxyObjToStr(proxyConf)),
        httpsAgent: new HttpsProxyAgent(proxyObjToStr(proxyConf)),
        proxy: proxyConf
    };
    if (applyPatch) {
        request.proxy = {}
        request.httpsAgent = new PatchedHttpsProxyAgent(proxyObjToStr(proxyConf));
    }
    const axiosInstance = axios.create();
    const test_descr= url + (applyPatch ? " [patched]: " : " [current]: ");
    return axiosInstance(request).then(function (response) {
        console.log(test_descr + 'Ok');
    }).catch(function (error) {
        console.log(test_descr + "KO");
        if (error.response) {
            console.log({
                http_head: error.response.request._header.split("\r\n")[0],
                path: error.response.request.path,
                host: error.response.request.host
            });
        }
    });
}

async function main() {
    process.env["HTTPS_PROXY"] = "http://implicit.proxy:8080";
    const proxyConf = {
        protocol: 'http',
        host: 'explicit',
        port: 9000
    };
    await test_req("http://neverssl.com", proxyConf, false);
    await test_req("https://www.example.com/", proxyConf, false);
    await test_req("http://neverssl.com", proxyConf, true);
    await test_req("https://www.example.com/", proxyConf, true);
}

await main();

I have the following results:

http://neverssl.com [current]: KO
{
  http_head: 'GET http://neverssl.com:8080/ HTTP/1.1',
  path: 'http://neverssl.com:8080/',
  host: implicit.proxy'
}
https://www.example.com/ [current]: KO
{
  http_head: 'GET https://www.example.com:8080/ HTTP/1.1',
  path: 'https://www.example.com:8080/',
  host: 'implicit.proxy'
}
http://neverssl.com [patched]: Ok
https://www.example.com/ [patched]: Ok
TooTallNate commented 5 months ago

I'm afraid I don't really understand what you're describing. Do you think you could submit a failing test case as a pull request? Also, try to take axios out of the equation to rule out some kind of issue with that package.

raphendyr commented 2 months ago

I think you are misconfiguring axios. The code here loads the proxy variable from the environment: https://github.com/axios/axios/blob/v1.x/lib/adapters/http.js#L77

You need to use false, i.e., request.proxy = false.

Please note that "proxy" in axios means "relay my request" or "man in the middle attack". In contrast, https-proxy-agent implements a tunnel or "HTTP CONNECT". These are two totally different methods with totally different implications.

In other words, do not user proxy from axios and always set it to false. It's likely never what you want (for anyone reading this).

Furthermore, note that the http-proxy-agent implements the relay method too. In other words, you want to use HttpsProxyAgent for both httpAgent and httpsAgent (it can be the same instance to my understanding).