天天看點

關于新浪微網誌SDK在代理服務網絡環境中遇到的問題 weibo4j.model.MySSLSocketFactory.createSocket

最近客戶在使用我們的新浪微網誌應用的時候突然不能使用了,背景抛出的異常如下:

Caused by: java.net.SocketTimeoutException: connect timed out at java.net.PlainSocketImpl.socketConnect(Native Method) at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333) at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195) at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182) at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366) at java.net.Socket.connect(Socket.java:525) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:550) at weibo4j.model.MySSLSocketFactory.createSocket(MySSLSocketFactory.java:88) at org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:706) at org.apache.commons.httpclient.MultiThreadedHttpConnectionManager$HttpConnectionAdapter.open(MultiThreadedHttpConnectionManager.java:1321) at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:386) at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:170) at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:396) atorg.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:324) at weibo4j.http.HttpClient.httpRequest(HttpClient.java:397)

經過跟客戶溝通發現,他們的環境配置了Apache反向代理,但是公司内部也配置了Apache反向代理,微網誌應用可以正常使用,是以排除反向代理的問題;進一步溝通發現,客戶的網絡環境配置的代理伺服器,且新浪、網易、騰訊平台的微網誌應用均不能正常使用了。

開始以為是微網誌平台的SDK不支援代理伺服器,但是檢視SourceCode發現,在新浪的weibo4j.model.Configuration類中,有幾行注釋的代碼:

 //defaultProperty.setProperty("weibo4j.https.proxyHost","");

        //defaultProperty.setProperty("weibo4j.https.proxyUser","");         //defaultProperty.setProperty("weibo4j.https.proxyPassword","");         //defaultProperty.setProperty("weibo4j.https.proxyPort","");   推測SDK是支援代理伺服器的。而且在weibo4j.http.HttpClient的構造方法中,存在 // 支援proxy         if (proxyHost != null && !proxyHost.equals("")) { ………… } 的判斷。     是以将Configuration中代理的注釋去掉,并把屬性配置上參數值,重新通路仍然報異常! 然後跟蹤代碼發現weibo4j.http.HttpClient.httpRequest(HttpClient.java:397)地方,新浪的實作為: client.executeMethod(method); 追蹤Apache Commons-HttpClient的源代碼發現: public int executeMethod(HttpMethod method)         throws IOException, HttpException  {                      LOG.trace("enter HttpClient.executeMethod(HttpMethod)");         // execute this method and use its host configuration, if it has one         return executeMethod(null, method, null);     } public int executeMethod(final HostConfiguration hostConfiguration, final HttpMethod method)         throws IOException, HttpException {              LOG.trace("enter HttpClient.executeMethod(HostConfiguration,HttpMethod)");           return executeMethod(hostConfiguration, method, null);      }

但是新浪使用client.executeMethod(method),而代理是在HostConfiguration的ProxyHost中設定的,這裡把代理資訊給丢掉了!!!

是以需要修改為: if(getProxyHost() != null && getProxyHost().length() > 0){    log.debug("HOSTCONFIGURATION: " + client.getHostConfiguration().toString());    client.executeMethod(client.getHostConfiguration(), method); }else{    client.executeMethod(method); }

重新部署應用後再次通路仍然報異常!瘋了!!!

追蹤了半天Apache Commons-HttpClient的源代碼,在要撞南牆的時候終于發現問題:   新浪SDK OAuth2.0版本采用的是HTTPS協定,并且在weibo4j.http.HttpClient的構造方法中注冊了HTTPS的SSLSocketFactory  Protocol myhttps = new Protocol("https", new MySSLSocketFactory(), 443);         Protocol.registerProtocol("https", myhttps); 但是在代理伺服器注冊的部分,采用的方法為: client.getHostConfiguration().setProxy(proxyHost, proxyPort);

親,追蹤了半天Apache Commons-HttpClient的源代碼才發現,setProxy注冊的是HTTP協定好麼!!!org.apache.commons.httpclient.HostConfiguration中         public synchronized void setProxy(final String proxyHost, int proxyPort) {         this.proxyHost = new ProxyHost(proxyHost, proxyPort);      }

而ProxyHost是這樣構造的:     public ProxyHost(final String hostname, int port) {         super(hostname, port, Protocol.getProtocol("http"));     } 是HTTP好麼!!!怎麼拿HTTP和HTTPS去通信呢。。。。讓一點都不懂HttpClient的我搞得做夢都是異常堆棧裡的調用關系啊!新浪你坑爹啊!

最後,把這個修改為: //使用預設的setProxy端口,注冊的為ProxyHost,此方式注冊的為HTTP協定,無法與HTTPS協定通信,将會報錯 //新浪OAuth協定采用HTTPS協定,代理請求也必須為HTTPS協定             final String proxyHostName = proxyHost;             final HttpHost proxyHost = new HttpHost(proxyHostName, proxyPort, new Protocol("https", new MySSLSocketFactory(), proxyPort));

OK了!接着搞騰訊和網易去!根據經驗,騰訊的SDK更坑爹啊!!!淚奔ING。。。

繼續閱讀