JDK的HttpURLConnection調用setRequestProperty失敗的原因走查

廢話不說,真接上代碼。

HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Content-Length", "0");

中的第二句沒有起作用(發出去的http頭裏,未含有Content-Length字段)。

但是這個代碼在android平臺上跑的時候,第二句是起了作用的。


爲了找出原因,真接跟到源代碼中看:

 public void setRequestProperty(String key, String value) {
	if (connected)
            throw new IllegalStateException("Already connected");
	if (key == null)
            throw new NullPointerException ("key is null");

	if (isExternalMessageHeaderAllowed(key, value)) {
	    requests.set(key, value);
	}
    }

 private boolean isExternalMessageHeaderAllowed(String key, String value) {
        checkMessageHeader(key, value);
	if (!isRestrictedHeader(key, value)) {
	    return true;
	}
	return false;
    }

 private boolean isRestrictedHeader(String key, String value) {
	if (allowRestrictedHeaders) {
	    return false;
	}

	key = key.toLowerCase();
	if (restrictedHeaderSet.contains(key)) {
	    /*
	     * Exceptions to restricted headers:
	     *
	     * Allow "Connection: close".
	     */
	    if (key.equals("connection") && value.equalsIgnoreCase("close")) {
	        return false;
	    }
	    return true;
	} else if (key.startsWith("sec-")) {
	    return true;
	}
	return false;
    }

由上面代碼可以看出,一個屬性能不能加到request中,由allowRestrictedHeaders和restrictedHeaderSet決定的。如果key在restrictedHeaderSet,而且allowRestrictedHeaders爲false時,這個key的值是不能加到request中的。

繼續來看restrictedHeaderSet和allowRestrictedHeaders是哪裏來的。在靜態初始化代碼中發現allowRestrictedHeaders是由安全管理器的返回值決定的。

restrictedHeaderSet的內容是由restrictedHeaders數組決定的。


allowRestrictedHeaders = ((Boolean)java.security.AccessController.doPrivileged(
		new sun.security.action.GetBooleanAction(
		    "sun.net.http.allowRestrictedHeaders"))).booleanValue();
	if (!allowRestrictedHeaders) {
	    restrictedHeaderSet = new HashSet<String>(restrictedHeaders.length);
	    for (int i=0; i < restrictedHeaders.length; i++) {
	        restrictedHeaderSet.add(restrictedHeaders[i].toLowerCase());
	    }
	} else {
	    restrictedHeaderSet = null;
	}

數組的聲明如下:


 private static final Set<String> restrictedHeaderSet;
    private static final String[] restrictedHeaders = {
	/* Restricted by XMLHttpRequest2 */
	//"Accept-Charset",
	//"Accept-Encoding",
	"Access-Control-Request-Headers",
	"Access-Control-Request-Method",
	"Connection", /* close is allowed */
	"Content-Length",
	//"Cookie",
	//"Cookie2",
	"Content-Transfer-Encoding",
	//"Date",
	"Expect",
	"Host",
	"Keep-Alive",
	"Origin",
	// "Referer", 
	// "TE",
	"Trailer",
	"Transfer-Encoding",
	"Upgrade",
	//"User-Agent",
	"Via"
    };

由此可以看出Content-Length,Host,Transfer-Encoding等都是加不進去的。

但這是爲什麼呢?又應該如何解決呢?

於是搜索了下。

找到下面兩篇。

http://stackoverflow.com/questions/6056125/why-does-content-length-http-header-field-use-a-value-other-than-the-one-given-i/6056230#6056230

http://lxy2330.iteye.com/blog/1882836

大概原因就是出於安全考慮,這些字段儘量不要被改動。sun就做了這一套安全機制。

解決方法是在JVM啓動時加入sun.net.http.allowRestrictedHeaders屬性爲true。

按第二篇裏的做法,是失敗的。

試着的JVM啓動參數裏面加,但失敗了。

於是我就直接在程序裏面加了。如下面代碼。然後就OK了。

System.setProperty("sun.net.http.allowRestrictedHeaders", "true");






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章