Repository: netty/netty
Issue: SSLContextBuilder should accept Iterable parameters too

### Expected behavior

It is possible to pass parameters like SSL providers to `SslContextBuilder` without having to convert iterables to arrays, which is tedious.

### Actual behavior

Need to convert to arrays, except for the `ciphers` method.

Wanted to run this by everyone - if the API change makes sense I'd be happy to send a PR. Accepting `Iterable` would make the class easier to use and also make it more consistent.

/cc @trustin 

Issue comments:
@anuraaga this sounds like a good idea... I think we may also want to support varargs ... Maybe you want to provide an PR ?
@anuraaga interested in doing a pr ?
Yup - sorry for the delay have had some stuff lately but will be able to get to this by next week.
@anuraaga are you still interested ?

Pull request review comments:
Probably not a big deal given it's unlikely to be called frequently, but would be nice to check for `instanceof Collection` and then use `.toArray(...)` directly instead of copying to new list.

Patch that resolved the issue:
diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java b/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java
index b0474265b0e..f17169238d9 100644
--- a/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java
+++ b/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java
@@ -16,21 +16,24 @@
 
 package io.netty.handler.ssl;
 
-import static io.netty.util.internal.ObjectUtil.checkNotNull;
-
 import io.netty.util.internal.UnstableApi;
 
-import java.security.KeyStore;
-import java.security.Provider;
 import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.TrustManagerFactory;
-
 import java.io.File;
 import java.io.InputStream;
+import java.security.KeyStore;
 import java.security.PrivateKey;
+import java.security.Provider;
 import java.security.cert.X509Certificate;
-import javax.net.ssl.SSLEngine;
+import java.util.ArrayList;
+import java.util.List;
+
+import static io.netty.util.internal.EmptyArrays.EMPTY_STRINGS;
+import static io.netty.util.internal.EmptyArrays.EMPTY_X509_CERTIFICATES;
+import static io.netty.util.internal.ObjectUtil.checkNotNull;
 
 /**
  * Builder for configuring a new SslContext for creation.
@@ -77,6 +80,17 @@ public static SslContextBuilder forServer(PrivateKey key, X509Certificate... key
         return new SslContextBuilder(true).keyManager(key, keyCertChain);
     }
 
+    /**
+     * Creates a builder for new server-side {@link SslContext}.
+     *
+     * @param key a PKCS#8 private key
+     * @param keyCertChain the X.509 certificate chain
+     * @see #keyManager(PrivateKey, X509Certificate[])
+     */
+    public static SslContextBuilder forServer(PrivateKey key, Iterable<? extends X509Certificate> keyCertChain) {
+        return forServer(key, toArray(keyCertChain, EMPTY_X509_CERTIFICATES));
+    }
+
     /**
      * Creates a builder for new server-side {@link SslContext}.
      *
@@ -119,6 +133,20 @@ public static SslContextBuilder forServer(
         return new SslContextBuilder(true).keyManager(key, keyPassword, keyCertChain);
     }
 
+    /**
+     * Creates a builder for new server-side {@link SslContext}.
+     *
+     * @param key a PKCS#8 private key
+     * @param keyCertChain the X.509 certificate chain
+     * @param keyPassword the password of the {@code keyFile}, or {@code null} if it's not
+     *     password-protected
+     * @see #keyManager(File, File, String)
+     */
+    public static SslContextBuilder forServer(
+            PrivateKey key, String keyPassword, Iterable<? extends X509Certificate> keyCertChain) {
+        return forServer(key, keyPassword, toArray(keyCertChain, EMPTY_X509_CERTIFICATES));
+    }
+
     /**
      * Creates a builder for new server-side {@link SslContext}.
      *
@@ -215,6 +243,13 @@ public SslContextBuilder trustManager(X509Certificate... trustCertCollection) {
         return this;
     }
 
+    /**
+     * Trusted certificates for verifying the remote endpoint's certificate, {@code null} uses the system default.
+     */
+    public SslContextBuilder trustManager(Iterable<? extends X509Certificate> trustCertCollection) {
+        return trustManager(toArray(trustCertCollection, EMPTY_X509_CERTIFICATES));
+    }
+
     /**
      * Trusted manager for verifying the remote endpoint's certificate. {@code null} uses the system default.
      */
@@ -257,6 +292,17 @@ public SslContextBuilder keyManager(PrivateKey key, X509Certificate... keyCertCh
         return keyManager(key, null, keyCertChain);
     }
 
+    /**
+     * Identifying certificate for this host. {@code keyCertChain} and {@code key} may
+     * be {@code null} for client contexts, which disables mutual authentication.
+     *
+     * @param key a PKCS#8 private key
+     * @param keyCertChain an X.509 certificate chain
+     */
+    public SslContextBuilder keyManager(PrivateKey key, Iterable<? extends X509Certificate> keyCertChain) {
+        return keyManager(key, toArray(keyCertChain, EMPTY_X509_CERTIFICATES));
+    }
+
     /**
      * Identifying certificate for this host. {@code keyCertChainFile} and {@code keyFile} may
      * be {@code null} for client contexts, which disables mutual authentication.
@@ -341,6 +387,20 @@ public SslContextBuilder keyManager(PrivateKey key, String keyPassword, X509Cert
         return this;
     }
 
+    /**
+     * Identifying certificate for this host. {@code keyCertChain} and {@code key} may
+     * be {@code null} for client contexts, which disables mutual authentication.
+     *
+     * @param key a PKCS#8 private key file
+     * @param keyPassword the password of the {@code key}, or {@code null} if it's not
+     *     password-protected
+     * @param keyCertChain an X.509 certificate chain
+     */
+    public SslContextBuilder keyManager(PrivateKey key, String keyPassword,
+                                        Iterable<? extends X509Certificate> keyCertChain) {
+        return keyManager(key, keyPassword, toArray(keyCertChain, EMPTY_X509_CERTIFICATES));
+    }
+
     /**
      * Identifying manager for this host. {@code keyManagerFactory} may be {@code null} for
      * client contexts, which disables mutual authentication. Using a {@link KeyManagerFactory}
@@ -427,6 +487,15 @@ public SslContextBuilder protocols(String... protocols) {
         return this;
     }
 
+    /**
+     * The TLS protocol versions to enable.
+     * @param protocols The protocols to enable, or {@code null} to enable the default protocols.
+     * @see SSLEngine#setEnabledCipherSuites(String[])
+     */
+    public SslContextBuilder protocols(Iterable<String> protocols) {
+        return protocols(toArray(protocols, EMPTY_STRINGS));
+    }
+
     /**
      * {@code true} if the first write request shouldn't be encrypted.
      */
@@ -464,4 +533,15 @@ public SslContext build() throws SSLException {
                 ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout, enableOcsp, keyStoreType);
         }
     }
+
+    private static <T> T[] toArray(Iterable<? extends T> iterable, T[] prototype) {
+        if (iterable == null) {
+            return null;
+        }
+        final List<T> list = new ArrayList<T>();
+        for (T element : iterable) {
+            list.add(element);
+        }
+        return list.toArray(prototype);
+    }
 }


Test patch: