Верстакфорум практиков
рекламаiprazon: приватные серверные адреса IPv4 и SOCKS5, безлимитный трафик, бесплатный тест до 2 часов
ФорумНастройка и подключение

Node: undici ProxyAgent вместо старых обёрток

ilya.works
ilya.works
Участник
сообщений 305
с ноя
15 ноября, 14:22первое сообщение

Переписал сборщик со свежим node и выкинул request, который лежал мёртвым грузом. Взял встроенный fetch, и обнаружил забавное: переменные окружения он не смотрит вообще.

export HTTPS_PROXY=http://login:pass@193.0.2.44:8000
node -e "fetch('https://example.org/ip').then(r=>r.text()).then(console.log)"

Выводит адрес самой машины. Через curl с теми же переменными всё уходит куда надо. Получается, встроенный fetch надо учить отдельно?

scrapy_boy
scrapy_boy
Участник
сообщений 530
с окт
22 апреля, 17:39#2

Надо. Внутри у него undici, а undici переменные окружения исторически игнорирует, ходит напрямую.

Учится он через диспетчер:

import { ProxyAgent, setGlobalDispatcher } from 'undici';

const agent = new ProxyAgent({ uri: 'http://193.0.2.44:8000', token: 'Basic ' + Buffer.from('login:pass').toString('base64'), });

setGlobalDispatcher(agent);

const r = await fetch('https://example.org/ip'); console.log(await r.text()); ```

setGlobalDispatcher ставит его на все последующие вызовы fetch в процессе. Если нужен не на все, передавайте агента прямо в вызов через поле dispatcher.

ilya.works
ilya.works
Участник
сообщений 305
с ноя
1 сентября, 08:56#3
scrapy_boy: token: 'Basic ' плюс base64

А почему пару входа нельзя просто вписать в uri, как обычно делают в строке подключения?

Пробовал, http://login:pass@193.0.2.44:8000, соединение поднимается, а в ответ прилетает 407.

nullbyte
nullbyte
Ветеран
сообщений 2680
с апр
8 февраля, 11:13#4

Потому что ProxyAgent берёт из uri только схему, хост и порт. Пара оттуда не читается, её ждут в отдельном поле, и это ровно то место, где спотыкаются все, кто пришёл из мира curl.

Ещё важно: заголовок собирается вручную, включая слово Basic и пробел после него. Забыли слово, получите тот же 407, и в отладке это выглядит совершенно одинаково.

Если пароль со спецсимволами, кодировать его перед base64 не нужно, туда идёт исходная строка как есть. А вот в uri пришлось бы кодировать процентами, лишний повод не мешать эти два способа.

если не воспроизводится, значит не понял
Настя Лужина
Настя Лужина
Новичок
сообщений 265
с янв, второй сезон
15 июля, 14:30#5

подскажите, а свежие версии undici правда сами читают переменные?

nullbyte
nullbyte
Ветеран
сообщений 2680
с апр
22 декабря, 17:47#6

Правда, но через отдельного агента, глобально поведение не поменяли:

import { EnvHttpProxyAgent, setGlobalDispatcher } from 'undici';
setGlobalDispatcher(new EnvHttpProxyAgent());

Он смотрит HTTP_PROXY, HTTPS_PROXY и NO_PROXY, включая написание строчными. Пару входа при этом читает прямо из адреса, тут она работает.

если не воспроизводится, значит не понял
proxy_hands
proxy_hands
Знаток
сообщений 1405
с мая
1 мая, 08:04#7

Раз тема про node, оставлю сюда же грабли соседних библиотек, чтобы не заводить отдельную.

axios поле proxy понимает по-своему и для https через туннель работает криво: он подставляет адрес посредника, но при этом ломает проверку сертификата на некоторых сборках. Рабочий порядок такой: proxy: false плюс отдельный агент.

import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';

const agent = new HttpsProxyAgent('http://login:pass@193.0.2.44:8000');

const client = axios.create({ httpsAgent: agent, httpAgent: agent, proxy: false, timeout: 20000, }); ```

Без proxy: false axios попробует применить обе настройки сразу, и запрос уйдёт в туннель внутри туннеля. Симптом узнаваемый: обрыв соединения без внятного сообщения, иногда через минуту после старта.

И второе, уже про нагрузку. У ProxyAgent есть поля connections и keepAliveTimeout. По умолчанию соединения переиспользуются, и на длинном прогоне это хорошо. Но если вы гоняете тысячи коротких запросов и видите рост памяти, ограничьте число соединений в агенте явно, иначе пул растёт вместе с очередью задач.

skript_kid
skript_kid
Новичок
сообщений 54
с сен, второй сезон
8 октября, 11:21#8

а как проверить что запрос реально ушёл через посредника? вдруг он идёт мимо

scrapy_boy
scrapy_boy
Участник
сообщений 530
с окт
15 марта, 14:38#9

Самое простое: запросить сервис, который отдаёт ваш видимый адрес, и сравнить с адресом машины.

const r = await fetch('https://example.org/ip');
console.log('vidimyy adres:', (await r.text()).trim());

Совпал с адресом сервера, значит запрос ушёл напрямую и агент не применился. Второй способ надёжнее: временно вписать заведомо нерабочий порт. Если ошибка соединения не появилась, значит трафик идёт мимо агента.

ilya.works
ilya.works
Участник
сообщений 305
с ноя
22 августа, 17:55#10

Собрал у себя так: EnvHttpProxyAgent для основного сбора, отдельный ProxyAgent с явными connections для тяжёлого куска, где тысячи мелких запросов.

Проверку с нерабочим портом добавил в тесты, штука отличная: сразу видно, когда правка кода случайно уводит запросы мимо. Спасибо, вопрос закрыт.