Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proxy-agent is not working with http proxy & web sockets #176

Closed
OpportunityLiu opened this issue May 18, 2023 · 5 comments · Fixed by #192
Closed

proxy-agent is not working with http proxy & web sockets #176

OpportunityLiu opened this issue May 18, 2023 · 5 comments · Fixed by #192

Comments

@OpportunityLiu
Copy link

Since proxies will drop hop-by-hop headers (Connect and Upgrade), we cannot use http-proxy-agent to connect to a web socket. But when I use proxy-agent, it will send ws: requests via http-proxy-agent.

@TooTallNate
Copy link
Owner

Can you share your code?

@OpportunityLiu
Copy link
Author

OpportunityLiu commented May 20, 2023

Here is a simple repro:

proxy.js

import * as http from 'http';
import { createProxy } from 'proxy';

const server = createProxy(http.createServer());
server.listen(3128, () => {
    var port = server.address().port;
    console.log('HTTP(s) proxy server listening on port %d', port);
});

server.js

import { createServer } from 'node:http';
import { WebSocketServer } from 'ws';

const s = createServer((req, res) => {
    console.log('request', req.url, req.headers);
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('okay');
});
s.listen(8080);
const wss = new WebSocketServer({ server: s });
wss.on('listening', () => {
    console.log('Ws server listening');
});
wss.on('connection', (ws, req) => {
    console.log('connected', req.url, req.headers);
});

client.js

import { WebSocket } from 'ws';
import { ProxyAgent } from 'proxy-agent';

const ws1 = new WebSocket('ws://localhost:8080/ws1');

process.env['http_proxy'] = 'http://localhost:3128';
process.env['https_proxy'] = 'http://localhost:3128';
process.env['no_proxy'] = '';
const agent = new ProxyAgent();
const ws2 = new WebSocket('ws://localhost:8080/ws2', { agent });

Start server.js and proxy.js, then run client.js.

In server.js, you will get:

Ws server listening
connected /ws1 {
  'sec-websocket-version': '13',
  'sec-websocket-key': 'b3x5PQrzBDcfjKK7T9ndXQ==',
  connection: 'Upgrade',
  upgrade: 'websocket',
  'sec-websocket-extensions': 'permessage-deflate; client_max_window_bits',
  host: 'localhost:8080'
}
request /ws2 {
  'sec-websocket-version': '13',
  'sec-websocket-key': '2dCSx8sD4wUmNCXAENRoRA==',
  'sec-websocket-extensions': 'permessage-deflate; client_max_window_bits',
  host: 'localhost:8080',
  'proxy-connection': 'close',
  'x-forwarded-for': '::ffff:127.0.0.1',
  via: '1.1 xxx (proxy/2.1.1)',
  connection: 'close'
}

indicates that connection without proxy-agent (/ws1) is handled successfully by server, while /ws2 is handled as a plain http request.

In client.js, you will get:

node:events:491
      throw er; // Unhandled 'error' event
      ^

Error: Unexpected server response: 200
    at ClientRequest.<anonymous> (...\node_modules\ws\lib\websocket.js:888:7)
    at ClientRequest.emit (node:events:513:28)
    at HTTPParser.parserOnIncomingClient (node:_http_client:693:27)
    at HTTPParser.parserOnHeadersComplete (node:_http_common:128:17)
    at Socket.socketOnData (node:_http_client:534:22)
    at Socket.emit (node:events:513:28)
    at addChunk (node:internal/streams/readable:315:12)
    at readableAddChunk (node:internal/streams/readable:289:9)
    at Socket.Readable.push (node:internal/streams/readable:228:10)
    at TCP.onStreamRead (node:internal/stream_base_commons:190:23)
Emitted 'error' event on WebSocket instance at:
    at emitErrorAndClose (...\node_modules\ws\lib\websocket.js:1008:13)
    at processTicksAndRejections (node:internal/process/task_queues:83:21)

@OpportunityLiu
Copy link
Author

Currently a workaround is always to use https-proxy-agent for http proxies.

import { proxies } from 'proxy-agent';

proxies['http'][0] = proxies['http'][1];
proxies['https'][0] = proxies['https'][1];

@TooTallNate
Copy link
Owner

Fixed in proxy-agent@6.2.1.

@ttodua
Copy link

ttodua commented Sep 20, 2023

a simple example can be found here too - https://gist.github.com/ttodua/7a66e5ca28e55deebc58b0dd8e0c39a2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants