TooTallNate / proxy-agents

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

Socket not emitting data when proxy fails #87

Closed alefwmm closed 4 years ago

alefwmm commented 5 years ago

Hello guys,

I have one problem currently and a possible solution for it. I am using node-https-proxy-agent#3.0.1 with axios#0.19.0.

It works properly when the proxy connects successfully. The problem comes when my proxy provider gives me an error response like HTTP/1.1 502 Proxy Error during the connection process. When this happens, I am unable to catch this response and then, a timeout is thrown by my axios instance.

This is the case for all version beyond node-https-proxy-agent#2.2.1, the one that fixed a major security flaw. Reading axios code, I saw that it does not handle the closed socket properly, expecting the normal stream events to properly execute.

This way, I chose to do the following:

// ...
// Replaced the following
// socket.push(buffers)
// with
socket.emit('data', buffers);
socket.emit('end');
// ...

Here a way to reproduce the problem:

const axios = require("axios");
const HttpsProxyAgent = require("https-proxy-agent");

const agent = new HttpsProxyAgent("a:b@zproxy.lum-superproxy.io:22225");

const instance = axios.create({
    baseURL: `https://example.com`,
    timeout: 10000,
    httpsAgent: agent,
    validateStatus: () => true // Accepts all status codes
});

instance.get().then(response => {
    // Expected a response with the proxy error here
    // It should give a response with a 407 Invalid Auth status
    console.log(response);
}).catch(error => {
    // A timeout error is thrown instead
    // Error: timeout of 10000ms exceeded...
    console.error(error)
});

Using the solution explained before, it worked. I would like to know if this is the best way to do this.

I am currently using this branch

watson commented 4 years ago

I'm seeing the same issue (with both the latest version and v2.2.3+).

Here's a simple program to reproduce:

const http = require('http')
const https = require('https')
const HttpsProxyAgent = require('https-proxy-agent')

const server = http.createServer()

server.on('connect', (req, socket, head) => {
  console.log('proxy got request for', req.url)
  socket.end('HTTP/1.1 500 Internal Server Error\r\n\r\n')
})

server.listen(() => {
  const opts = {
    host: 'example.com',
    agent: new HttpsProxyAgent('http://localhost:' + server.address().port)
  }

  https.get(opts, res => {
    console.log('client got response:', res.statusCode)
    res.resume()
    res.on('end', () => {
      console.log('client detected end of response')
    })
  })
})

If using the latest version res will never emit an end event. If using v2.2.2 it will.