Как получить HTTP/HTTPS через прокси

Я пытаюсь выполнить запрос http/s через рабочий прокси. У меня есть рабочий пример в устаревшем проекте в узле JS, где использование собственной библиотеки Node.js https (require('https')) может сделать запрос, если он используется со следующим объектом параметров:

{
    host: "<actual target url>"
    hostname: "<proxy ip>"
}

Например, чтобы сделать https-запрос к example.com через прокси 1.1.1.1, я буду использовать:

{
    host: "http://example.com"
    hostname: "1.1.1.1"
}

В Golang я пробовал несколько задокументированных вариантов. В частности, я ожидаю:

proxyUrl, _ := url.Parse("<proxy ip>")
myClient := &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyUrl)}}
resp, err := myClient.Get("https://<actual target url>/...")

В результате прокси отклоняет соединение, указывая на неточность в запросе. (err существует, и соответственно есть nil)


Сам прокси-сервер представляет собой экземпляр NGINX, настроенный в соответствии со следующим принципом:

https://gist.github.com/selfish/6e858eb17aa82971d25b21775e9649cb#file-nginx-conf

Может ли кто-нибудь помочь понять разницу в обработке HTTP для Node.js и Golang?

можешь вставить ошибку?

aitchkhan 31.03.2019 08:50

сервер nginx получил запрос от вашего клиента go?

Pan Ke 31.03.2019 11:02

nginx — это обратный прокси. В Go не требуется специальной настройки. Просто отправьте запрос на адрес и порт nginx. Transport.Proxy предназначен для прямых прокси-серверов, обычно используемых для кэширования и/или фильтрации.

Peter 31.03.2019 12:57

Питер прав. В вашем примере node.js вы на самом деле не говорите ему использовать HTTP-прокси - вы просто говорите, что для доступа к цели следует использовать другой IP-адрес, чем тот, который возвращается DNS. Таким образом, он выполняет обычный HTTP-запрос. Вместо этого ваш код Go выполняет запрос прокси-сервера HTTP, который отличается от обычного запроса HTTP и не ожидается обратным прокси-сервером. Таким образом, это терпит неудачу.

Steffen Ullrich 31.03.2019 13:56

@Питер действительно был прав. Если вы хотите добавить это в качестве ответа, я приму. Спасибо!!

Selfish 01.04.2019 11:03
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
5
1 134
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы путаете прямые и обратные прокси.

Концептуально это работает так:

Обратные прокси

  • развертываются рядом с серверной частью соединения
  • претендовать на происхождение
  • контролируются владельцем сайта или от его имени
  • по своей сути не известны пользовательскому агенту

Переадресация прокси

  • развернуты рядом с клиентской стороной соединения
  • контролируются агентом пользователя или от его имени
  • явно настроены в пользовательском агенте

(Реальность, конечно, сложнее, но этого достаточно, чтобы подчеркнуть различия).

                                     Internet                    ||  Invisible to
                                         +                       ||  User Agent
                                         |                       ||
 +------------+     +---------------+    |    +---------------+  ||  +--------+
 |            |     |               |    |    |               |  ||  |        |
 | User Agent +---->+ Forward Proxy +-------->+ Reverse Proxy +----->+ Origin |
 |            |     |               |    |    |               |  ||  |        |
 +------------+     +---------------+    |    +---------------+  ||  +--------+
                                         |                       ||
                                         +                       ||
                                                                 ||

nginx — это обратный прокси-сервер, но, устанавливая поле Transport.Proxy, вы обращаетесь с ним как с прямым прокси-сервером. Это запрос, который видит nginx:

CONNECT example.com:443 HTTP/1.1
Host: example.com:443
User-Agent: Go-http-client/1.1

По сути, это означает: «Установите TCP-соединение с example.com:443, а затем действуйте как тупой TCP-прокси». Поскольку nginx является обратным прокси, он справедливо путается при столкновении с запросом CONNECT.

Чтобы отправить запрос на конкретный обратный прокси-сервер, вам просто нужно изменить URL-адрес запроса и, возможно, заголовок хоста (это зависит от того, ожидает ли nginx определенный server_name). Никакой специальной настройки клиента не требуется.

Предполагая, что nginx работает на 198.51.100.1:

req, _ := http.NewRequest("GET", "http://198.51.100.1", nil)
req.Host = "example.com" // if necessary
res, _ := http.DefaultClient.Do(req)

Это приводит к отправке следующего запроса на адрес 198.51.100.1:80:

GET / HTTP/1.1
Host: example.com
User-Agent: Go-http-client/1.1
Accept-Encoding: gzip

Обратите внимание, что обратный прокси-сервер полностью зависит от того, попадет ли запрос на сайт example.com. Клиент не знает и не контролирует, что происходит после прокси.

Если вы не в состоянии изменить запрос, вы можете установить функцию Транспорт.DialContext таким образом, чтобы ваш прокси всегда вызывался, независимо от URL-адреса запроса и заголовка хоста. Это приводит к тому же запросу, что и выше, и должен быть эквивалентен вашему коду JavaScript:

c := &http.Client{
    Transport: &http.Transport{
        DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
            return (&net.Dialer{}).DialContext(ctx, "tcp", "198.51.100.1:80")

            // Or 198.51.100.1:443 if nginx has TLS enabled, although that almost
            // certainly causes TLS validation errors because a certificate for 
            // example.com is expected.
        },
    },
}

req, _ := http.NewRequest("GET", "http://example.com", nil)
res, _ := c.Do(req)

Это золото. Спасибо!

Selfish 03.04.2019 20:16

Другие вопросы по теме