3838import io .grpc .HttpConnectProxiedSocketAddress ;
3939import io .grpc .Internal ;
4040import io .grpc .ManagedChannelBuilder ;
41+ import io .grpc .Metadata ;
4142import io .grpc .NameResolverProvider ;
4243import io .grpc .NameResolverRegistry ;
4344import io .grpc .internal .AtomicBackoff ;
6061import io .netty .channel .ReflectiveChannelFactory ;
6162import io .netty .channel .socket .nio .NioSocketChannel ;
6263import io .netty .handler .ssl .SslContext ;
64+ import io .netty .util .AsciiString ;
6365import java .net .InetSocketAddress ;
6466import java .net .SocketAddress ;
6567import java .util .Collection ;
6668import java .util .Collections ;
6769import java .util .HashMap ;
70+ import java .util .HashSet ;
6871import java .util .Map ;
72+ import java .util .Set ;
6973import java .util .concurrent .Executor ;
7074import java .util .concurrent .ScheduledExecutorService ;
7175import java .util .concurrent .TimeUnit ;
@@ -105,6 +109,7 @@ public final class NettyChannelBuilder extends ForwardingChannelBuilder2<NettyCh
105109 private ObjectPool <? extends EventLoopGroup > eventLoopGroupPool = DEFAULT_EVENT_LOOP_GROUP_POOL ;
106110 private boolean autoFlowControl = DEFAULT_AUTO_FLOW_CONTROL ;
107111 private int flowControlWindow = DEFAULT_FLOW_CONTROL_WINDOW ;
112+ private final Set <AsciiString > neverIndexedMetadataKeys = new HashSet <>();
108113 private int maxHeaderListSize = GrpcUtil .DEFAULT_MAX_HEADER_LIST_SIZE ;
109114 private int softLimitHeaderListSize = GrpcUtil .DEFAULT_MAX_HEADER_LIST_SIZE ;
110115 private int maxInboundMessageSize = GrpcUtil .DEFAULT_MAX_MESSAGE_SIZE ;
@@ -434,6 +439,43 @@ public NettyChannelBuilder flowControlWindow(int flowControlWindow) {
434439 return this ;
435440 }
436441
442+ /**
443+ * Configures an outbound metadata key to use HPACK's never-indexed literal representation.
444+ *
445+ * <p>All values associated with the key's normalized name will be sent as literals and will not
446+ * be added to the peer's HPACK dynamic table. This method is additive and may be called multiple
447+ * times. Configuring the same normalized key name more than once has no additional effect. By
448+ * default, no metadata keys are configured as never indexed.
449+ *
450+ * @since 1.84.0
451+ */
452+ @ ExperimentalApi ("https://github.com/grpc/grpc-java/issues/12976" )
453+ @ CanIgnoreReturnValue
454+ public NettyChannelBuilder neverIndexMetadataKey (Metadata .Key <?> key ) {
455+ neverIndexedMetadataKeys .add (AsciiString .of (checkNotNull (key , "key" ).name ()));
456+ return this ;
457+ }
458+
459+ /**
460+ * Configures outbound metadata keys to use HPACK's never-indexed literal representation.
461+ *
462+ * <p>This method is equivalent to calling {@link #neverIndexMetadataKey} for each key in {@code
463+ * keys}. Duplicate normalized key names are ignored.
464+ *
465+ * @since 1.84.0
466+ */
467+ @ ExperimentalApi ("https://github.com/grpc/grpc-java/issues/12976" )
468+ @ CanIgnoreReturnValue
469+ public NettyChannelBuilder neverIndexMetadataKeys (
470+ Collection <? extends Metadata .Key <?>> keys ) {
471+ Set <AsciiString > normalizedKeys = new HashSet <>();
472+ for (Metadata .Key <?> key : checkNotNull (keys , "keys" )) {
473+ normalizedKeys .add (AsciiString .of (checkNotNull (key , "key" ).name ()));
474+ }
475+ neverIndexedMetadataKeys .addAll (normalizedKeys );
476+ return this ;
477+ }
478+
437479 /**
438480 * Sets the maximum size of header list allowed to be received. This is cumulative size of the
439481 * headers with some overhead, as defined for
@@ -626,6 +668,7 @@ ClientTransportFactory buildTransportFactory() {
626668 eventLoopGroupPool ,
627669 autoFlowControl ,
628670 flowControlWindow ,
671+ neverIndexedMetadataKeys ,
629672 maxInboundMessageSize ,
630673 maxHeaderListSize ,
631674 softLimitHeaderListSize ,
@@ -769,6 +812,7 @@ private static final class NettyTransportFactory implements ClientTransportFacto
769812 private final EventLoopGroup group ;
770813 private final boolean autoFlowControl ;
771814 private final int flowControlWindow ;
815+ private final Set <AsciiString > neverIndexedMetadataKeys ;
772816 private final int maxMessageSize ;
773817 private final int maxHeaderListSize ;
774818 private final int softLimitHeaderListSize ;
@@ -790,6 +834,7 @@ private static final class NettyTransportFactory implements ClientTransportFacto
790834 ObjectPool <? extends EventLoopGroup > groupPool ,
791835 boolean autoFlowControl ,
792836 int flowControlWindow ,
837+ Set <AsciiString > neverIndexedMetadataKeys ,
793838 int maxMessageSize ,
794839 int maxHeaderListSize ,
795840 int softLimitHeaderListSize ,
@@ -807,6 +852,8 @@ private static final class NettyTransportFactory implements ClientTransportFacto
807852 this .group = groupPool .getObject ();
808853 this .autoFlowControl = autoFlowControl ;
809854 this .flowControlWindow = flowControlWindow ;
855+ this .neverIndexedMetadataKeys = Collections .unmodifiableSet (
856+ new HashSet <>(checkNotNull (neverIndexedMetadataKeys , "neverIndexedMetadataKeys" )));
810857 this .maxMessageSize = maxMessageSize ;
811858 this .maxHeaderListSize = maxHeaderListSize ;
812859 this .softLimitHeaderListSize = softLimitHeaderListSize ;
@@ -856,6 +903,7 @@ public void run() {
856903 localNegotiator ,
857904 autoFlowControl ,
858905 flowControlWindow ,
906+ neverIndexedMetadataKeys ,
859907 maxMessageSize ,
860908 maxHeaderListSize ,
861909 softLimitHeaderListSize ,
@@ -895,6 +943,7 @@ public SwapChannelCredentialsResult swapChannelCredentials(ChannelCredentials ch
895943 groupPool ,
896944 autoFlowControl ,
897945 flowControlWindow ,
946+ neverIndexedMetadataKeys ,
898947 maxMessageSize ,
899948 maxHeaderListSize ,
900949 softLimitHeaderListSize ,
0 commit comments