Как получить HTTP / HTTPS через прокси - PullRequest
0 голосов
/ 31 марта 2019

Я пытаюсь выполнить запрос 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"
}

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

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?

1 Ответ

1 голос
/ 02 апреля 2019

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

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

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

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

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

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

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

                                     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. Клиент не знает и не контролирует, что происходит после прокси.

Если вы не можете изменить запрос, вы можете установить функцию Transport.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)
...