Support clone url type configuration

Setting gerrit.cloneUrlType in the plugin config to change clone url
type from default ssh (if ssh is enabled) to http or git.

Change-Id: I120863e0829f6ec7aecd041fbdf87ed1228a8746
diff --git a/src/main/java/com/googlesource/gerrit/plugins/gitiles/PluginModule.java b/src/main/java/com/googlesource/gerrit/plugins/gitiles/PluginModule.java
index 5aab4cd..97ab188 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/gitiles/PluginModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/gitiles/PluginModule.java
@@ -43,17 +43,24 @@
 import java.net.URL;
 import java.net.UnknownHostException;
 import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
 import javax.servlet.http.HttpServletRequest;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.transport.resolver.RepositoryResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 class PluginModule extends LifecycleModule {
   private final boolean noWebLinks;
+  private final String cloneUrlType;
+  private static final Logger log = LoggerFactory.getLogger(Module.class);
 
   @Inject
   PluginModule(PluginConfigFactory configFactory) {
     Config config = configFactory.getGlobalPluginConfig("gitiles");
     this.noWebLinks = config.getBoolean("gerrit", null, "noWebLinks", false);
+    this.cloneUrlType = config.getString("gerrit", null, "cloneUrlType");
   }
 
   @Override
@@ -72,6 +79,82 @@
     listener().to(Lifecycle.class);
   }
 
+  private Optional<String> getSshCloneUrl(URL gerritUrl, List<String> advertisedSshAddresses) {
+    try {
+      if (!advertisedSshAddresses.isEmpty()) {
+        String addr = advertisedSshAddresses.get(0);
+        int index = addr.indexOf(":");
+        String port = "";
+        if (index != -1) {
+          port = addr.substring(index);
+        }
+        if (addr.startsWith("*:") || "".equals(addr)) {
+          if (gerritUrl != null && gerritUrl.getHost() != null) {
+            addr = gerritUrl.getHost();
+          } else {
+            addr = getLocalHostName();
+          }
+        } else {
+          if (index != -1) {
+            addr = addr.substring(0, index);
+          }
+        }
+        return Optional.of("ssh://" + addr + port + "/");
+      }
+    } catch (UnknownHostException e) {
+      log.error("Unable to get SSH clone url.", e);
+      return Optional.empty();
+    }
+    return Optional.empty();
+  }
+
+  private Optional<String> getHttpCloneUrl(Config gerritConfig) {
+    Optional<String> httpUrl =
+        Optional.ofNullable(gerritConfig.getString("gerrit", null, "gitHttpUrl"));
+    if (httpUrl.isEmpty()) {
+      return getDefaultCloneUrl(gerritConfig);
+    }
+    return httpUrl;
+  }
+
+  private Optional<String> getGitCloneUrl(Config gerritConfig) {
+    Optional<String> gitUrl =
+        Optional.ofNullable(gerritConfig.getString("gerrit", null, "canonicalGitUrl"));
+    if (gitUrl.isEmpty()) {
+      return getDefaultCloneUrl(gerritConfig);
+    }
+    return gitUrl;
+  }
+
+  private Optional<String> getDefaultCloneUrl(Config gerritConfig) {
+    return Optional.ofNullable(gerritConfig.getString("gerrit", null, "canonicalWebUrl"));
+  }
+
+  private Optional<String> getUserConfig(
+      Config gerritConfig, URL u, @SshAdvertisedAddresses List<String> advertisedSshAddresses) {
+    Optional<String> gitUrl = Optional.empty();
+    // Try to use user's config first.
+    if (this.cloneUrlType != null) {
+      switch (this.cloneUrlType) {
+        case "ssh":
+          gitUrl = getSshCloneUrl(u, advertisedSshAddresses);
+          break;
+        case "http":
+          gitUrl = getHttpCloneUrl(gerritConfig);
+          break;
+        case "git":
+          gitUrl = getGitCloneUrl(gerritConfig);
+          break;
+      }
+      if (gitUrl.isEmpty()) {
+        log.info(
+            "Failed to use clone url type configuration."
+                + " Using default type (prefer SSH, then HTTP, then Git).");
+      }
+    }
+    return gitUrl;
+  }
+
   @Provides
   GitilesUrls getGitilesUrls(
       @GerritServerConfig Config gerritConfig,
@@ -88,37 +171,22 @@
       hostName = "Gerrit";
     }
 
-    // Arbitrarily prefer SSH, then HTTP, then git.
-    // TODO: Use user preferences.
-    String gitUrl;
-    if (!advertisedSshAddresses.isEmpty()) {
-      String addr = advertisedSshAddresses.get(0);
-      int index = addr.indexOf(":");
-      String port = "";
-      if (index != -1) {
-        port = addr.substring(index);
-      }
-      if (addr.startsWith("*:") || "".equals(addr)) {
-        if (u != null && u.getHost() != null) {
-          addr = u.getHost();
-        } else {
-          addr = getLocalHostName();
-        }
-      } else {
-        if (index != -1) {
-          addr = addr.substring(0, index);
-        }
-      }
-      gitUrl = "ssh://" + addr + port + "/";
-    } else {
-      gitUrl = gerritConfig.getString("gerrit", null, "gitHttpUrl");
-      if (gitUrl == null) {
-        gitUrl = gerritConfig.getString("gerrit", null, "canonicalGitUrl");
-      }
-    }
-    if (gitUrl == null) {
-      throw new ProvisionException("Unable to determine any canonical git URL from gerrit.config");
-    }
+    // If no config is set, or we can't get the chosen type of URL determined in the config,
+    // arbitrarily prefer SSH, then HTTP, then git.
+    String gitUrl =
+        Stream.of(
+                getUserConfig(gerritConfig, u, advertisedSshAddresses),
+                getSshCloneUrl(u, advertisedSshAddresses),
+                getHttpCloneUrl(gerritConfig),
+                getGitCloneUrl(gerritConfig))
+            .filter(Optional::isPresent)
+            .map(Optional::get)
+            .findFirst()
+            .orElseThrow(
+                () ->
+                    new ProvisionException(
+                        "Unable to determine any canonical git URL from gerrit.config"));
+
     return new DefaultUrls(hostName, gitUrl, gerritUrl);
   }
 
diff --git a/src/main/resources/+Documentation/config.md b/src/main/resources/+Documentation/config.md
index 562603b..49d6c01 100644
--- a/src/main/resources/+Documentation/config.md
+++ b/src/main/resources/+Documentation/config.md
@@ -64,3 +64,16 @@
 
 Note: If using this setting you possibly want to set Gerrit's auth.cookiePath to "/"
 if it's not running in document root already.
+
+The flag `gerrit.cloneUrlType` can be set to either `ssh`, `http`, or `git`. This
+will determine which protocol to be used to display repos' clone url to the user:
+`git` will use value of `gerrit.canonicalGitUrl` and `http` will use the value of
+`gerrit.gitHttpUrl`. If those values are not set then http/git format will fall
+back to `gerrit.canonicalWebUrl`.
+If it's not set, or it fails to get the set type of URL, it will automatically prefer
+SSH, then HTTP, then Git.
+
+```
+  [gerrit]
+    cloneUrlType = http
+```