廢話不說,真接上代碼。
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");