clash for windows 系统代理时 pip 出现 ProxyError 的情况分析记录

21/06/07 Update: CFW [解决了](https://github.com/Fndroid/clash_for_windows_pkg/issues/1787)这个问题,此问题同样存在于 [QV2ray](https://github.com/Qv2ray/Qv2ray/issues/1533) 最近终于想写点 Python,结果打开 PyCharm 开个 venv 之后,pip给我疯狂报错: ``` WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProxyError('Cannot connect to proxy.', OSError(0, 'Error'))': /simple /gitpython/ WARNING: Retrying (Retry(total=3, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProxyError('Cannot connect to proxy.', OSError(0, 'Error'))': /simple /gitpython/ ``` 偏偏在venv外面就是一切正常,这促使我想找到问题。 遇到这种问题那肯定是直接debug走起,首先用文本搜索到抛这个错的函数,然后在嫌疑语句上都打上断,我们就能找到罪魁祸首: ![image-20210222200745856](https://vip1.loli.io/2021/02/22/gozhcf7nx6mFp3t.png) 追到这个函数的实现里面,虽然直接看起来没什么问题,但是和venv外没问题的老版pip一比较,很容易就能发现不对劲:选中的那两行在老版是不存在的 ![image-20210222200929044](https://vip2.loli.io/2021/02/22/M3uycJwZl6QOgE1.png) 有了这个额外信息,我们很容易就能找到这个 [Support for web proxies is broken in pip 20.3 · Issue #9190 · pypa/pip (github.com)](https://github.com/pypa/pip/issues/9190) 来说明问题,2016年底,curl加入了这个把https协议前缀另加解释与定义的联盟:[HTTPS proxy with curl | daniel.haxx.se](https://daniel.haxx.se/blog/2016/11/26/https-proxy-with-curl/),而 urllib3 显然也跟上了这个脚步: ![image-20210222202632913](https://vip2.loli.io/2021/02/22/lRnUMIEw2kBVL7f.png) 在因为前缀设置了 `tls_in_tls_required` 之后,urllib3 会企图把这个代理服务器看作一个套了 tls 的 http CONNECT 代理。 但是问题是,我并没有设置 `https://` 前缀的代理服务器,这个行为是什么神奇的情况呢? 继续向下追,找到如何获取代理的: ![image-20210222201304250](https://vip2.loli.io/2021/02/22/vlOdsYbQ4FGKRfr.png) 确实,env里面没有proxy,那按照windows的习俗找找注册表也情有可原对吧? 这个函数的内部实现是这样: ![image-20210222201432723](https://vip2.loli.io/2021/02/22/ZgJR9yecEb6tKOk.png) 看红框的行为是不是好像很眼熟?IE的代理设置似乎就是这样的? 不,并不是一样的,因为IE的代理设置把HTTPS(在`zh-MS`方言里叫安全)代理定义为支持 `CONNECT` 动词的HTTP代理,尽管很久以来人们都是这样用的,但是当它前面出现一个协议前缀的时候就不一样了。 因为 clash for windows 打开系统代理的代理配置看起来并没有写明了 protocol: ![image-20210222222519422](https://vip2.loli.io/2021/02/22/P8sMyutm2LVGKbf.png) 所以首先,我们的py会根据IE时代的约定俗成把这样一个没有指明protocol的proxy url自动补全三种协议,然后再按照约定俗成的行为为https请求使用https_proxy,最后在一个http代理上试图开tls。 这个配置在 IE 时代行为会是正常的,在现代的库中行为也是正常的,但是对于这样一个混杂了两种行为的库,模糊不清就成了问题。 上面的截图并不是 urllib3 的,而是 属于py自己的标准库 urllib 的,也算是 urllib3 开发人员的思想和 urllib 的历史遗留实现冲突了吧。

评论

  1. 原来如此,但这也就是说clash的代理,是不支持https吗?按照网上的教程,平常手动加代理,不也是加两个http_proxy和https_proxy变量吗。但是指向的不都是一个代理ip吗。这块不是很明白。

    回复删除
    回复
    1. https_proxy 指在访问 https 协议的 url 时使用的代理,可以是 http/https/socks 等协议的代理,通常也是以uri的形式指明协议

      删除
  2. 碰到这个问题很久了,每次都只能暂时把clash系统代理关闭,今天终于知道原因了。蛋疼的是这问题双方各自都没有bug,估计不可能官方修了。。所以说这个有什么补救的方法吗?

    回复删除
    回复
    1. clash新版本有个Specify Protocol开启就好了,或者System Proxy的Type换成PAC。都可以解决

      删除
    2. 嗯,刚刚看了cfw的issue已经解决了,还是谢谢了

      删除
  3. 我开了specify protocol或换成pac会报另外的错 ERROR: XMLRPC request failed [code: -32500],还是不不行

    回复删除
  4. 感谢,clash 新版本有个 Specify Protocol 开启就好了,已经好了

    回复删除

发表评论

此博客中的热门博文

ESXi 配置 DSM 黑群晖踩坑记

在 VPS 上使用 Cloudflare Warp 提升媒体解锁能力 & 路由解决办法