Merge branch 'releases/Release_2_11_4_Branch'
[jalview.git] / src / jalview / util / HttpUtils.java
index 74f77a2..a074a39 100644 (file)
@@ -22,10 +22,44 @@ package jalview.util;
 
 import java.io.IOException;
 import java.io.InputStream;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.ProtocolException;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URL;
+import java.net.URLConnection;
+
+import jalview.bin.Console;
 
 public class HttpUtils
 {
 
 public class HttpUtils
 {
+  public final static String JALVIEWSCHEMEPREFIX = "jalview";
+
+  public static boolean isPlausibleUri(String s)
+  {
+    if (s == null)
+    {
+      return false;
+    }
+    if (startsWithHttpOrHttps(s) || isJalviewSchemeUri(s))
+    {
+      return true;
+    }
+    try
+    {
+      URI u = new URI(s);
+      // allow file:/home/... as well as file:///home... as java copes
+      if (s.startsWith("file:/"))
+      {
+        return true;
+      }
+    } catch (URISyntaxException e)
+    {
+      return false;
+    }
+    return false;
+  }
 
   /**
    * Returns true if it is possible to open an input stream at the given URL,
 
   /**
    * Returns true if it is possible to open an input stream at the given URL,
@@ -39,7 +73,7 @@ public class HttpUtils
     InputStream is = null;
     try
     {
     InputStream is = null;
     try
     {
-      is = new URL(url).openStream();
+      is = HttpUtils.openStream(new URL(url));
       if (is != null)
       {
         return true;
       if (is != null)
       {
         return true;
@@ -69,4 +103,278 @@ public class HttpUtils
     return file.startsWith("http://") || file.startsWith("https://");
   }
 
     return file.startsWith("http://") || file.startsWith("https://");
   }
 
+  /**
+   * wrapper to get/post to a URL or check headers
+   * 
+   * @param url
+   * @param ids
+   * @param readTimeout
+   * @return
+   * @throws IOException
+   * @throws ProtocolException
+   */
+  public static boolean checkUrlAvailable(URL url, int readTimeout)
+          throws IOException, ProtocolException
+  {
+    // jalview.bin.Console.outPrintln(System.currentTimeMillis() + " " + url);
+
+    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+    connection.setRequestMethod("HEAD");
+    connection.setDoInput(true);
+    connection.setUseCaches(false);
+    connection.setConnectTimeout(300);
+    connection.setReadTimeout(readTimeout);
+
+    // HttpURLConnection doesn't follow redirects from http to https. It should!
+    HttpURLConnection conn = followConnection(connection);
+    return conn.getResponseCode() == 200;
+  }
+
+  /**
+   * wrapper to return a new HttpURLConnection to a new URL when there is a
+   * redirect from http to https, otherwise return the unused original
+   * HttpURLConnection
+   * 
+   * @param HttpURLConnection
+   *          conn0
+   * @return HttpUrlConnection conn
+   */
+  public static HttpURLConnection followConnection(HttpURLConnection conn0)
+          throws IOException
+  {
+    return followConnection(conn0, false);
+  }
+
+  public static HttpURLConnection followConnection(HttpURLConnection conn0,
+          boolean followAnyway) throws IOException
+  {
+    URL url = conn0.getURL();
+    // we are only checking for a redirect from http to https otherwise the java
+    // connection will follow when called (if not unset)
+    if (url == null)
+    {
+      return conn0;
+    }
+    if (!conn0.getInstanceFollowRedirects())
+    {
+      return conn0;
+    }
+    if (!"http".equals(url.getProtocol()) && !followAnyway)
+    {
+      return conn0;
+    }
+
+    // check the response code
+    HttpURLConnection checkConn = (HttpURLConnection) url.openConnection();
+    httpURLConnectionCopyAttributes(conn0, checkConn);
+
+    boolean redirectToHttps = false;
+    int response = checkConn.getResponseCode();
+    checkConn.disconnect();
+    if (response >= 300 && response < 400)
+    {
+      // we are only checking for a redirect from http to https
+      URL loc = new URL(conn0.getHeaderField("Location"));
+      if (loc != null && "https".equals(loc.getProtocol()))
+      {
+        redirectToHttps = true;
+        url = loc;
+      }
+    }
+    else if (followAnyway)
+    {
+      // checkConn might have followed a https->https redirect
+      url = checkConn.getURL();
+    }
+
+    if (!redirectToHttps && !followAnyway)
+    {
+      return conn0;
+    }
+
+    // We want to return an HttpURLConnection to the new (probably https) URL
+    // that is unconnected in case further manipulation of the request is
+    // required.
+    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+    httpURLConnectionCopyAttributes(conn0, conn);
+    return conn;
+  }
+
+  private static void httpURLConnectionCopyAttributes(
+          HttpURLConnection conn0, HttpURLConnection conn1)
+          throws ProtocolException
+  {
+    conn1.setRequestMethod(conn0.getRequestMethod());
+    conn1.setDoInput(conn0.getDoInput());
+    conn1.setUseCaches(conn0.getUseCaches());
+    conn1.setConnectTimeout(conn0.getConnectTimeout());
+    conn1.setReadTimeout(conn0.getReadTimeout());
+    conn1.setInstanceFollowRedirects(conn0.getInstanceFollowRedirects());
+  }
+
+  /**
+   * wrapper to follow a URL connection ALLOWING redirects from http to https
+   * 
+   * @param URL
+   *          url
+   * @return HttpUrlConnection conn
+   */
+  public static URLConnection openConnection(URL url) throws IOException
+  {
+    return openConnection(url, false);
+  }
+
+  public static URLConnection openConnection(URL url, boolean followAnyway)
+          throws IOException
+  {
+    if (url == null)
+    {
+      Console.debug("HttpUtils.openConnection(url) called with null url");
+      return null;
+    }
+    Console.debug("HttpUtils.openConnection(url) called with url="
+            + url.toString());
+    URLConnection conn = null;
+    String protocol = url.getProtocol();
+    if ("http".equals(protocol) || "https".equals(protocol))
+    {
+      HttpURLConnection conn0 = (HttpURLConnection) url.openConnection();
+      if (conn0 != null)
+      {
+        conn = HttpUtils.followConnection(conn0, followAnyway);
+      }
+      else
+      {
+        conn = conn0;
+      }
+    }
+    else
+    {
+      conn = url.openConnection();
+    }
+    return conn;
+  }
+
+  /**
+   * wrapper to follow a URL connection ALLOWING redirects from http to https
+   * and return the followed InputStream
+   * 
+   * @param URL
+   *          url
+   * @return HttpUrlConnection conn
+   */
+  public static InputStream openStream(URL url) throws IOException
+  {
+    return openStream(url, false);
+  }
+
+  public static InputStream openStream(URL url, boolean followAnyway)
+          throws IOException
+  {
+    if (url == null)
+    {
+      return null;
+    }
+    InputStream is = null;
+    String protocol = url.getProtocol();
+    if ("http".equals(protocol) || "https".equals(protocol))
+    {
+      HttpURLConnection conn = HttpUtils.followConnection(
+              (HttpURLConnection) url.openConnection(), followAnyway);
+      if (conn != null)
+      {
+        is = conn.getInputStream();
+      }
+    }
+    else
+    {
+      is = url.openStream();
+    }
+    return is;
+  }
+
+  /**
+   * check if a jalview:// scheme URL is given
+   * 
+   * @param String
+   *          uri
+   * @return boolean
+   */
+  public static boolean isJalviewSchemeUri(String jalviewUriString)
+  {
+    if (jalviewUriString == null)
+    {
+      return false;
+    }
+    URI jalviewUri;
+    try
+    {
+      jalviewUri = new URI(jalviewUriString);
+    } catch (URISyntaxException e)
+    {
+      return false;
+    }
+    String scheme = jalviewUri.getScheme();
+    if (scheme == null || !scheme.startsWith(JALVIEWSCHEMEPREFIX))
+    {
+      return false;
+    }
+    int jspl = JALVIEWSCHEMEPREFIX.length();
+    return scheme.length() == jspl // jalview
+            || scheme.length() == jspl + 1 // jalviewX
+            || scheme.substring(jspl).equals("http") // jalviewhttp
+            || scheme.substring(jspl).equals("https"); // jalviewhttps
+  }
+
+  /**
+   * convert a jalview scheme URI to its equivalent URL or path
+   * 
+   * @param String
+   *          uri
+   * @return String
+   */
+  public static String equivalentJalviewUrl(String jalviewUriString)
+  {
+    if (!isJalviewSchemeUri(jalviewUriString))
+    {
+      // not a jalviewUriString, hand it back
+      return jalviewUriString;
+    }
+    URI jalviewUri;
+    try
+    {
+      jalviewUri = new URI(jalviewUriString);
+    } catch (URISyntaxException e)
+    {
+      return null;
+    }
+    String scheme = jalviewUri.getScheme();
+    String host = jalviewUri.getHost();
+    if (host != null && host.length() > 0 || scheme
+            .substring(JALVIEWSCHEMEPREFIX.length()).startsWith("http"))
+    {
+      URI newUri;
+      try
+      {
+        newUri = new URI(
+                scheme.equals(JALVIEWSCHEMEPREFIX + "http") ? "http"
+                        : "https",
+                jalviewUri.getUserInfo(), host, jalviewUri.getPort(),
+                jalviewUri.getPath(), jalviewUri.getQuery(),
+                jalviewUri.getFragment());
+        // return a URL
+        return newUri.toURL().toString();
+      } catch (URISyntaxException | MalformedURLException e)
+      {
+        ErrorLog.errPrintln("Trying to convert '" + jalviewUriString
+                + "' to URL failed");
+      }
+    }
+    else
+    {
+      // return a file path (not a file URI)
+      return jalviewUri.getPath();
+    }
+    return null;
+  }
 }
 }