我出息了,给 JDK 上报了一个 BUG,mongodb 入门到精通
我们的项目依赖于一个外部服务,该外部服务提供 REST 接口供我方调用,这是很常见的一个场景。本地和测试环境测试都没有问题,一切就绪上了生产后,程序调用接口就总是网络不通。 需要说明的是本地、测试环境、生产环境通过不同的域名访问该外部服务。生产程序调用不通,神奇的是在生产环境通过?`curl`?等命令却能够正常调用对方接口。 ![](https://static001.geekbang.org/infoq/0d/0dbd6fd6fa9fc61d38aa0cfce3e5231a.gif) 这 TM 就神奇了,唯一不同的就是发起 HTTP 请求的客户端了,估计就是 http 客户端有问题了?通过最后排查发现,居然发现了一枚 “JDK 的 bug”,然后我就提交到了 JDK 的官网…… ![](https://static001.geekbang.org/infoq/6d/6d25bc4189a83333aeda6ff0c237c8b4.png) 下面我们就来重现一下这个问题。 []( )server 端准备 ----------------------------------------------------------------------------- 这里用 Nginx 模拟了一下 上文提到的 REST 服务,假设调用正常返回?`"Hello, World\n ``` 【一线大厂 Java 面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】 浏览器打开:qq.cn.hn/FTf 免费领取 ``` "`,Nginx 配置如下: server { listen 80; server_name test_1.tanglei.name; location /testurl { add_header Content-Type 'text/plain; charset=utf-8'; return 200 "Hello, World\n"; } } []( )不同的 client 请求 -------------------------------------------------------------------------------- 下面用不同的 Http client (分别用命令行`curl`,python 的?`requests`包,和 Java 的 URL 等尝试)去请求。 * `curl`?请求,正常。 [root@VM_77_245_centos vhost]# curl -i "http://test_1.tanglei.name/testurl" HTTP/1.1 200 OK Server: nginx Content-Length: 13 Connection: keep-alive Content-Type: text/plain; charset=utf-8 Hello, World [root@VM_77_245_centos vhost]# * `python requests`?正常。 >>> import requests >>> r = requests.get("http://test_1.tanglei.name/testurl") >>> r.text u'Hello, World\n' * Java 的?`java.net.URLConnection`?同样正常。 static String getContent(java.net.URL url) throws Exception { java.net.URLConnection conn = url.openConnection(); java.io.InputStreamReader in = new java.io.InputStreamReader(conn.getInputStream(), "utf-8"); java.io.BufferedReader reader = new java.io.BufferedReader(in); StringBuilder sb = new StringBuilder(); int c = -1; while ((c = reader.read()) != -1) { sb.append((char)c); } reader.close(); in.close(); String response = sb.toString(); return response; } 上面的这个方法?`String getContent(java.net.URL url)`?传入一个构造好的?`java.net.URL`?然后 get 请求,并以?`String`?方式返回 response。 String srcUrl = "http://test_1.tanglei.name/testurl"; java.net.URL url = new java.net.URL(srcUrl); System.out.println("\nurl result:\n" + getContent(url)); // OK 上面的语句输出正常,结果如下: url result: Hello, World 这就尼玛神奇了吧。看看我们程序中用的 httpclient 的实现,结果发现是有用?`java.net.URI`,心想,这不至于吧,用 URI 就不行了么。 ![](https://static001.geekbang.org/infoq/9b/9be2cb387d45519106fd512505280a86.png) 换?`java.net.URI`?试试? (这里不展开讲 URL 和 URI 的区别联系了,可以简单的认为 URL 是 URI 的一个子集,详细的可参考?URI、URL 和 URN\[1\],?wiki URI\[2\]) 直接通过`java.net.URI`构造,再调用?`URI.toURL`?得到?`URL`,调用同样正常。 关键的来了,httpclient 源码中用的构造函数是另外一个: URI(String scheme, String host, String path, String fragment) Constructs a hierarchical URI from the given components. 我用这个方法构造`URI`,会构造失败: new java.net.URI(uri.getScheme(), uri.getHost(), uri.getPath(), null) error: protocol = http host = null new java.net.URI(url.getProtocol(), url.getHost(), url.getPath(), null) error: Illegal character in hostname at index 11: http://test_1.tanglei.name/testurl
评论