--- a/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Wed May 15 00:36:03 2013 -0400
+++ b/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Wed May 15 15:01:59 2013 +0100
@@ -36,6 +36,7 @@
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.net.SocketTimeoutException;
+import java.net.SocketPermission;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URI;
@@ -45,8 +46,13 @@
import java.net.CacheResponse;
import java.net.SecureCacheResponse;
import java.net.CacheRequest;
+import java.net.HttpURLPermission;
import java.net.Authenticator.RequestorType;
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
+import java.security.PrivilegedActionException;
import java.io.*;
+import java.net.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
@@ -303,6 +309,19 @@
*/
private MessageHeader requests;
+ /* The headers actually set by the user are recorded here also
+ */
+ private MessageHeader userHeaders;
+
+ /* Headers and request method cannot be changed
+ * once this flag is set in :-
+ * - getOutputStream()
+ * - getInputStream())
+ * - connect()
+ * Access synchronized on this.
+ */
+ private boolean connecting = false;
+
/* The following two fields are only used with Digest Authentication */
String domain; /* The list of authentication domains */
DigestAuthentication.Parameters digestparams;
@@ -370,6 +389,9 @@
private int connectTimeout = NetworkClient.DEFAULT_CONNECT_TIMEOUT;
private int readTimeout = NetworkClient.DEFAULT_READ_TIMEOUT;
+ /* A permission converted from a HttpURLPermission */
+ private SocketPermission socketPermission;
+
/* Logging support */
private static final PlatformLogger logger =
PlatformLogger.getLogger("sun.net.www.protocol.http.HttpURLConnection");
@@ -487,6 +509,14 @@
}
}
+ public synchronized void setRequestMethod(String method)
+ throws ProtocolException {
+ if (connecting) {
+ throw new IllegalStateException("connect in progress");
+ }
+ super.setRequestMethod(method);
+ }
+
/* adds the standard key/val pairs to reqests if necessary & write to
* given PrintStream
*/
@@ -729,6 +759,7 @@
super(u);
requests = new MessageHeader();
responses = new MessageHeader();
+ userHeaders = new MessageHeader();
this.handler = handler;
instProxy = p;
if (instProxy instanceof sun.net.ApplicationProxy) {
@@ -849,6 +880,9 @@
// overridden in HTTPS subclass
public void connect() throws IOException {
+ synchronized (this) {
+ connecting = true;
+ }
plainConnect();
}
@@ -867,10 +901,86 @@
return false;
}
+ private String getHostAndPort(URL url) {
+ String host = url.getHost();
+ int port = url.getPort();
+ if (port == -1) {
+ String scheme = url.getProtocol();
+ if ("http".equals(scheme)) {
+ return host + ":80";
+ } else { // scheme must be https
+ return host + ":443";
+ }
+ }
+ return host + ":" + Integer.toString(port);
+ }
+
protected void plainConnect() throws IOException {
- if (connected) {
- return;
+ synchronized (this) {
+ if (connected) {
+ return;
+ }
+ }
+ SocketPermission p = URLtoSocketPermission(this.url);
+ if (p != null) {
+ try {
+ AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Void>() {
+ public Void run() throws IOException {
+ plainConnect0();
+ return null;
+ }
+ }
+// }, null, p -- replace line above, when limited doPriv ready
+ );
+ } catch (PrivilegedActionException e) {
+ throw (IOException) e.getException();
+ }
+ } else {
+ // run without additional permission
+ plainConnect0();
}
+ }
+
+ /**
+ * if the caller has a HttpURLPermission for connecting to the
+ * given URL, then return a SocketPermission which permits
+ * access to that destination. Return null otherwise. The permission
+ * is cached in a field (which can only be changed by redirects)
+ */
+ SocketPermission URLtoSocketPermission(URL url) throws IOException {
+
+ if (socketPermission != null) {
+ return socketPermission;
+ }
+
+ SecurityManager sm = System.getSecurityManager();
+
+ if (sm == null) {
+ return null;
+ }
+
+ // the permission, which we might grant
+
+ SocketPermission newPerm = new SocketPermission(
+ getHostAndPort(url), "connect"
+ );
+
+ String actions = getRequestMethod()+":" +
+ getUserSetHeaders().getHeaderNamesInList();
+
+ HttpURLPermission p = new HttpURLPermission(url.toString(), actions);
+ try {
+ sm.checkPermission(p);
+ socketPermission = newPerm;
+ return socketPermission;
+ } catch (SecurityException e) {
+ // fall thru
+ }
+ return null;
+ }
+
+ protected void plainConnect0() throws IOException {
// try to see if request can be served from local cache
if (cacheHandler != null && getUseCaches()) {
try {
@@ -1068,7 +1178,28 @@
@Override
public synchronized OutputStream getOutputStream() throws IOException {
-
+ connecting = true;
+ SocketPermission p = URLtoSocketPermission(this.url);
+
+ if (p != null) {
+ try {
+ return AccessController.doPrivileged(
+ new PrivilegedExceptionAction<OutputStream>() {
+ public OutputStream run() throws IOException {
+ return getOutputStream0();
+ }
+ }
+// }, null, p -- replace line above, when limited doPriv ready
+ );
+ } catch (PrivilegedActionException e) {
+ throw (IOException) e.getException();
+ }
+ } else {
+ return getOutputStream0();
+ }
+ }
+
+ private synchronized OutputStream getOutputStream0() throws IOException {
try {
if (!doOutput) {
throw new ProtocolException("cannot write to a URLConnection"
@@ -1231,8 +1362,30 @@
}
@Override
+ public synchronized InputStream getInputStream() throws IOException {
+ connecting = true;
+ SocketPermission p = URLtoSocketPermission(this.url);
+
+ if (p != null) {
+ try {
+ return AccessController.doPrivileged(
+ new PrivilegedExceptionAction<InputStream>() {
+ public InputStream run() throws IOException {
+ return getInputStream0();
+ }
+ }
+// }, null, p -- replace line above, when limited doPriv ready
+ );
+ } catch (PrivilegedActionException e) {
+ throw (IOException) e.getException();
+ }
+ } else {
+ return getInputStream0();
+ }
+ }
+
@SuppressWarnings("empty-statement")
- public synchronized InputStream getInputStream() throws IOException {
+ private synchronized InputStream getInputStream0() throws IOException {
if (!doInput) {
throw new ProtocolException("Cannot read from URLConnection"
@@ -2319,18 +2472,19 @@
return false;
}
- int stat = getResponseCode();
+ final int stat = getResponseCode();
if (stat < 300 || stat > 307 || stat == 306
|| stat == HTTP_NOT_MODIFIED) {
return false;
}
- String loc = getHeaderField("Location");
+ final String loc = getHeaderField("Location");
if (loc == null) {
/* this should be present - if not, we have no choice
* but to go forward w/ the response we got
*/
return false;
}
+
URL locUrl;
try {
locUrl = new URL(loc);
@@ -2342,6 +2496,38 @@
// treat loc as a relative URI to conform to popular browsers
locUrl = new URL(url, loc);
}
+
+ final URL locUrl0 = locUrl;
+ socketPermission = null; // force recalculation
+ SocketPermission p = URLtoSocketPermission(locUrl);
+
+ if (p != null) {
+ try {
+ return AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Boolean>() {
+ public Boolean run() throws IOException {
+ return followRedirect0(loc, stat, locUrl0);
+ }
+ }
+// }, null, p -- replace line above, when limited doPriv ready
+ );
+ } catch (PrivilegedActionException e) {
+ throw (IOException) e.getException();
+ }
+ } else {
+ // run without additional permission
+ return followRedirect0(loc, stat, locUrl);
+ }
+ }
+
+ /* Tells us whether to follow a redirect. If so, it
+ * closes the connection (break any keep-alive) and
+ * resets the url, re-connects, and resets the request
+ * property.
+ */
+ private boolean followRedirect0(String loc, int stat, URL locUrl)
+ throws IOException
+ {
disconnectInternal();
if (streaming()) {
throw new HttpRetryException (RETRY_MSG3, stat, loc);
@@ -2753,17 +2939,24 @@
* @param value the value to be set
*/
@Override
- public void setRequestProperty(String key, String value) {
- if (connected)
+ public synchronized void setRequestProperty(String key, String value) {
+ if (connected || connecting)
throw new IllegalStateException("Already connected");
if (key == null)
throw new NullPointerException ("key is null");
if (isExternalMessageHeaderAllowed(key, value)) {
requests.set(key, value);
+ if (!key.equalsIgnoreCase("Content-Type")) {
+ userHeaders.set(key, value);
+ }
}
}
+ MessageHeader getUserSetHeaders() {
+ return userHeaders;
+ }
+
/**
* Adds a general request property specified by a
* key-value pair. This method will not overwrite
@@ -2776,14 +2969,17 @@
* @since 1.4
*/
@Override
- public void addRequestProperty(String key, String value) {
- if (connected)
+ public synchronized void addRequestProperty(String key, String value) {
+ if (connected || connecting)
throw new IllegalStateException("Already connected");
if (key == null)
throw new NullPointerException ("key is null");
if (isExternalMessageHeaderAllowed(key, value)) {
requests.add(key, value);
+ if (!key.equalsIgnoreCase("Content-Type")) {
+ userHeaders.add(key, value);
+ }
}
}