Giter Site home page Giter Site logo

Comments (8)

astur avatar astur commented on August 16, 2024 10

It's not only with node-fetch.

For example, this code:

const request = require('got');
const HttpsProxyAgent = require('https-proxy-agent');
const agent = new HttpsProxyAgent('http://very.slow.proxy');

request('https://example.com', {
    agent,
    timeout: 1000,
    retry: 0,
})
    .then(r => console.log(r))
    .catch(e => console.log(e));

It will not stop after error, cause socket will not be closed at same moment.

Using wtfnode you can see something like this:

[WTF Node?] open handles:
- File descriptors: (note: stdio always exists)
  - fd 1 (tty) (stdio)
  - fd 2 (tty) (stdio)
- Sockets:
  - 192.168.1.51:53797 -> undefined:undefined
    - Listeners:
Unable to determine callsite for "Function". Did you require `wtfnode` at the top of your entry point?
      - connect: (anonymous) @ unknown:0
Unable to determine callsite for "connect". Did you require `wtfnode` at the top of your entry point?
      - connect: connect @ unknown:0

from proxy-agents.

Slind14 avatar Slind14 commented on August 16, 2024

same

from proxy-agents.

ciarans avatar ciarans commented on August 16, 2024

Yep, sadly just hangs 😭

from proxy-agents.

ciarans avatar ciarans commented on August 16, 2024

I managed to get a workaround by wrapping my request in this - https://www.npmjs.com/package/promise-timeout

from proxy-agents.

mh4ck avatar mh4ck commented on August 16, 2024

Just took a look into the code and it seems that the latest version got no "timeout" handling.

Is there a best practise to handle it?

from proxy-agents.

Knautiluz avatar Knautiluz commented on August 16, 2024

Same, infinity request using https-proxy-agent with http-proxy-agent works perfectly`

from proxy-agents.

TooTallNate avatar TooTallNate commented on August 16, 2024

This module has gone through a large refactor and modernization. I am closing this issue as a bit of house cleaning. If you feel that this issue still exists in the latest release, feel free to open a new issue.

from proxy-agents.

miticc06 avatar miticc06 commented on August 16, 2024

I found the problem in the source, I quickly fixed it with the following:

We will clone http-proxy-agent and fix it.

http-proxy-agent.js

const net = require('net');
const tls = require('tls');
const http = require('http');
// const createDebug = require('debug');
const { once } = require('events');
const { Agent } = require('agent-base');

// const debug = createDebug('http-proxy-agent');

class HttpProxyAgent extends Agent {
    static protocols = ['http', 'https'];

    constructor(proxy, opts) {
        super(opts);
        this.timeout = opts.timeout;
        this.proxy = typeof proxy === 'string' ? new URL(proxy) : proxy;
        this.proxyHeaders = opts?.headers ?? {};
        // debug('Creating new HttpProxyAgent instance: %o', this.proxy.href);

        // Trim off the brackets from IPv6 addresses
        const host = (this.proxy.hostname || this.proxy.host).replace(
            /^\[|\]$/g,
            ''
        );
        const port = this.proxy.port
            ? parseInt(this.proxy.port, 10)
            : this.proxy.protocol === 'https:'
                ? 443
                : 80;
        this.connectOpts = {
            ...(opts ? omit(opts, 'headers') : null),
            host,
            port,
        };
    }

    addRequest(req, opts) {
        req._header = null;
        this.setRequestProps(req, opts);
        // @ts-expect-error `addRequest()` isn't defined in `@types/node`
        super.addRequest(req, opts);
    }

    setRequestProps(req, opts) {
        const { proxy } = this;
        const protocol = opts.secureEndpoint ? 'https:' : 'http:';
        const hostname = req.getHeader('host') || 'localhost';
        const base = `${protocol}//${hostname}`;
        const url = new URL(req.path, base);
        if (opts.port !== 80) {
            url.port = String(opts.port);
        }

        // Change the `http.ClientRequest` instance's "path" field
        // to the absolute path of the URL that will be requested.
        req.path = String(url);

        // Inject the `Proxy-Authorization` header if necessary.

        const headers =
            typeof this.proxyHeaders === 'function'
                ? this.proxyHeaders()
                : { ...this.proxyHeaders };
        if (proxy.username || proxy.password) {
            const auth = `${decodeURIComponent(
                proxy.username
            )}:${decodeURIComponent(proxy.password)}`;
            headers['Proxy-Authorization'] = `Basic ${Buffer.from(
                auth
            ).toString('base64')}`;
        }

        if (!headers['Proxy-Connection']) {
            headers['Proxy-Connection'] = this.keepAlive
                ? 'Keep-Alive'
                : 'close';
        }
        for (const name of Object.keys(headers)) {
            const value = headers[name];
            if (value) {
                req.setHeader(name, value);
            }
        }
    }

    async connect(req, opts) {
        req._header = null;

        if (!req.path.includes('://')) {
            this.setRequestProps(req, opts);
        }

        // At this point, the http ClientRequest's internal `_header` field
        // might have already been set. If this is the case then we'll need
        // to re-generate the string since we just changed the `req.path`.
        let first;
        let endOfHeaders;
        // debug('Regenerating stored HTTP header string for request');
        req._implicitHeader();
        if (req.outputData && req.outputData.length > 0) {
            // debug(
            //     'Patching connection write() output buffer with updated header'
            // );
            first = req.outputData[0].data;
            endOfHeaders = first.indexOf('\r\n\r\n') + 4;
            req.outputData[0].data =
                req._header + first.substring(endOfHeaders);
            // debug('Output buffer: %o', req.outputData[0].data);
        }

        // Create a socket connection to the proxy server.
        let socket
        if (this.proxy.protocol === 'https:') {
            // debug('Creating `tls.Socket`: %o', this.connectOpts);
            socket = tls.connect(this.connectOpts);
        } else {
            // debug('Creating `net.Socket`: %o', this.connectOpts);
            socket = net.connect(this.connectOpts);
        }


        const cleanup = () => {
            req.destroy();
            socket.destroy();
            socket.emit('connect', 'timeout')
        };

        if (this.timeout !== null) {
            socket.setTimeout(this.timeout);
            socket.on('timeout', () => cleanup());
        }

        // Wait for the socket's `connect` event, so that this `callback()`
        // function throws instead of the `http` request machinery. This is
        // important for i.e. `PacProxyAgent` which determines a failed proxy
        // connection via the `callback()` function throwing.
        await once(socket, 'connect');


        return socket;
    }
}

function omit(obj, ...keys) {
    const ret = {};
    for (let key in obj) {
        if (!keys.includes(key)) {
            ret[key] = obj[key];
        }
    }
    return ret;
}


module.exports = {
    HttpProxyAgent,
}

Let's start using it like this:

const { HttpProxyAgent } = require("./http-proxy-agent");
const { default: axios } = require('axios');

const TIMEOUT = 1000
const proxyAgent = new HttpProxyAgent(proxy, { timeout: TIMEOUT });
const axiosProxy = axios.create({
    httpsAgent: proxyAgent,
    httpAgent: proxyAgent,
})
const res = await axiosProxy.request({
    method: 'get',
    url: `https://ifconfig.me/ip`,
    timeout: TIMEOUT,
})

from proxy-agents.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.