【日常踩坑】从 SSLEOFError 到正确配置 Proxy

本文主要参考 Python 遭遇 ProxyError 问题记录 重新梳理改写

踩坑

在前几天搞定 pip 的 SSL 认证之后,结果在利用 requests 库请求 HTTPS 网站又出现了 SSLError(SSLEOFError(8, 'EOF occurred in violation of protocol (_ssl.c:1129)')) 的 SSL 相关错误

经过一系列的查询资料和测试发现,原因竟然在于 python 自身的 urllib 库没有正确配置 HTTPS 代理

代理服务器

普通的代理服务器

Proxy_Server

上面提及的 HTTP(S) 代理,其实是通过代理服务器进行 HTTP(S) 流量的转发的意思,也是在上图中的 黄线 所代表的协议,文中后续用 出口协议 来指代

而和代理服务器之间其实也需要一种协议进行通信,就是在上图中的 绿线 部分,文中后续用 入口协议 来指代

入口协议 通常使用较多的都是 HTTP 和 Socks4/Socks5,很少有采用 HTTPS 作为与代理服务器间的连接协议,这点也是导致之前报错的主要原因

因国家法律规定,部分内容已删除,完整内容请查看文章末尾链接

代理配置

因此 入口协议出口协议 之间其实没有任何因果联系,以 CFW 为例

它的 入口协议 支持 http 以及 socks,而且都在同一个端口,因此正确的代理配置应该是这样的:

# 正确的配置方式
HTTP_PROXY=http://127.0.0.1:7890
HTTPS_PROXY=http://127.0.0.1:7890

或者

# 正确的配置方式
HTTP_PROXY=socks5://127.0.0.1:7890
HTTPS_PROXY=socks5://127.0.0.1:7890

重点:

HTTPS_PROXY 也应该填写 http://127.0.0.1:7890,因为 HTTPS_PROXYHTTPS 代表的是 出口协议,而 http://127.0.0.1:7890 代表和 127.0.0.1:7890 服务器之间的 入口协议HTTP

追根溯源

# 错误的配置方式
HTTP_PROXY=http://127.0.0.1:7890
HTTPS_PROXY=https://127.0.0.1:7890

而之前一直采用的上述错误配置,则会因为旧版本的 pythonpip 内含的 urllib3 不支持 HTTPS 的 入口协议 ,自动转换成了 HTTP 的 入口协议 进行连接了

urllib3

但是在 urllib3 库升级到 v1.26.0 版本之后,增加了对 HTTPS 的 入口协议 的支持,参见 Add support for HTTPS connections to proxies.

Urllib3_Support_HTTPS

pip

pip 内置了的 requestsurllib3 包,而不依赖全局的 requestsurllib3

pip 版本高于 20.3 时,内置的 requests 包升级到了 v2.25.0,urllib3 包也升级到了 v1.26.2,也就是说开始支持 HTTPS 的 入口协议 了,参见 pypa/pip 20.3 (2020-11-30) NEWS.rst

Pip_Support_HTTPS

万恶之源 urllib

但是其实他们都不是罪魁祸首,真正的原因其实在 python 的内置包 urllib

CFW 等软件一般会通过修改 Windows 注册表的 计算机\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings 目录下的 ProxyServer 来配置代理服务器地址端口以及 ProxyEnable 是否启用代理

Win11_Reg

CFW 在配置代理服务器时,仅仅给出了地址和端口,并没有给出 入口协议

# urllib 配置代理的源码摘录:
if '=' in proxyServer:
    # Per-protocol settings
    for p in proxyServer.split(';'):
        protocol, address = p.split('=', 1)
        # See if address has a type:// prefix
        if not re.match('(?:[^/:]+)://', address):
            address = '%s://%s' % (protocol, address)
        proxies[protocol] = address
else:
    # Use one setting for all protocols
    if proxyServer[:5] == 'http:':
        proxies['http'] = proxyServer
    else:
        proxies['http'] = 'http://%s' % proxyServer
        proxies['https'] = 'https://%s' % proxyServer
        proxies['ftp'] = 'ftp://%s' % proxyServer

