reactor / reactor-netty

TCP/HTTP/UDP/QUIC client/server with Reactor over Netty

Home Page:https://projectreactor.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

PrematureCloseException from exceeding header limit after H1 upgrade to H2

samueldlightfoot opened this issue · comments

After performing successful upgrade from H1 to H2, if second request exceeds headers limit a PrematureCloseException is seen by the client rather than a 413.

It also appears future requests even with headers within the size limit do not detect the bad connection and also fail with PrematureCloseException, exacerbating the issue.

Expected Behavior

Client should receive 413 exception rather than PrematureCloseException.,

Actual Behavior

PrematureCloseException is seen on the client-side. We see the real error HeaderListSizeException propagates to ChannelOperations::onError, but at this point isDisposed == true (as channel::isActive == false), so therefore the error is not propagated further.

	@Override
	public final void onError(Throwable t) {
		if (isDisposed()) {
			if (log.isDebugEnabled()) {
				log.debug(format(channel(), "An outbound error could not be processed"), t);
			}
			return;
		}
		OUTBOUND_CLOSE.set(this, Operators.cancelledSubscription());
		onOutboundError(t);
	}
2023-10-06T20:26:38.107-04:00 DEBUG 24876 --- [ctor-http-nio-5] reactor.netty.http.client.Http2Pool      : [f0195865-1, L:/127.0.0.1:63494 - R:localhost/127.0.0.1:9995] Channel activated
2023-10-06T20:26:38.107-04:00 DEBUG 24876 --- [ctor-http-nio-4] reactor.netty.http.client.Http2Pool      : [f0195865-1, L:/127.0.0.1:63494 - R:localhost/127.0.0.1:9995] Channel deactivated
2023-10-06T20:26:38.109-04:00 DEBUG 24876 --- [ctor-http-nio-4] r.n.http.client.HttpClientOperations     : [f0195865, L:/127.0.0.1:63494 - R:localhost/127.0.0.1:9995](H2 - -1) New HTTP/2 stream
2023-10-06T20:26:38.109-04:00 DEBUG 24876 --- [ctor-http-nio-4] r.netty.http.client.HttpClientConfig     : [f0195865, L:/127.0.0.1:63494 - R:localhost/127.0.0.1:9995](H2 - -1) Initialized HTTP/2 stream pipeline AbstractHttp2StreamChannel$3{(reactor.left.h2ToHttp11Codec = io.netty.handler.codec.http2.Http2StreamFrameToHttpObjectCodec), (reactor.left.httpTrafficHandler = reactor.netty.http.client.Http2StreamBridgeClientHandler), (reactor.left.httpMetricsHandler = reactor.netty.http.client.MicrometerHttpClientMetricsHandler), (reactor.right.reactiveBridge = reactor.netty.channel.ChannelOperationsHandler)}
2023-10-06T20:26:38.109-04:00 DEBUG 24876 --- [ctor-http-nio-4] r.netty.http.client.HttpClientConnect    : [f0195865/2-1, L:/127.0.0.1:63494 - R:localhost/127.0.0.1:9995] Handler is being applied: {uri=http://localhost:9995/callSuccess, method=POST}
2023-10-06T20:26:39.937-04:00 DEBUG 24876 --- [ctor-http-nio-4] r.n.http.client.Http2ConnectionProvider  : [f0195865/2-1, L:/127.0.0.1:63494 ! R:localhost/127.0.0.1:9995] Stream closed, now: 0 active streams and 2147483647 max active streams.
2023-10-06T20:26:41.587-04:00 DEBUG 24876 --- [ctor-http-nio-4] r.netty.channel.ChannelOperations        : [f0195865/2-1, L:/127.0.0.1:63494 ! R:localhost/127.0.0.1:9995] An outbound error could not be processed

io.netty.handler.codec.http2.Http2Exception$HeaderListSizeException: Header size exceeded max allowed size (1024)
	at io.netty.handler.codec.http2.Http2Exception.headerListSizeError(Http2Exception.java:195) ~[netty-codec-http2-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.http2.Http2CodecUtil.headerListSizeExceeded(Http2CodecUtil.java:233) ~[netty-codec-http2-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.http2.HpackEncoder.encodeHeadersEnforceMaxHeaderListSize(HpackEncoder.java:140) ~[netty-codec-http2-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.http2.HpackEncoder.encodeHeaders(HpackEncoder.java:124) ~[netty-codec-http2-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.http2.DefaultHttp2HeadersEncoder.encodeHeaders(DefaultHttp2HeadersEncoder.java:74) ~[netty-codec-http2-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.http2.DefaultHttp2FrameWriter.writeHeadersInternal(DefaultHttp2FrameWriter.java:501) ~[netty-codec-http2-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.http2.DefaultHttp2FrameWriter.writeHeaders(DefaultHttp2FrameWriter.java:260) ~[netty-codec-http2-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.http2.DefaultHttp2ConnectionEncoder.sendHeaders(DefaultHttp2ConnectionEncoder.java:184) ~[netty-codec-http2-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.http2.DefaultHttp2ConnectionEncoder.writeHeaders0(DefaultHttp2ConnectionEncoder.java:233) ~[netty-codec-http2-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.http2.DefaultHttp2ConnectionEncoder.writeHeaders(DefaultHttp2ConnectionEncoder.java:151) ~[netty-codec-http2-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.http2.DecoratingHttp2FrameWriter.writeHeaders(DecoratingHttp2FrameWriter.java:45) ~[netty-codec-http2-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.http2.Http2FrameCodec.writeHeadersFrame(Http2FrameCodec.java:404) ~[netty-codec-http2-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.http2.Http2FrameCodec.write(Http2FrameCodec.java:303) ~[netty-codec-http2-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:881) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:863) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:968) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:856) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.http2.AbstractHttp2StreamChannel.write0(AbstractHttp2StreamChannel.java:1108) ~[netty-codec-http2-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.http2.AbstractHttp2StreamChannel$Http2ChannelUnsafe.writeHttp2StreamFrame(AbstractHttp2StreamChannel.java:961) ~[netty-codec-http2-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.http2.AbstractHttp2StreamChannel$Http2ChannelUnsafe.write(AbstractHttp2StreamChannel.java:932) ~[netty-codec-http2-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.DefaultChannelPipeline$HeadContext.write(DefaultChannelPipeline.java:1367) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:877) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:863) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:968) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:856) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:851) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.MessageToMessageEncoder.writePromiseCombiner(MessageToMessageEncoder.java:140) ~[netty-codec-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.MessageToMessageEncoder.write(MessageToMessageEncoder.java:120) ~[netty-codec-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.MessageToMessageCodec.write(MessageToMessageCodec.java:116) ~[netty-codec-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:879) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:863) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:968) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:856) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at reactor.netty.http.client.Http2StreamBridgeClientHandler.write(Http2StreamBridgeClientHandler.java:51) ~[reactor-netty-http-1.1.7.jar:1.1.7]
	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:879) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:863) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:968) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:856) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at reactor.netty.http.client.AbstractHttpClientMetricsHandler.write(AbstractHttpClientMetricsHandler.java:116) ~[reactor-netty-http-1.1.7.jar:1.1.7]
	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:879) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeWriteAndFlush(AbstractChannelHandlerContext.java:940) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:966) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.writeAndFlush(AbstractChannelHandlerContext.java:934) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.AbstractChannelHandlerContext.writeAndFlush(AbstractChannelHandlerContext.java:984) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.DefaultChannelPipeline.writeAndFlush(DefaultChannelPipeline.java:1025) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.handler.codec.http2.AbstractHttp2StreamChannel.writeAndFlush(AbstractHttp2StreamChannel.java:490) ~[netty-codec-http2-4.1.92.Final.jar:4.1.92.Final]
	at reactor.netty.http.HttpOperations.lambda$send$0(HttpOperations.java:143) ~[reactor-netty-http-1.1.7.jar:1.1.7]
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:132) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2545) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.request(FluxContextWrite.java:136) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:171) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.request(FluxContextWrite.java:136) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2341) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2215) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onSubscribe(FluxContextWrite.java:101) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:96) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onSubscribe(FluxContextWrite.java:101) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.Mono.subscribe(Mono.java:4485) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.FluxConcatIterable$ConcatIterableSubscriber.onComplete(FluxConcatIterable.java:147) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.FluxConcatIterable.subscribe(FluxConcatIterable.java:60) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.MonoFromFluxOperator.subscribe(MonoFromFluxOperator.java:81) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.Operators$MonoInnerProducerBase.complete(Operators.java:2811) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.MonoSingle$SingleSubscriber.onComplete(MonoSingle.java:180) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:152) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2547) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:171) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.MonoSingle$SingleSubscriber.doOnRequest(MonoSingle.java:103) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.Operators$MonoInnerProducerBase.request(Operators.java:2878) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2341) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2215) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.MonoSingle$SingleSubscriber.onSubscribe(MonoSingle.java:115) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:96) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.FluxFromMonoOperator.subscribe(FluxFromMonoOperator.java:83) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.FluxDeferContextual.subscribe(FluxDeferContextual.java:57) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.netty.http.client.HttpClientConnect$HttpIOHandlerObserver.onStateChange(HttpClientConnect.java:445) ~[reactor-netty-http-1.1.7.jar:1.1.7]
	at reactor.netty.ReactorNetty$CompositeConnectionObserver.onStateChange(ReactorNetty.java:710) ~[reactor-netty-core-1.1.7.jar:1.1.7]
	at reactor.netty.http.client.Http2ConnectionProvider$DisposableAcquire.operationComplete(Http2ConnectionProvider.java:381) ~[reactor-netty-http-1.1.7.jar:1.1.7]
	at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:590) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:557) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:492) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.util.concurrent.DefaultPromise.addListener(DefaultPromise.java:185) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.util.concurrent.DefaultPromise.addListener(DefaultPromise.java:35) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
	at reactor.netty.http.client.Http2ConnectionProvider$DisposableAcquire.onNext(Http2ConnectionProvider.java:317) ~[reactor-netty-http-1.1.7.jar:1.1.7]
	at reactor.netty.http.client.Http2ConnectionProvider$DisposableAcquire.onNext(Http2ConnectionProvider.java:210) ~[reactor-netty-http-1.1.7.jar:1.1.7]
	at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107) ~[reactor-core-3.5.6.jar:3.5.6]
	at reactor.netty.http.client.Http2Pool$Borrower.deliver(Http2Pool.java:805) ~[reactor-netty-http-1.1.7.jar:1.1.7]
	at reactor.netty.http.client.Http2Pool.lambda$drainLoop$1(Http2Pool.java:387) ~[reactor-netty-http-1.1.7.jar:1.1.7]
	at io.netty.util.concurrent.AbstractEventExecutor.runTask$$$capture(AbstractEventExecutor.java:174) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute$$$capture(AbstractEventExecutor.java:167) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
	at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]

2023-10-06T20:26:41.589-04:00 DEBUG 24876 --- [ctor-http-nio-4] r.n.http.client.Http2ConnectionProvider  : [f0195865/2-1, L:/127.0.0.1:63494 ! R:localhost/127.0.0.1:9995] Stream opened, now: 0 active streams and 2147483647 max active streams.
2023-10-06T20:26:41.590-04:00  WARN 24876 --- [ctor-http-nio-4] r.netty.http.client.HttpClientConnect    : [f0195865/2-1, L:/127.0.0.1:63494 ! R:localhost/127.0.0.1:9995] The connection observed an error

reactor.netty.http.client.PrematureCloseException: Connection prematurely closed BEFORE response

`

Steps to Reproduce

Use client-side protocols of H1 & H2 forcing upgrade of connection from H1 to H2.

Send a second request (after initial connect) that exceeds header limit, and PrematureCloseException will be seen by client.

Possible Solution

@pderop Not sure why your comment was deleted, but many thanks! The change makes perfect sense given the request is never sent to the server.

Thanks again for looking into this. Our application logic will be able to make good use of the cause.