首页 存档 技术 查看内容

Requests 2.12中的五个为什么

2018-3-30 13:00 |来自: 互联网 323 0

摘要: Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。 无论什么时候,大型开源项目会遇到一个共同的麻烦。升级中的一些变动会导致很多人的代码无法运行,然后我们每一次修复都会带来新的、不可预料的 ...


Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。

无论什么时候,大型开源项目会遇到一个共同的麻烦。升级中的一些变动会导致很多人的代码无法运行,然后我们每一次修复都会带来新的、不可预料的问题。这些版本会让项目的维护者非常头疼,因为这些版本的发布使我们每一次升级看起来都像是我们在修补原来犯的错误。

Requests 也曾遇到类似的问题,而当下我们正在面对这样的问题。至今2.12版本已经给不少用户带来了麻烦,而大多数修复的尝试都引发了后续的问题。

为了能让大家更加了解问题的成因,我会就这个问题进行事后分析。诚然我们很难针对一个还在继续发展的事物进行“事后的”分析,但是有大量能够帮助大家理解的技术细节和背景需要交代。所以,简而言之,我们开始分析吧。我将会以一个当下存在的#3735号问题(https://github.com/kennethreitz/requests/issues/3735)问题作为引入,同时这个问题涉及到我们五个为什么的本质。

然而,与普遍使用的五个为什么的调试思路不同,我在每一步都会涉及到大量的技术细节,这是为了给大家提供我们做出决定的技术背景。理想情况下即便你并不关心 Requests,这些技术细节对你也足够有吸引力。

所以,到此为止,我们开始讨论。

五个为什么

问题:当前v2.12.2版本的 Requests,无法使用除 "http" 或 "https" 协议之外的 URL,也无法将参数关键字传给 Requests URL 。

为什么?

在 2.12.2 版本中,我们不再处理除了 “http” 或 “https” 以外的协议,这种处理包括给 URL 添加参数。此前我们已经开始拒绝处理如此形式的 URL(例如我们自从 2.1.0 版本开始就不会处理 ”ftp“ 协议)但是以头四个字母为 “http” 的方案我们还会处理。现在我们将只会处理 “http” 和 “https”。

为什么?

在处理非 http 协议的 URL 时我们会尝试使用 IDNA(译者注:Internationalized Domain Names in Applications)来编码,并尝试找出主机名。这类 URL 通常没有合适的主机名,所以我们会粗略地挑选出 URL 中主机名的部分并使用 IDNA 进行编码。当然大部分情况下这样做是无效的。

为什么?

原本这样的 URL 是不会失效。然而在2.12.0版本中,我们不再使用 IDNA2003 进行编码,而是使用 IDNA2008。我们使用的 IDNA2008 编码器相较于先前的版本更为严格。这意味着大部分不是主机名称的东西不能确保 IDNA 能够编码。

为什么?

IDNA2008 是 IDNA 的升级版本。IDNA 是一个允许人们使用当地语言作为域名的协议,尽管域名系统(DNS)只能使用 ASCII。具体来讲 IDNA 是将像 "ουτοπα.δπθ.gr" 一样的域名转换为 "xnkxae4bafwg.xnpxaix.gr"

IDNA 通过一个叫做 Punycode 的协议实现这样的转换。Punycode 本质上只是一种将包含非 ASCII 范围的 Unicode 的字符串转换为只包含 ASCII 字符的字符串。这有一点像 base64 编码:我们将很大范围的输入信息映射至很小的输出范围。这里的解码算法稍微有些复杂而且值得深入挖掘,但是我们不在此进行讨论。

