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");






展开阅读全文

没有更多推荐了,返回首页