给JDK报了一个P4的Bug,结果居然……
點擊上方?好好學java?,選擇?星標?公眾號
重磅資訊、干貨,第一時間送達
今日推薦:騰訊推出高性能 RPC 開發框架
個人原創100W+訪問量博客:點擊前往,查看更多
作者:程序猿石頭/碼農唐磊(本文來自作者投稿)
背景
分享一下之前踩的一個坑,背景是這樣的:
我們的項目依賴于一個外部服務,該外部服務提供 REST 接口供我方調用,這是很常見的一個場景。本地和測試環境測試都沒有問題,一切就緒。上了生產后,程序調用接口就總是網絡不通。
需要說明的是本地、測試環境、生產環境通過不同的域名訪問該外部服務。生產程序調用不通,神奇的是在生產環境通過 curl 等命令卻能夠正常調用對方接口。
這 TM 就神奇了,唯一不同的就是發起 HTTP 請求的客戶端了,估計就是 HTTP 客戶端有問題了?通過最后排查發現,居然發現了一枚 “JDK 的 bug”,然后石頭就提交到了 JDK 的官網……
下面我們就來重現一下這個問題。
Server 端準備
這里用 Nginx 模擬了一下 上文提到的 REST 服務,假設調用正常返回?"Hello, World\n",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 請求,正常。
Python requests 正常。
Java 的 java.net.URLConnection 同樣正常。
上面的這個方法 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 就不行了么。
換?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所以問題發現了,我們的項目中依賴的第三方 httpclient 包底層用到了 java.net.URI,恰好在? java.net.URI 中是不允許以下劃線 (_) 作為 hostname 字段的。
即 uri.getHost()?和 uri.toURL().getHost()?居然能不相等。
這是 JDK 的 Bug 吧?
有理由懷疑,這是 JDK 的 Bug 吧?
從官網上還真找到了關于包含下劃線作為 hostname 的 bug 提交 issue。戳這里 JDK-8132508 : Bug JDK-8029354 reproduces with underscore in hostname[3],然后發現該 "bug" reporter 的情況貌似跟我的差不多,只不過引爆 bug 的點不一樣。
該 "bug" reviewer 最后以 "Not an Issue" 關閉,給出的理由是:
RFC?952?disallows?_?underscores?in?hostnames.?So,?this?is?not?a?bug.
確實,rfc952[4]?明確明確說了域名只能由字母?(A-Z)、數字(0-9)、減號?(-)?和點?(.)?組成。
那 OK 吧,既然明確規定了 hostname 不能包含下劃線,為啥 java.net.URL 卻允許呢?
造成 java.net.URI 和 java.net.URL 在處理 hostname 時的標準不一致,且本身 java.net.URI 在構造的時候也帶了?"有色"眼鏡,同一個URL字符串 通過靜態方法 java.net.URI.create(String)?或者通過帶1個參數的構造方法 java.net.URI(String)?都能成功構造出 URI 的實例,但通過帶4個參數的構造方法就不能構造了。
要知道,在 coding 過程中,盡早反饋異常信息更有利于軟件開發持續迭代的過程。我們在開發過程中也應該遵循這一點原則。
于是我就去 JDK 官網提交了一個 bug,大意是說 java.net.URI 和 java.net.URL 在處理 hostname 的時候標準不一致,容易使開發人員埋藏一些潛在的 bug,同時也還把這個問題反饋到 stackoverflow[5]?了。
I am wondering, if hostname with underscore is not valid, why the result is differrent between java.net.URI and java.net.URL? Is it a bug or a feature? Here is the example.
java.net.URL url = new java.net.URL("http://test_1.tanglei.name");?
System.out.println(url.getHost()); //test_1.tanglei.name?
java.net.URI uri = new java.net.URI("http://test_1.tanglei.name");?
System.out.println(uri.getHost()); //null
這個 JDK bug issue 詳細信息見 JDK-8170265 : underscore is allowed in java.net.URL while not in java.net.URI[6],openjdk JDK-8170265[7]。
經過初步 Review,被認為是一個 P4 的 Bug。說的是 java.net.URL 遵循的是 RFC 2396 規范,確實不允許含有下劃線的 hostname。java.net.URI 做到了, 而 java.net.URL 沒有做到。
重點來了,然后,卻被另外一個 Reviewer 直接給斃了。給出的原因是 java.net.URL 構造方法中,API 文檔中說了本來也不會做驗證即?No validation of the inputs is performed by this constructor.??在線 API Doc 戳這里[8]?(可以點連接,進去搜索關鍵字 "No validation")。
當初沒有收到及時反饋,就沒有來得及懟回去。
其實就算 "No validation of the inputs is performed by this constructor." 是合理的,里面也只有3個構造函數有這樣的說明,按照這樣的邏輯是不是說另外的構造函數有驗證呢……(示例中的默認的構造函數都沒有說呀)。
這里有 java.net.URL 的源碼[9],感興趣的同學可以看看。
恩,以上就是結論了。
不過,反正我自己感覺目前 Java API 關于這里的設計不太合理,歡迎大家討論。
我在SO提問的這個回答[10]比較有意思,哈哈。
The review is somewhat terse, but the reviewer's point is the URL constructor is behaving in accordance with its specification. Since the specification explicitly states that no validation is performed, this is not a bug in the code. This is indisputable.
What he didn't spell out is that fixing this inconsistency (by changing the URL class specification) would break lots of peoples' 20+ year old code Java code. That would be a really bad idea. It can't happen.
So ... this inconsistency is a "feature".
附本文鏈接
[1] URI、URL 和 URN: https://www.ibm.com/developerworks/cn/xml/x-urlni.html
[2]wiki URI: https://en.wikipedia.org/wiki/Uniform_Resource_Identifier
[3]JDK-8132508 : Bug JDK-8029354 reproduces with underscore in hostname: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8132508
[4]rfc952: https://tools.ietf.org/html/rfc952
[5]stackoverflow: https://stackoverflow.com/questions/44226003/conflicts-between-java-net-url-and-java-net-uri-when-dealing-with-hostname-conta
[6]JDK-8170265 : underscore is allowed in java.net.URL while not in java.net.URI: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8170265
[7]openjdk JDK-8170265: https://bugs.openjdk.java.net/browse/JDK-8170265
[8]在線 api doc 戳這里: https://docs.oracle.com/javase/8/docs/api/java/net/URL.html
[9]這里有java.net.URL 的源碼: http://www.docjar.com/html/api/java/net/URL.java.html
[10]我在SO提問的這個回答: https://stackoverflow.com/questions/44226003/conflicts-between-java-net-url-and-java-net-uri-when-dealing-with-hostname-conta?answertab=active#tab-to
最后,再附上我歷時三個月總結的?Java 面試 + Java 后端技術學習指南,筆者這幾年及春招的總結,github 1.4k star,拿去不謝!下載方式1.?首先掃描下方二維碼 2.?后臺回復「Java面試」即可獲取總結
以上是生活随笔為你收集整理的给JDK报了一个P4的Bug,结果居然……的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 拜托!不要再问我是否了解多线程了好吗
- 下一篇: 终于弄明白 i = i++和 i = +