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

最近终于想写点 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 的历史遗留实现冲突了吧。

评论

此博客中的热门博文

ESXi 配置 DSM 黑群晖踩坑记

Asgard 4T SATA SSD 狗东上车记录&测评

wsl2, docker desktop, etc踩坑小记