From 36e1f711ae91dc71dfad26b6adbebbac0b3aee81 Mon Sep 17 00:00:00 2001
From: David Turner <dturner@twitter.com>
Date: Tue, 2 Dec 2014 14:03:24 -0500
Subject: [PATCH] Add timeout support for HTTP requests

Adds a new settings parameter, defaultTimeout, an integer number of
milliseconds.  This timeout is used for both connect, and during the
download.  So if, during the download, more than defaultTimeout
milliseconds have passed between packets, the download will
fail. Fixes IVY-735.

Signed-off-by: David Turner <dturner@twitter.com>
---
 .../org/apache/ivy/core/settings/IvySettings.java  |  4 +++
 .../ivy/core/settings/XmlSettingsParser.java       |  8 +++++
 .../org/apache/ivy/util/url/BasicURLHandler.java   | 38 +++++++++++++++++-----
 .../org/apache/ivy/util/url/HttpClientHandler.java | 23 ++++++++++---
 src/java/org/apache/ivy/util/url/URLHandler.java   |  2 ++
 .../apache/ivy/util/url/URLHandlerDispatcher.java  |  8 +++++
 .../apache/ivy/util/url/URLHandlerRegistry.java    |  3 ++
 7 files changed, 72 insertions(+), 14 deletions(-)

diff --git a/src/java/org/apache/ivy/core/settings/IvySettings.java b/src/java/org/apache/ivy/core/settings/IvySettings.java
index 1ef8dc7..1846c31 100644
--- a/src/java/org/apache/ivy/core/settings/IvySettings.java
+++ b/src/java/org/apache/ivy/core/settings/IvySettings.java
@@ -1255,6 +1255,10 @@ public class IvySettings implements SortEngineSettings, PublishEngineSettings, P
         this.defaultLockStrategy = defaultLockStrategy;
     }
 