当 IDNA2003 正被广泛使用之际,IDNA2008 在几个方面更新了规范。具体来讲,它做出了下列改变:

  1. 大量原本合法的字符被规定为非法的。根据官网(http://unicode.org/faq/idn.html)所写有8,000个此类字符被禁止使用,包括“全部的大写字母,全角和半角的变形,符号以及标点符号“。例如 "http://.com"在 IDNA2003 中是合法的URL ( "http://xnn3h.com" ),但是在 IDNA2008 中是非法的(毫无疑问相比于 IDNA2003 这更无聊,但是可能这是最好的选择)。

  2. 它还改变了四个特定字符编码,其中两个极为重要: “”(拉丁文字母 S 的小写, U 00DF)和 “”(希腊文字母 SIGMA 的小写,U 03C2)。

那么我们为什么要迁移到 IDNA2008 呢?一部分是因为在它的更新中,一些事情的处理变得更合理,但是最重要的是 .de 的国家**域名(ccTLD)强制要求使用 IDNA2008。这里有很多复杂的原因,大多数与不同地区的文化传统有关:例如,IDNA2003 曾将映射为“ss”,但是 IDNA2008 根据 Punycode 将其映射为 “zca”(与其 Unicode 代码一致)。这与最近德语中的变革有很大关系。

这足够说明 IDNA2008 是一个很复杂的东西。然而无论**域名的注册使用什么,我们能够使用是很重要的。否则,人们非常有可能偶然地访问错误的网站。这是真实存在的风险,同时也是我们想改变的。

为什么?

这一个“为什么”究竟用于解释什么,我也不清楚,所以我们来说“我们究竟为什么要做 IDNA 呢?

世界上大多数地方是不讲英文的,Requests 的用户遍布世界各地,他们都应拥有使用母语访问网络的权利,而不是**理解 Unicode 的工作原理。人们常说,好的产品应该让更多的人满意,对于非英语地区的人们,能够使用像 “http://ουτοπα.δπθ.gr” 一样的网址会让他们很满意。

归根结底,Requests 是一个关心全世界用户的项目,不仅仅是使用英语的地区的用户。

突出的问题

我们当前项目的根本问题是我们对于URL的态度与怪异的协议之间的差异。在2.1.0版本中我们加入了一个补丁允许使用非 http 和 https 协议的 URL,本质上使我们的程序本身从此不再处理这类 URL。我们不会以任何方式尝试解析或者理解这类 URL,只是将这些 URL 传至系统的更底层。我们真正地跳过了所有的处理:我们甚至不会规范化URL的类型,它们可以是 bytes,也可以是 unicode 类型。

然而,检测的方法去不是十分严谨。2.1.0版本中加入的代码如下:

if":"inurlandnoturl.lower().startswith("http"):
self.url=url
return

这段代码很明显是是为了各种不同的URL所设计的。第一次检查(检查 URL 中是否包含 ":" )设计成忽略那些根本没有协议的 URL,如果我们不明确地找出协议,就无法进行下一步处理。第二次检查设计为确认其协议为 "http" 或 "https"。

不幸的是,这样不精确的代码意味着任何前四个字母为"http"的协议都会被按照 HTTP 协议处理。由于这一特点,一些下游用户可以自行加入一些功能,他们会故意构建符合这种形式的非 HTTP 协议:最典型的是 "http docker" 和 "http unix" 。

然而,这些协议中可能没有主机名,更糟糕的是这些主机名在 IDNA2008 中可能是非法的。由于 IDNA2008 更严格地检查,这些用户无法继续使用,因此我们尝试给他们提供一个“逃生出口”。其中一个方法是更严格地检查,只对我们明确支持的两个协议进行处理。这意味着,一些原有的 URL 不能够继续使用。它们不是真正的 HTTP URL,却能够通过检查并被以和 HTTP URL 一样的方法进行处理。毫无疑问,这些URL的用户不会喜欢这样的变化。

当下我个人的建议是恢复原来更宽松的检查方法,并加入的补丁使其能够正确编码那些 IDNA2008 不能正常编码的域名,希望这些补丁能够满足这些用户。理想情况下,我们应该尽快发布2.13.3版本,而且应该是2.12版本中第一个完美的版本。

总结

我不知道该总结什么。我只是把我脑海中能想到的东西写出来,让大家知道我们当下在做什么,帮助大家了解大项目的维护工作如何进行。通常情况下像 Requests 一样大的项目不会出现未经至少一个用户测试的代码。这意味着,我们做出的任何一个改变都应该是正确的,因为如果你发布后发现代码太过宽松,当你把代码变得更严谨时,必定会使一些代码无法运行。记住:你永远可以放松检查,而不破坏运行中的代码;但是你不可能让检查变得更严格,却不破坏正在使用的代码。

我想,如果说这件事能带给我什么收获的话,那就是给社区的维护者一个拥抱。


英文原文:https://lukasa.co.uk/2016/11/Five_Whys_Requests_212/
译者:潘礼宁


声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

相关分类

返回顶部