http - 如何通过代理进行 HTTP/HTTPS GET
问题描述
我正在尝试通过工作代理发出 http/s 请求。我在节点 JS 的遗留项目中有一个工作示例,如果与以下选项对象一起使用,则使用本机 Node.js https
( ) 库可以发出请求:require('https')
{
host: "<actual target url>"
hostname: "<proxy ip>"
}
例如,要example.com
通过代理向 https 请求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
存在,并且 resp 是nil
)
代理本身是一个根据这个要点配置的 NGINX 实例:
https://gist.github.com/selfish/6e858eb17aa82971d25b21775e9649cb#file-nginx-conf
任何人都可以帮助理解 Node.js 和 Golang 的 HTTP 处理的区别吗?
解决方案
您混淆了正向和反向代理。
从概念上讲,它是这样工作的:
反向代理
- 部署在连接的服务器端附近
- 假装是起源
- 由网站所有者或代表网站所有者控制
- 用户代理本身并不知道
转发代理
- 部署在连接的客户端附近
- 由用户代理或代表用户代理控制
- 在用户代理中明确配置
(当然,现实比这更复杂,但这足以突出差异)。
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
这本质上意味着,“建立到 example.com:443 的 TCP 连接,然后像一个愚蠢的 TCP 代理一样工作。” 由于 nginx 是一个反向代理,只有在遇到 CONNECT 请求时才会感到困惑。
要将请求发送到特定的反向代理,您只需修改请求 URL,可能还有 Host 标头(这取决于 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 和 Host 标头无关。这会产生与上述相同的请求,并且应该等同于您的 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)
推荐阅读
- html - 如何让星星背景与鼠标交互
- javascript - 使用 JavaScript+HTML 设置网站 cookie
- python - python中的异步或并发
- c# - Xamarin Forms Android 应用程序在事件处理程序期间创建类对象后崩溃
- python - 合并两个数据帧以减少内存消耗
- android - Android - onClickListener 不适用于 CardView 中的 ImageButton
- visual-studio - Visual Studio 2019 扩展无限加载
- python - 无法在 cmd 中执行 python popen.read
- android - 如果我不想实现 FirebaseMessagingService,是否有替代 onNewToken 的方法?
- c++ - 如何在 C++11 中以与 C++03 中相同的方式使用 errno