aboutsummaryrefslogtreecommitdiffstats
path: root/netx
diff options
context:
space:
mode:
authorAdam Domurad <[email protected]>2013-02-14 15:48:21 -0500
committerAdam Domurad <[email protected]>2013-02-14 15:48:21 -0500
commit59d2d03b074c70cc9c771d4787353726b1426d91 (patch)
tree8eb9cec30b1d95251b8cc9dab783530e825f582a /netx
parent5f81c444fcb54e316999146db197bfe2d1aaaaef (diff)
Fix PR835, use HEAD requests to query if resource URL is valid
Diffstat (limited to 'netx')
-rw-r--r--netx/net/sourceforge/jnlp/cache/ResourceTracker.java83
-rw-r--r--netx/net/sourceforge/jnlp/util/StreamUtils.java58
2 files changed, 124 insertions, 17 deletions
diff --git a/netx/net/sourceforge/jnlp/cache/ResourceTracker.java b/netx/net/sourceforge/jnlp/cache/ResourceTracker.java
index 849bafa..898594f 100644
--- a/netx/net/sourceforge/jnlp/cache/ResourceTracker.java
+++ b/netx/net/sourceforge/jnlp/cache/ResourceTracker.java
@@ -35,7 +35,9 @@ import java.net.URLDecoder;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarOutputStream;
import java.util.jar.Pack200;
@@ -47,6 +49,7 @@ import net.sourceforge.jnlp.Version;
import net.sourceforge.jnlp.event.DownloadEvent;
import net.sourceforge.jnlp.event.DownloadListener;
import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.util.StreamUtils;
import net.sourceforge.jnlp.util.WeakList;
/**
@@ -567,7 +570,7 @@ public class ResourceTracker {
if (threads < maxThreads) {
threads++;
- Thread thread = new Thread(new Downloader());
+ Thread thread = new Thread(new Downloader(), "DownloaderThread" + threads);
thread.start();
}
}
@@ -793,6 +796,12 @@ public class ResourceTracker {
// connect
URL finalLocation = findBestUrl(resource);
+
+ if (finalLocation == null) {
+ System.err.println("Attempted to download " + resource.location + ", but failed to connect!");
+ throw new NullPointerException("finalLocation == null"); // Caught below
+ }
+
resource.setDownloadLocation(finalLocation);
URLConnection connection = finalLocation.openConnection(); // this won't change so should be okay unsynchronized
connection.addRequestProperty("Accept-Encoding", "pack200-gzip, gzip");
@@ -851,12 +860,44 @@ public class ResourceTracker {
entry.unlock();
}
}
+ /**
+ * Connects to the given URL, and grabs a response code if the URL uses
+ * the HTTP protocol, or returns an arbitrary valid HTTP response code.
+ *
+ * @return the response code if HTTP connection, or HttpURLConnection.HTTP_OK if not.
+ * @throws IOException
+ */
+ private static int getUrlResponseCode(URL url, Map<String, String> requestProperties, String requestMethod) throws IOException {
+ URLConnection connection = url.openConnection();
+
+ for (Map.Entry<String, String> property : requestProperties.entrySet()){
+ connection.addRequestProperty(property.getKey(), property.getValue());
+ }
+
+ if (connection instanceof HttpURLConnection) {
+ HttpURLConnection httpConnection = (HttpURLConnection)connection;
+ httpConnection.setRequestMethod(requestMethod);
+
+ int responseCode = httpConnection.getResponseCode();
+
+ /* Fully consuming current request helps with connection re-use
+ * See http://docs.oracle.com/javase/1.5.0/docs/guide/net/http-keepalive.html */
+ StreamUtils.consumeAndCloseInputStream(httpConnection.getInputStream());
+
+ return responseCode;
+ }
+
+ return HttpURLConnection.HTTP_OK /* return a valid response code */;
+ }
+
/**
- * Returns the best URL to use for downloading the resource
+ * Returns the 'best' valid URL for the given resource.
+ * This first adjusts the file name to take into account file versioning
+ * and packing, if possible.
*
* @param resource the resource
- * @return a URL or null
+ * @return the best URL, or null if all failed to resolve
*/
private URL findBestUrl(Resource resource) {
DownloadOptions options = downloadOptions.get(resource);
@@ -869,29 +910,37 @@ public class ResourceTracker {
System.err.println("All possible urls for " +
resource.toString() + " : " + urls);
}
- URL bestUrl = null;
+
for (URL url : urls) {
try {
- URLConnection connection = url.openConnection();
- connection.addRequestProperty("Accept-Encoding", "pack200-gzip, gzip");
- if (connection instanceof HttpURLConnection) {
- HttpURLConnection con = (HttpURLConnection)connection;
- int responseCode = con.getResponseCode();
- if (responseCode == -1 || responseCode < 200 || responseCode >= 300) {
- continue;
+ Map<String, String> requestProperties = new HashMap<String, String>();
+ requestProperties.put("Accept-Encoding", "pack200-gzip, gzip");
+
+ int responseCode = getUrlResponseCode(url, requestProperties, "HEAD");
+
+ if (responseCode == HttpURLConnection.HTTP_NOT_IMPLEMENTED ) {
+ System.err.println("NOTE: The server does not appear to support HEAD requests, falling back to GET requests.");
+ /* Fallback: use GET request in the rare case the server does not support HEAD requests */
+ responseCode = getUrlResponseCode(url, requestProperties, "GET");
+ }
+
+ /* Check if within valid response code range */
+ if (responseCode >= 200 && responseCode < 300) {
+ if (JNLPRuntime.isDebug()) {
+ System.err.println("best url for " + resource.toString() + " is " + url.toString());
}
+ return url; /* This is the best URL */
}
+ } catch (IOException e) {
+ // continue to next candidate
if (JNLPRuntime.isDebug()) {
- System.err.println("best url for " + resource.toString() + " is " + url.toString());
+ System.err.println("While processing " + url.toString() + " for resource " + resource.toString() + " got " + e);
}
- bestUrl = url;
- break;
- } catch (IOException e) {
- // continue
}
}
- return bestUrl;
+ /* No valid URL, return null */
+ return null;
}
/**
diff --git a/netx/net/sourceforge/jnlp/util/StreamUtils.java b/netx/net/sourceforge/jnlp/util/StreamUtils.java
new file mode 100644
index 0000000..ed92fb6
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/util/StreamUtils.java
@@ -0,0 +1,58 @@
+/*
+Copyright (C) 2013 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+ */
+
+package net.sourceforge.jnlp.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class StreamUtils {
+
+ /**
+ * Ensure a stream is fully read, required for correct behaviour in some
+ * APIs, namely HttpURLConnection.
+ * @throws IOException
+ */
+ public static void consumeAndCloseInputStream(InputStream in) throws IOException {
+ byte[] throwAwayBuffer = new byte[256];
+ while (in.read(throwAwayBuffer) > 0) {
+ /* ignore contents */
+ }
+ in.close();
+ }
+
+} \ No newline at end of file