转载

URI中+号的坑

URI中 + 号表示空格,这一点已经是常识般的存在。但是还是存在很多令人困扰的地方,并带来许多BUG。

首先是一些Java类,甚至是JDK自带的URI类令人迷惑的表现:

URI uri = new URI(null, null, "1%+ 2", "a=%+ b", null);
System.out.println(uri);  // 1%25+%202?a=%25+%20b

URIBuilder uriBuilder = new URIBuilder();
uriBuilder.setPath("1%+ 2");
uriBuilder.addParameter("a","%+ b");
System.out.println(uriBuilder);  // 1%25+%202?a=%25%2B+b

可以看到JDK的URI类和HttpClient的URIBuild类对同样的输入,编码不一样:

  • 对于URI的path部分,两者都保留了 + 号不做编码,把空格编码为 %20
  • 对于URI的query部分
    • JDK的URI类:对 + 号不做编码,空格编码为 %20
    • HttpClient的URIBuild类: + 号编码为 %2B ,空格编码为 +

可以看到,即使是应该被认为是客观正确的类,在处理 + 和空格上面,表现差距很大,大到生成的URI的语义不一样了。

那回头来看, URI中+号表示空格 ,这个到底正确么?还是一个错误的常识?

查阅了一些资料,比较令人信服的说法是:

+
+

具体讨论见这篇 问答 。其中作者提到,RFC中,只是定义了URI的各个component允许存在哪些字符,但是对于特定的字符是否有特殊的含义,是没有做出说明的。而在HTTP的规范中,对 application/x-www-form-urlencoded 进行说明时,提到了 replace spaces with + and other special characters as in RFC1738

也就是说在URI的RFC规范中,没有定义 + 号表示空格,是指说了 + 可以在path和query中存在。而HTTP规范中,在参数编码时,提到了空格编码为 + 号,后者被大规模接受了。

而Java的URI来看起来是是完全按照URI的RFC规范来实现,其在编码各个component时,只有不允许存在的字符才会进行编码, + 是允许存在的,所以都没有进行编码。这样带来的问题是,query中的+号没有被编码,导致访问时异常。

URIBuilder的编码,在编码却query时是按照 application/x-www-form-urlencoded 规则进行的,所以编码出的地址是比较正确的。

回头看另外一个问题,就是常识中,我们认为 URI中+号表示空格 ,但是其实path中的 + 号表示加号本身。这个就麻烦了,因为很多时候,我们在进行Java web编码的时候,会使用 URLEncoderURLDecoder 类来处理uri的path,这两个类使用的是 application/x-www-form-urlencoded 规则,会用 + 号表示空格,这样在处理path时就不正确了。

细节好多,感觉分分钟都能踩坑,更别提很多人对URI编码没有概念,代码中大量存在用字符串拼接的方式拼接URI,这种方式的结果就是,带来大量的BUG。。。

但是即使是S3,也没有正确处理好URI。。。这个让我非常意外,在AWS的论坛( 帖子地址 )上有人提到S3对path中的 + 号是按照空格处理的!我试了一下还真是。。

# curl -i "https://xx.s3-eu-west-1.amazonaws.com/1+%2B2.txt"
HTTP/1.1 200 OK

# curl -i "https://xx.s3-eu-west-1.amazonaws.com/1%20%2B2.txt"
HTTP/1.1 200 OK

# curl -i "https://xx.s3-eu-west-1.amazonaws.com/1%2B%2B2.txt"
HTTP/1.1 403 Forbidden

在这个帖子中,有AWS的员工回复,表示的确有这个问题,但是因为有大量的这种地址存在了,所以不会去修改。。。

原文  http://imushan.com/2019/08/15/network/URI中plus号的坑/
正文到此结束
Loading...