@@ -104,6 +104,7 @@ public final class NettyChannelBuilder extends ForwardingChannelBuilder2<NettyCh
104
104
private boolean autoFlowControl = DEFAULT_AUTO_FLOW_CONTROL ;
105
105
private int flowControlWindow = DEFAULT_FLOW_CONTROL_WINDOW ;
106
106
private int maxHeaderListSize = GrpcUtil .DEFAULT_MAX_HEADER_LIST_SIZE ;
107
+ private int softLimitHeaderListSize = GrpcUtil .DEFAULT_MAX_HEADER_LIST_SIZE ;
107
108
private int maxInboundMessageSize = GrpcUtil .DEFAULT_MAX_MESSAGE_SIZE ;
108
109
private long keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED ;
109
110
private long keepAliveTimeoutNanos = DEFAULT_KEEPALIVE_TIMEOUT_NANOS ;
@@ -452,6 +453,40 @@ public NettyChannelBuilder maxHeaderListSize(int maxHeaderListSize) {
452
453
public NettyChannelBuilder maxInboundMetadataSize (int bytes ) {
453
454
checkArgument (bytes > 0 , "maxInboundMetadataSize must be > 0" );
454
455
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 ;
455
490
return this ;
456
491
}
457
492
@@ -573,10 +608,22 @@ ClientTransportFactory buildTransportFactory() {
573
608
574
609
ProtocolNegotiator negotiator = protocolNegotiatorFactory .newNegotiator ();
575
610
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 );
580
627
}
581
628
582
629
@ VisibleForTesting
@@ -710,6 +757,7 @@ private static final class NettyTransportFactory implements ClientTransportFacto
710
757
private final int flowControlWindow ;
711
758
private final int maxMessageSize ;
712
759
private final int maxHeaderListSize ;
760
+ private final int softLimitHeaderListSize ;
713
761
private final long keepAliveTimeNanos ;
714
762
private final AtomicBackoff keepAliveBackoff ;
715
763
private final long keepAliveTimeoutNanos ;
@@ -724,11 +772,20 @@ private static final class NettyTransportFactory implements ClientTransportFacto
724
772
NettyTransportFactory (
725
773
ProtocolNegotiator protocolNegotiator ,
726
774
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 ) {
732
789
this .protocolNegotiator = checkNotNull (protocolNegotiator , "protocolNegotiator" );
733
790
this .channelFactory = channelFactory ;
734
791
this .channelOptions = new HashMap <ChannelOption <?>, Object >(channelOptions );
@@ -738,6 +795,7 @@ private static final class NettyTransportFactory implements ClientTransportFacto
738
795
this .flowControlWindow = flowControlWindow ;
739
796
this .maxMessageSize = maxMessageSize ;
740
797
this .maxHeaderListSize = maxHeaderListSize ;
798
+ this .softLimitHeaderListSize = softLimitHeaderListSize ;
741
799
this .keepAliveTimeNanos = keepAliveTimeNanos ;
742
800
this .keepAliveBackoff = new AtomicBackoff ("keepalive time nanos" , keepAliveTimeNanos );
743
801
this .keepAliveTimeoutNanos = keepAliveTimeoutNanos ;
@@ -774,13 +832,30 @@ public void run() {
774
832
};
775
833
776
834
// 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 ());
784
859
return transport ;
785
860
}
786
861
@@ -796,11 +871,24 @@ public SwapChannelCredentialsResult swapChannelCredentials(ChannelCredentials ch
796
871
if (result .error != null ) {
797
872
return null ;
798
873
}
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 );
804
892
return new SwapChannelCredentialsResult (factory , result .callCredentials );
805
893
}
806
894
0 commit comments