CSDN|给 JDK 报了一个P4 的 Bug,结果居然……


CSDN|给 JDK 报了一个P4 的 Bug,结果居然……
本文插图
作者 |码农唐磊
背景分享一下之前踩的一个坑 , 背景是这样的:
我们的项目依赖于一个外部服务 , 该外部服务提供 REST 接口供我方调用 , 这是很常见的一个场景 。 本地和测试环境测试都没有问题 , 一切就绪上了生产后 , 程序调用接口就总是网络不通 。
需要说明的是本地、测试环境、生产环境通过不同的域名访问该外部服务 。 生产程序调用不通 , 神奇的是在生产环境通过 curl 等命令却能够正常调用对方接口 。这就神奇了 , 唯一不同的就是发起 HTTP 请求的客户端了 , 估计就是 http客户端有问题了?通过最后排查发现 , 居然发现了一枚 “JDK 的 bug” , 然后石头就提交到了 JDK 的官网……CSDN|给 JDK 报了一个P4 的 Bug,结果居然……
本文插图
下面我们就来重现一下这个问题 。
server端准备
这里用 Nginx 模拟了一下 上文提到的 REST 服务 , 假设调用正常返回 "Hello, World\n" , Nginx 配置如下:1server {2 listen 80;3 server_name test_1.tanglei.name;4 location /testurl {5 add_header Content-Type 'text/plain; charset=utf-8';6 return 200 "Hello, World\n";7 }8}
不同的client请求下面用不同的 Http client (分别用命令行curl , python的requests包 , 和 Java 的 URL等尝试)去请求 。
curl请求 , 正常 。1[root@VM_77_245_centos vhost]# curl -i "http://test_1.tanglei.name/testurl"2HTTP/1.1 200 OK3Server: nginx4Content-Length: 135Connection: keep-alive6Content-Type: text/plain; charset=utf-88Hello, World9#
python requests 正常1>>> import requests2>>> r = requests.get("http://test_1.tanglei.name/testurl")3>>> r.text4u'Hello, World\n'
Java 的java.net.URLConnection同样正常 。1static String getContent(java.net.URL url) throws Exception {2 java.net.URLConnection conn = url.openConnection;3 java.io.InputStreamReader in = new java.io.InputStreamReader(conn.getInputStream, "utf-8");4 java.io.BufferedReader reader = new java.io.BufferedReader(in); 5 StringBuilder sb = new StringBuilder;6 int c = -1;7 while ((c = reader.read) != -1) {8 sb.append((char)c);9 }10 reader.close;11 in.close;12 String response = sb.toString;13 return response;14}
上面的这个方法String getContent(java.net.URL url) 传入一个构造好的 java.net.URL然后 get 请求 , 并以String方式返回 response 。
1String srcUrl = "http://test_1.tanglei.name/testurl";2java.net.URL url = new java.net.URL(srcUrl);3System.out.println("\nurl result:\n" + getContent(url)); // OK
上面的语句输出正常 , 结果如下:
1url result:2Hello, World
这就神奇了吧 。 看看我们程序中用的 httpclient 的实现 , 结果发现是有用java.net.URI , 心想 , 这不至于吧 , 用 URI 就不行了么 。换 java.net.URI 试试? (这里不展开讲URL和URI的区别联系了 , 可以简单的认为URL是URI的一个子集 , 详细的可参考 URI、URL 和 URN[1], wiki URI[2])
直接通过 java.net.URI 构造 , 再调用 URI.toURL 得到 URL , 调用同样正常 。
关键的来了 , httpclient 源码中用的构造函数是另外一个:
1URI(String scheme, String host, String path, String fragment)2Constructs a hierarchical URI from the given components.
我用这个方法构造URI , 会构造失败:1new java.net.URI(uri.getScheme, uri.getHost, uri.getPath, null) error: protocol = http host = null2new java.net.URI(url.getProtocol, url.getHost, url.getPath, null) error: Illegal character in hostname at index 11: http://test_1.tanglei.name/testurl


推荐阅读