netty: prevent IndexOutOfBoundsException when no handler is passed to BufferingHandler
diff --git a/netty/src/main/java/io/grpc/netty/ProtocolNegotiators.java b/netty/src/main/java/io/grpc/netty/ProtocolNegotiators.java
index c028b6b..2d47eb6 100644
--- a/netty/src/main/java/io/grpc/netty/ProtocolNegotiators.java
+++ b/netty/src/main/java/io/grpc/netty/ProtocolNegotiators.java
@@ -465,7 +465,7 @@
        * This check is necessary as a channel may be registered with different event loops during it
        * lifetime and we only want to configure it once.
        */
-      if (handlers != null) {
+      if (handlers != null && handlers.length > 0) {
         for (ChannelHandler handler : handlers) {
           ctx.pipeline().addBefore(ctx.name(), null, handler);
         }
diff --git a/netty/src/test/java/io/grpc/netty/ProtocolNegotiatorsTest.java b/netty/src/test/java/io/grpc/netty/ProtocolNegotiatorsTest.java
index 6beda2a..df1c052 100644
--- a/netty/src/test/java/io/grpc/netty/ProtocolNegotiatorsTest.java
+++ b/netty/src/test/java/io/grpc/netty/ProtocolNegotiatorsTest.java
@@ -32,6 +32,7 @@
 import io.grpc.SecurityLevel;
 import io.grpc.internal.GrpcAttributes;
 import io.grpc.internal.testing.TestUtils;
+import io.grpc.netty.ProtocolNegotiators.AbstractBufferingHandler;
 import io.grpc.netty.ProtocolNegotiators.HostPort;
 import io.grpc.netty.ProtocolNegotiators.ServerTlsHandler;
 import io.grpc.netty.ProtocolNegotiators.TlsNegotiator;
@@ -584,6 +585,24 @@
     elg.shutdownGracefully();
   }
 
+  @Test(expected = Test.None.class /* no exception expected */)
+  @SuppressWarnings("TestExceptionChecker")
+  public void bufferingHandler_shouldNotThrowForEmptyHandler() throws Exception {
+    LocalAddress addr = new LocalAddress("local");
+    ChannelFuture unused = new Bootstrap()
+        .channel(LocalChannel.class)
+        .handler(new BufferingHandlerWithoutHandlers())
+        .group(group)
+        .register().sync();
+    ChannelFuture sf = new ServerBootstrap()
+        .channel(LocalServerChannel.class)
+        .childHandler(new ChannelHandlerAdapter() {})
+        .group(group)
+        .bind(addr);
+    // sync will trigger client's NoHandlerBufferingHandler which should not throw
+    sf.sync();
+  }
+
   private static class FakeGrpcHttp2ConnectionHandler extends GrpcHttp2ConnectionHandler {
 
     static GrpcHttp2ConnectionHandler noopHandler() {
@@ -629,4 +648,11 @@
   private static ByteBuf bb(String s, Channel c) {
     return ByteBufUtil.writeUtf8(c.alloc(), s);
   }
+
+  private static class BufferingHandlerWithoutHandlers extends AbstractBufferingHandler {
+
+    public BufferingHandlerWithoutHandlers(ChannelHandler... handlers) {
+      super(handlers);
+    }
+  }
 }