+    public synchronized void setDefaultTimeout(int timeout) {
+        URLHandlerRegistry.setTimeout(timeout);
+    }
+
     public synchronized RepositoryCacheManager getDefaultRepositoryCacheManager() {
         if (defaultRepositoryCacheManager == null) {
             defaultRepositoryCacheManager = new DefaultRepositoryCacheManager("default-cache",
diff --git a/src/java/org/apache/ivy/core/settings/XmlSettingsParser.java b/src/java/org/apache/ivy/core/settings/XmlSettingsParser.java
index a40cc1c..e73a4a5 100644
--- a/src/java/org/apache/ivy/core/settings/XmlSettingsParser.java
+++ b/src/java/org/apache/ivy/core/settings/XmlSettingsParser.java
@@ -129,6 +129,8 @@ public class XmlSettingsParser extends DefaultHandler {
 
     private boolean deprecatedMessagePrinted = false;
 
+    private Integer defaultTimeout = null;
+
     public XmlSettingsParser(IvySettings ivy) {
         this.ivy = ivy;
     }
@@ -374,6 +376,9 @@ public class XmlSettingsParser extends DefaultHandler {
 
         // we do not set following defaults here since no instances has been registered yet
         defaultResolver = (String) attributes.get("defaultResolver");
+        String timeoutAttribute = (String) attributes.get("defaultTimeout");
+        if (timeoutAttribute != null)
+            defaultTimeout = Integer.parseInt(timeoutAttribute);
         defaultCM = (String) attributes.get("defaultConflictManager");
         defaultLatest = (String) attributes.get("defaultLatestStrategy");
         defaultCircular = (String) attributes.get("circularDependencyStrategy");
@@ -602,6 +607,9 @@ public class XmlSettingsParser extends DefaultHandler {
         if (defaultResolver != null) {
             ivy.setDefaultResolver(ivy.substitute(defaultResolver));
         }
+        if (defaultTimeout != null) {
+            ivy.setDefaultTimeout(defaultTimeout);
+        }
         if (defaultCM != null) {
             ConflictManager conflictManager = ivy.getConflictManager(ivy.substitute(defaultCM));
             if (conflictManager == null) {
diff --git a/src/java/org/apache/ivy/util/url/BasicURLHandler.java b/src/java/org/apache/ivy/util/url/BasicURLHandler.java
index 72641ef..9f3625e 100644
--- a/src/java/org/apache/ivy/util/url/BasicURLHandler.java
+++ b/src/java/org/apache/ivy/util/url/BasicURLHandler.java
@@ -50,8 +50,10 @@ public class BasicURLHandler extends AbstractURLHandler {
         }
     }
 
+    private int defaultTimeout = 0;
+
     public URLInfo getURLInfo(URL url) {
-        return getURLInfo(url, 0);
+        return getURLInfo(url, defaultTimeout);
     }
 
     public URLInfo getURLInfo(URL url, int timeout) {
@@ -63,8 +65,7 @@ public class BasicURLHandler extends AbstractURLHandler {
         URLConnection con = null;
         try {
             url = normalizeToURL(url);
-            con = url.openConnection();
-            con.setRequestProperty("User-Agent", getUserAgent());
+            con = openConnection(url, timeout);
             if (con instanceof HttpURLConnection) {
                 HttpURLConnection httpCon = (HttpURLConnection) con;
                 if (getRequestMethod() == URLHandler.REQUEST_METHOD_HEAD) {
@@ -158,8 +159,7 @@ public class BasicURLHandler extends AbstractURLHandler {
         URLConnection conn = null;
         try {
             url = normalizeToURL(url);
-            conn = url.openConnection();
-            conn.setRequestProperty("User-Agent", getUserAgent());
+            conn = openConnection(url);
             conn.setRequestProperty("Accept-Encoding", "gzip,deflate");
             if (conn instanceof HttpURLConnection) {
                 HttpURLConnection httpCon = (HttpURLConnection) conn;
@@ -192,8 +192,7 @@ public class BasicURLHandler extends AbstractURLHandler {
         URLConnection srcConn = null;
         try {
             src = normalizeToURL(src);
-            srcConn = src.openConnection();
-            srcConn.setRequestProperty("User-Agent", getUserAgent());
+            srcConn = openConnection(src);
             srcConn.setRequestProperty("Accept-Encoding", "gzip,deflate");
             if (srcConn instanceof HttpURLConnection) {
                 HttpURLConnection httpCon = (HttpURLConnection) srcConn;
@@ -241,10 +240,9 @@ public class BasicURLHandler extends AbstractURLHandler {
         HttpURLConnection conn = null;
         try {
             dest = normalizeToURL(dest);
-            conn = (HttpURLConnection) dest.openConnection();
+            conn = (HttpURLConnection) openConnection(dest);
             conn.setDoOutput(true);
             conn.setRequestMethod("PUT");
-            conn.setRequestProperty("User-Agent", getUserAgent());
             conn.setRequestProperty("Content-type", "application/octet-stream");
             conn.setRequestProperty("Content-length", Long.toString(source.length()));
             conn.setInstanceFollowRedirects(true);
@@ -327,4 +325,26 @@ public class BasicURLHandler extends AbstractURLHandler {
             }
         }
     }
+
+    public int getTimeout() {
+        return defaultTimeout;
+    }
+
+    public void setTimeout(int defaultTimeout) {
+        this.defaultTimeout = defaultTimeout;
+    }
+
+    protected URLConnection openConnection(URL url) throws IOException {
+        return openConnection(url, defaultTimeout);
+    }
+
+    protected URLConnection openConnection(URL url, int timeout) throws IOException{
+        URLConnection con = url.openConnection();
+        if (timeout != 0) {
+            con.setConnectTimeout(timeout);
+            con.setReadTimeout(timeout);
+        }
+        con.setRequestProperty("User-Agent", getUserAgent());
+        return con;
+    }
 }
diff --git a/src/java/org/apache/ivy/util/url/HttpClientHandler.java b/src/java/org/apache/ivy/util/url/HttpClientHandler.java
index 64d4a25..ddf88dd 100644
--- a/src/java/org/apache/ivy/util/url/HttpClientHandler.java
+++ b/src/java/org/apache/ivy/util/url/HttpClientHandler.java
@@ -47,6 +47,7 @@ import org.apache.commons.httpclient.methods.GetMethod;
 import org.apache.commons.httpclient.methods.HeadMethod;
 import org.apache.commons.httpclient.methods.PutMethod;
 import org.apache.commons.httpclient.methods.RequestEntity;
+import org.apache.commons.httpclient.params.HttpConnectionParams;
 import org.apache.commons.httpclient.params.HttpMethodParams;
 import org.apache.ivy.Ivy;
 import org.apache.ivy.util.CopyProgressListener;
@@ -72,6 +73,8 @@ public class HttpClientHandler extends AbstractURLHandler {
 
     private HttpClientHelper httpClientHelper;
 
+    private int defaultTimeout = 0;
+
     private static HttpClient httpClient;
 
     public HttpClientHandler() {
@@ -95,7 +98,7 @@ public class HttpClientHandler extends AbstractURLHandler {
     }
 
     public InputStream openStream(URL url) throws IOException {
-        GetMethod get = doGet(url, 0);
+        GetMethod get = doGet(url, defaultTimeout);
         if (!checkStatusCode(url, get)) {
             get.releaseConnection();
             throw new IOException("The HTTP response code for " + url
@@ -108,7 +111,7 @@ public class HttpClientHandler extends AbstractURLHandler {
     }
 
     public void download(URL src, File dest, CopyProgressListener l) throws IOException {
-        GetMethod get = doGet(src, 0);
+        GetMethod get = doGet(src, defaultTimeout);
         try {
             // We can only figure the content we got is want we want if the status is success.
             if (!checkStatusCode(src, get)) {
@@ -142,7 +145,7 @@ public class HttpClientHandler extends AbstractURLHandler {
     }
 
     public URLInfo getURLInfo(URL url) {
-        return getURLInfo(url, 0);
+        return getURLInfo(url, defaultTimeout);
     }
 
     public URLInfo getURLInfo(URL url, int timeout) {
@@ -248,7 +251,11 @@ public class HttpClientHandler extends AbstractURLHandler {
 
     private GetMethod doGet(URL url, int timeout) throws IOException {
         HttpClient client = getClient();
-        client.setTimeout(timeout);
+
+        httpClient.getParams().setParameter(
+                HttpConnectionParams.CONNECTION_TIMEOUT, timeout);
+        httpClient.getParams().setParameter(
+                HttpConnectionParams.SO_TIMEOUT, timeout);
 
         GetMethod get = new GetMethod(normalizeToString(url));
         get.setDoAuthentication(useAuthentication(url) || useProxyAuthentication());
@@ -259,7 +266,10 @@ public class HttpClientHandler extends AbstractURLHandler {
 
     private HeadMethod doHead(URL url, int timeout) throws IOException {
         HttpClient client = getClient();
-        client.setTimeout(timeout);
+        httpClient.getParams().setParameter(
+                HttpConnectionParams.CONNECTION_TIMEOUT, timeout);
+        httpClient.getParams().setParameter(
+                HttpConnectionParams.SO_TIMEOUT, timeout);
 
         HeadMethod head = new HeadMethod(normalizeToString(url));
         head.setDoAuthentication(useAuthentication(url) || useProxyAuthentication());
@@ -428,4 +438,7 @@ public class HttpClientHandler extends AbstractURLHandler {
         }
     }
 
+    public void setTimeout(int timeout) {
+        this.defaultTimeout = timeout;
+    }
 }
diff --git a/src/java/org/apache/ivy/util/url/URLHandler.java b/src/java/org/apache/ivy/util/url/URLHandler.java
index 96b51d9..b7e4997 100644
--- a/src/java/org/apache/ivy/util/url/URLHandler.java
+++ b/src/java/org/apache/ivy/util/url/URLHandler.java
@@ -170,4 +170,6 @@ public interface URLHandler {
     public void upload(File src, URL dest, CopyProgressListener l) throws IOException;
 
     public void setRequestMethod(int requestMethod);
+
+    public void setTimeout(int timeout);
 }
diff --git a/src/java/org/apache/ivy/util/url/URLHandlerDispatcher.java b/src/java/org/apache/ivy/util/url/URLHandlerDispatcher.java
index be15009..0301758 100644
--- a/src/java/org/apache/ivy/util/url/URLHandlerDispatcher.java
+++ b/src/java/org/apache/ivy/util/url/URLHandlerDispatcher.java
@@ -106,4 +106,12 @@ public class URLHandlerDispatcher implements URLHandler {
     public void setDefault(URLHandler default1) {
         defaultHandler = default1;
     }
+
+    public void setTimeout(int timeout) {
+        defaultHandler.setTimeout(timeout);
+        for (Object obj : handlers.values()) {
+            URLHandler handler = (URLHandler) obj;
+            handler.setTimeout(timeout);
+        }
+    }
 }
diff --git a/src/java/org/apache/ivy/util/url/URLHandlerRegistry.java b/src/java/org/apache/ivy/util/url/URLHandlerRegistry.java
index 21f10f1..f70acdf 100644
--- a/src/java/org/apache/ivy/util/url/URLHandlerRegistry.java
+++ b/src/java/org/apache/ivy/util/url/URLHandlerRegistry.java
@@ -70,4 +70,7 @@ public final class URLHandlerRegistry {
         }
     }
 
+    public static void setTimeout(int timeout) {
+        defaultHandler.setTimeout(timeout);
+    }
 }
-- 
2.0.4.261.g03babb9-twtrsrc

