nodejs / undici

An HTTP/1.1 client, written from scratch for Node.js
https://nodejs.github.io/undici
MIT License
6.05k stars 530 forks source link

Add a proxy server example for testing undici fetch. #3508

Open sonamoman opened 2 weeks ago

sonamoman commented 2 weeks ago

This would solve...

Hard to find an example of a proxy that works with undici fetch. Tried 3 different proxy servers in Node.js which all work with axios for example but not with undici fetch. Would be very helpful to add a simple proxy server example written in Node.js to test undici fetch with.

The implementation should look like...

A small code block in the README.md like the following Simple proxy in Node.js to test undici fetch with:

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

const PORT = 8000;
const server = createProxy(http.createServer());

server.on('request', (req, res) => {
  console.log(`Incoming request to ${req.url}`);
});
server.listen(PORT, () => {
  console.log('Proxy server listening on port', PORT);
});

Additional context

I want to migrate from axios to fetch (Windows 11, Node.js version 20.17.0) but i've tried 3 different proxies which all work (passing through the proxy and getting a response) with axios (or postman) but none with undici (version 6.19.8) fetch. Request to proxy1.js and proxy2.js never resolves or rejects while request to proxy3.js bypasses proxy completely and gets a response.

Undici fetch test where proxy runs on http://127.0.0.1:8000 and http://127.0.0.1:4003/api/a1 is an endpoint in mockoon which returns a simple json response.

import { ProxyAgent, fetch } from "undici";

(async () => {
    try {
        const client = new ProxyAgent('http://127.0.0.1:8000');
        const response = await fetch("http://127.0.0.1:4003/api/a1", {
            dispatcher: client,
            method: "GET"
        });
        const data = await response.json();
        console.log('Response data:', data);
    } catch (e) {
        console.log(e);
    }
})();

proxy1.js

import http from "http";
import httpProxy from "http-proxy";
import url from "url";

const PORT = 8000;
const proxy = httpProxy.createProxyServer({});

const server = http.createServer((req, res) => {
  console.log(`Incoming request to ${req.url}`);

  const parsedUrl = url.parse(req.url, true);
  const target = `${parsedUrl.protocol}//${parsedUrl.host}`; 
  proxy.web(req, res, { target });
});

server.listen(PORT, () => {
  console.log('Proxy server listening on port', PORT);
});

proxy2.js

import http from "http";
import request from "request";

const PORT = 8000;

http.createServer(function (req, res) {
  console.log(`Incoming request to ${req.url}`);
  req.pipe(request(req.url)).pipe(res);
}).listen(PORT);

proxy3.js

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

const PORT = 8000;
const server = createProxy(http.createServer());

server.on('request', (req, res) => {
  console.log(`Incoming request to ${req.url}`);
});
server.listen(PORT, () => {
  console.log('Proxy server listening on port', PORT);
});

Axios example which works with all 3 proxies:

import axios from "axios";

(async () => {
  try {
      const response = await axios.get('http://127.0.0.1:4003/api/a1', {
        proxy: {
          protocol: "http",
          host: '127.0.0.1',
          port: 8000,
        }
      });
      console.log('Response data:', response.data);
  } catch (e) {
    console.log(error);
  }
})();
metcoder95 commented 2 weeks ago

This is an example of a server proxy with proxy

import * as http from 'node:http'
import { once } from 'node:events'
import { createProxy } from 'proxy'

import { ProxyAgent } from './index.js'

const proxyServer = createProxy(http.createServer())
const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' })
  res.end('okay')
})

// proxyServer.on('request', (req, res) => {
//   console.log(`Incoming request to ${req.url}`)
// })

console.log('Listening...')
await once(proxyServer.listen(0), 'listening')
await once(server.listen(0), 'listening')

const { port: proxyPort } = proxyServer.address()
const { port } = server.address()

console.log(`Proxy listening on port ${proxyPort}`)
console.log(`Server listening on port ${port}`)
;(async () => {
  try {
    const agent = new ProxyAgent(`http://localhost:${proxyPort}`)
    const response = await fetch(`http://localhost:${port}`, {
      dispatcher: agent,
      method: 'GET'
    })
    const data = await response.text()
    console.log('Response data:', data)
  } catch (e) {
    console.log(e)
  }
})()

It is important to remark that undici does a tunneling to the proxy server using CONNECT.

Would you like to open a PR for sending a documentation update?

sonamoman commented 1 week ago

Sure, will open a PR real soon.