按照上面给出的 urllib 库源码逻辑,会将代理配置为

proxies = {
    'http': 'http://127.0.0.1:7890',
    'https': 'https://127.0.0.1:7890',
    'ftp': 'ftp://127.0.0.1:7890'
}

因此导致了 piprequests 等上层包,访问 HTTPS 网站时会错误的使用 https://127.0.0.1:7890 代理,而 CFW 根本不支持 HTTPS 的 入口协议,所以才会产生这么一系列的错误

个人推荐可以根据自己常用的科学上网工具所支持的 入口协议 来修改 urllib 库源码逻辑(文件位置一般在 ***/python3.*/urllib/request.py 或者 ***/anaconda3/Lib/urllib/request.py

if '=' in proxyServer:
    # Per-protocol settings
    for p in proxyServer.split(';'):
        protocol, address = p.split('=', 1)
        # See if address has a type:// prefix
        if not re.match('(?:[^/:]+)://', address):
            address = '%s://%s' % (protocol, address)
        proxies[protocol] = address
else:
    # Use one setting for all protocols
    proxies['http'] = 'http://%s' % proxyServer
    proxies['https'] = 'http://%s' % proxyServer
    proxies['ftp'] = 'http://%s' % proxyServer

或者简单的按照下面的方式进行修改(并不一定适用所有情况)

if '=' in proxyServer:
    # Per-protocol settings
    for p in proxyServer.split(';'):
        protocol, address = p.split('=', 1)
        # See if address has a type:// prefix
        if not re.match('(?:[^/:]+)://', address):
            address = '%s://%s' % (protocol, address)
        proxies[protocol] = address
else:
    # Use one setting for all protocols
    proxies['http'] = proxyServer
    proxies['https'] = proxyServer
    proxies['ftp'] = proxyServer

参考资料

本文作者: ywang_wnlo
本文链接: https://ywang-wnlo.github.io/posts/76f6af57/
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

  • 53
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 24
    评论
在 Nginx 中配置 proxy,可以使用 `location` 指令来匹配需要代理的 URL,然后使用 `proxy_pass` 指令将请求转发到后端服务器。 例如,将所有请求代理到 http://backend-server: ``` location / { proxy_pass http://backend-server; } ``` 如果后端服务器使用了 HTTPS 协议,可以使用 `https` 参数: ``` location / { proxy_pass https://backend-server; } ``` 如果需要配置反向代理,可以在 `location` 中添加需要代理的 URL 路径,然后使用 `proxy_pass` 将请求转发到指定的后端服务器。例如: ``` location /api { proxy_pass http://backend-server; } ``` 这样,所有以 `/api` 开头的请求都会被代理到 http://backend-server 上。 如果需要在请求头中添加一些额外的信息,可以使用 `proxy_set_header` 指令。例如,以下配置将在请求头中添加 `X-Forwarded-For` 和 `X-Real-IP` 两个字段: ``` location / { proxy_pass http://backend-server; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; } ``` 这些字段可以用于记录客户端的真实 IP 地址和请求来源。 另外,如果后端服务器返回的响应中包含一些需要重写的 URL,可以使用 `proxy_redirect` 指令进行重写。例如,以下配置将会将返回的 URL 中的 `/api` 替换为 `/backend`: ``` location /api { proxy_pass http://backend-server; proxy_redirect /api/ /backend/; } ``` 需要注意的是,如果后端服务器返回的资源中包含相对路径,可能会因为路径错误而导致页面无法正常显示。在这种情况下,可以在 Nginx 中添加 `proxy_set_header` 指令,将 `Host` 头部信息传递给后端服务器,以便后端服务器能够正确地解析相对路径。例如: ``` location / { proxy_pass http://backend-server; proxy_set_header Host $host; } ``` 这样就可以在代理中正确地处理相对路径了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值