@@ -104,6 +104,7 @@ public final class NettyChannelBuilder extends ForwardingChannelBuilder2<NettyCh
104104 private boolean autoFlowControl = DEFAULT_AUTO_FLOW_CONTROL ;
105105 private int flowControlWindow = DEFAULT_FLOW_CONTROL_WINDOW ;
106106 private int maxHeaderListSize = GrpcUtil .DEFAULT_MAX_HEADER_LIST_SIZE ;
107+ private int softLimitHeaderListSize = GrpcUtil .DEFAULT_MAX_HEADER_LIST_SIZE ;
107108 private int maxInboundMessageSize = GrpcUtil .DEFAULT_MAX_MESSAGE_SIZE ;
108109 private long keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED ;
109110 private long keepAliveTimeoutNanos = DEFAULT_KEEPALIVE_TIMEOUT_NANOS ;
@@ -452,6 +453,40 @@ public NettyChannelBuilder maxHeaderListSize(int maxHeaderListSize) {
452453 public NettyChannelBuilder maxInboundMetadataSize (int bytes ) {
453454 checkArgument (bytes > 0 , "maxInboundMetadataSize must be > 0" );
454455 this .maxHeaderListSize = bytes ;
456+ // Clear the soft limit setting, by setting soft limit to maxInboundMetadataSize. The
457+ // maxInboundMetadataSize will take precedence be applied before soft limit check.
458+ this .softLimitHeaderListSize = bytes ;
459+ return this ;
460+ }
461+
462+ /**
463+ * Sets the size of metadata that clients are advised to not exceed. When a metadata with size
464+ * larger than the soft limit is encountered there will be a probability the RPC will fail. The
465+ * chance of failing increases as the metadata size approaches the hard limit.
466+ * {@code Integer.MAX_VALUE} disables the enforcement. The default is implementation-dependent,
467+ * but is not generally less than 8 KiB and may be unlimited.
468+ *
469+ * <p>This is cumulative size of the metadata. The precise calculation is
470+ * implementation-dependent, but implementations are encouraged to follow the calculation used
471+ * for
472+ * <a href="http://httpwg.org/specs/rfc7540.html#rfc.section.6.5.2">HTTP/2's
473+ * SETTINGS_MAX_HEADER_LIST_SIZE</a>. It sums the bytes from each entry's key and value, plus 32
474+ * bytes of overhead per entry.
475+ *
476+ * @param soft the soft size limit of received metadata
477+ * @param max the hard size limit of received metadata
478+ * @return this
479+ * @throws IllegalArgumentException if soft and/or max is non-positive, or max smaller than
480+ * soft
481+ * @since 1.68.0
482+ */
483+ @ CanIgnoreReturnValue
484+ public NettyChannelBuilder maxInboundMetadataSize (int soft , int max ) {
485+ checkArgument (soft > 0 , "softLimitHeaderListSize must be > 0" );
486+ checkArgument (max > soft ,
487+ "maxInboundMetadataSize must be greater than softLimitHeaderListSize" );
488+ this .softLimitHeaderListSize = soft ;
489+ this .maxHeaderListSize = max ;
455490 return this ;
456491 }
457492
@@ -573,10 +608,22 @@ ClientTransportFactory buildTransportFactory() {
573608
574609 ProtocolNegotiator negotiator = protocolNegotiatorFactory .newNegotiator ();
575610 return new NettyTransportFactory (
576- negotiator , channelFactory , channelOptions ,
577- eventLoopGroupPool , autoFlowControl , flowControlWindow , maxInboundMessageSize ,
578- maxHeaderListSize , keepAliveTimeNanos , keepAliveTimeoutNanos , keepAliveWithoutCalls ,
579- transportTracerFactory , localSocketPicker , useGetForSafeMethods , transportSocketType );
611+ negotiator ,
612+ channelFactory ,
613+ channelOptions ,
614+ eventLoopGroupPool ,
615+ autoFlowControl ,
616+ flowControlWindow ,
617+ maxInboundMessageSize ,
618+ maxHeaderListSize ,
619+ softLimitHeaderListSize ,
620+ keepAliveTimeNanos ,
621+ keepAliveTimeoutNanos ,
622+ keepAliveWithoutCalls ,
623+ transportTracerFactory ,
624+ localSocketPicker ,
625+ useGetForSafeMethods ,
626+ transportSocketType );
580627 }
581628
582629 @ VisibleForTesting
@@ -710,6 +757,7 @@ private static final class NettyTransportFactory implements ClientTransportFacto
710757 private final int flowControlWindow ;
711758 private final int maxMessageSize ;
712759 private final int maxHeaderListSize ;
760+ private final int softLimitHeaderListSize ;
713761 private final long keepAliveTimeNanos ;
714762 private final AtomicBackoff keepAliveBackoff ;
715763 private final long keepAliveTimeoutNanos ;
@@ -724,11 +772,20 @@ private static final class NettyTransportFactory implements ClientTransportFacto
724772 NettyTransportFactory (
725773 ProtocolNegotiator protocolNegotiator ,
726774 ChannelFactory <? extends Channel > channelFactory ,
727- Map <ChannelOption <?>, ?> channelOptions , ObjectPool <? extends EventLoopGroup > groupPool ,
728- boolean autoFlowControl , int flowControlWindow , int maxMessageSize , int maxHeaderListSize ,
729- long keepAliveTimeNanos , long keepAliveTimeoutNanos , boolean keepAliveWithoutCalls ,
730- TransportTracer .Factory transportTracerFactory , LocalSocketPicker localSocketPicker ,
731- boolean useGetForSafeMethods , Class <? extends SocketAddress > transportSocketType ) {
775+ Map <ChannelOption <?>, ?> channelOptions ,
776+ ObjectPool <? extends EventLoopGroup > groupPool ,
777+ boolean autoFlowControl ,
778+ int flowControlWindow ,
779+ int maxMessageSize ,
780+ int maxHeaderListSize ,
781+ int softLimitHeaderListSize ,
782+ long keepAliveTimeNanos ,
783+ long keepAliveTimeoutNanos ,
784+ boolean keepAliveWithoutCalls ,
785+ TransportTracer .Factory transportTracerFactory ,
786+ LocalSocketPicker localSocketPicker ,
787+ boolean useGetForSafeMethods ,
788+ Class <? extends SocketAddress > transportSocketType ) {
732789 this .protocolNegotiator = checkNotNull (protocolNegotiator , "protocolNegotiator" );
733790 this .channelFactory = channelFactory ;
734791 this .channelOptions = new HashMap <ChannelOption <?>, Object >(channelOptions );
@@ -738,6 +795,7 @@ private static final class NettyTransportFactory implements ClientTransportFacto
738795 this .flowControlWindow = flowControlWindow ;
739796 this .maxMessageSize = maxMessageSize ;
740797 this .maxHeaderListSize = maxHeaderListSize ;
798+ this .softLimitHeaderListSize = softLimitHeaderListSize ;
741799 this .keepAliveTimeNanos = keepAliveTimeNanos ;
742800 this .keepAliveBackoff = new AtomicBackoff ("keepalive time nanos" , keepAliveTimeNanos );
743801 this .keepAliveTimeoutNanos = keepAliveTimeoutNanos ;
@@ -774,13 +832,30 @@ public void run() {
774832 };
775833
776834 // TODO(carl-mastrangelo): Pass channelLogger in.
777- NettyClientTransport transport = new NettyClientTransport (
778- serverAddress , channelFactory , channelOptions , group ,
779- localNegotiator , autoFlowControl , flowControlWindow ,
780- maxMessageSize , maxHeaderListSize , keepAliveTimeNanosState .get (), keepAliveTimeoutNanos ,
781- keepAliveWithoutCalls , options .getAuthority (), options .getUserAgent (),
782- tooManyPingsRunnable , transportTracerFactory .create (), options .getEagAttributes (),
783- localSocketPicker , channelLogger , useGetForSafeMethods , Ticker .systemTicker ());
835+ NettyClientTransport transport =
836+ new NettyClientTransport (
837+ serverAddress ,
838+ channelFactory ,
839+ channelOptions ,
840+ group ,
841+ localNegotiator ,
842+ autoFlowControl ,
843+ flowControlWindow ,
844+ maxMessageSize ,
845+ maxHeaderListSize ,
846+ softLimitHeaderListSize ,
847+ keepAliveTimeNanosState .get (),
848+ keepAliveTimeoutNanos ,
849+ keepAliveWithoutCalls ,
850+ options .getAuthority (),
851+ options .getUserAgent (),
852+ tooManyPingsRunnable ,
853+ transportTracerFactory .create (),
854+ options .getEagAttributes (),
855+ localSocketPicker ,
856+ channelLogger ,
857+ useGetForSafeMethods ,
858+ Ticker .systemTicker ());
784859 return transport ;
785860 }
786861
@@ -796,11 +871,24 @@ public SwapChannelCredentialsResult swapChannelCredentials(ChannelCredentials ch
796871 if (result .error != null ) {
797872 return null ;
798873 }
799- ClientTransportFactory factory = new NettyTransportFactory (
800- result .negotiator .newNegotiator (), channelFactory , channelOptions , groupPool ,
801- autoFlowControl , flowControlWindow , maxMessageSize , maxHeaderListSize , keepAliveTimeNanos ,
802- keepAliveTimeoutNanos , keepAliveWithoutCalls , transportTracerFactory , localSocketPicker ,
803- useGetForSafeMethods , transportSocketType );
874+ ClientTransportFactory factory =
875+ new NettyTransportFactory (
876+ result .negotiator .newNegotiator (),
877+ channelFactory ,
878+ channelOptions ,
879+ groupPool ,
880+ autoFlowControl ,
881+ flowControlWindow ,
882+ maxMessageSize ,
883+ maxHeaderListSize ,
884+ softLimitHeaderListSize ,
885+ keepAliveTimeNanos ,
886+ keepAliveTimeoutNanos ,
887+ keepAliveWithoutCalls ,
888+ transportTracerFactory ,
889+ localSocketPicker ,
890+ useGetForSafeMethods ,
891+ transportSocketType );
804892 return new SwapChannelCredentialsResult (factory , result .callCredentials );
805893 }
806894
0 commit comments