Search code examples
validationhttp-headersspring-webfluxspring-webclientreactor-netty

How to disable header validation when using ReactorClientHttpConnector


I use WebClient with ReactorClientHttpConnector in this configuration:

    public WebClient webClient() {
        HttpClient httpClient = HttpClient.create();
        return WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .build();
    }

In this configuration, all headers of a request pass validation for forbidden characters such \u0000 The problem is that one of my headers contains forbidden symbols and I can't remove it. Therefore these requests fail with an exception:

Caused by: java.lang.IllegalArgumentException: a header value contains a prohibited character '': ��[����-�~~�|9qb}3&�!��\�  ��_��b�� %�@���'j�Uc���9;b�������[����������������k���/N|����0ѧ�&�&����h,n�ʶ��������[˭����DK~�GN�yu�#�w�����q&;���_y��o[_̯��U�Ӥ(�m�)?//���n�u�a�^*����9��+1,:��;��}��&o�5���Z�WY6_&�.�_������t�MxV�j�+uso�Fl߃4����q��ͧ�PΆ�z��`��gE}��'Cj�E���u�r��v�/~�<��{��?�rv|�]�x��)��<��(Cn2=/���M�dï�'��kh�R<���OC0�;���m�PG�LN?~6�_
    at io.netty.handler.codec.http.DefaultHttpHeaders$HeaderValueConverterAndValidator.validateValueChar(DefaultHttpHeaders.java:473) ~[netty-codec-http-4.1.67.Final.jar:4.1.67.Final]
    at io.netty.handler.codec.http.DefaultHttpHeaders$HeaderValueConverterAndValidator.convertObject(DefaultHttpHeaders.java:453) ~[netty-codec-http-4.1.67.Final.jar:4.1.67.Final]
    at io.netty.handler.codec.http.DefaultHttpHeaders$HeaderValueConverterAndValidator.convertObject(DefaultHttpHeaders.java:444) ~[netty-codec-http-4.1.67.Final.jar:4.1.67.Final]
    at io.netty.handler.codec.DefaultHeaders.setObject(DefaultHeaders.java:496) ~[netty-codec-4.1.67.Final.jar:4.1.67.Final]
    at io.netty.handler.codec.http.DefaultHttpHeaders.set(DefaultHttpHeaders.java:189) ~[netty-codec-http-4.1.67.Final.jar:4.1.67.Final]
    at org.springframework.http.client.reactive.ReactorClientHttpRequest.lambda$applyHeaders$3(ReactorClientHttpRequest.java:126) ~[spring-web-5.3.9.jar:5.3.9]
    at java.base/java.util.Map.forEach(Map.java:661) ~[na:na]
    at org.springframework.http.client.reactive.ReactorClientHttpRequest.applyHeaders(ReactorClientHttpRequest.java:126) ~[spring-web-5.3.9.jar:5.3.9]
    at org.springframework.http.client.reactive.AbstractClientHttpRequest.lambda$null$0(AbstractClientHttpRequest.java:132) ~[spring-web-5.3.9.jar:5.3.9]
    at reactor.core.publisher.MonoRunnable.subscribe(MonoRunnable.java:49) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.Mono.subscribe(Mono.java:4338) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.FluxConcatIterable$ConcatIterableSubscriber.onComplete(FluxConcatIterable.java:147) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.FluxConcatIterable.subscribe(FluxConcatIterable.java:60) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.MonoFromFluxOperator.subscribe(MonoFromFluxOperator.java:81) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:157) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.Operators$MonoInnerProducerBase.complete(Operators.java:2664) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.MonoSingle$SingleSubscriber.onComplete(MonoSingle.java:180) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:150) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2400) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:169) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.MonoSingle$SingleSubscriber.doOnRequest(MonoSingle.java:103) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.Operators$MonoInnerProducerBase.request(Operators.java:2731) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2194) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2068) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.MonoSingle$SingleSubscriber.onSubscribe(MonoSingle.java:115) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:96) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.4.9.jar:3.4.9]
    at reactor.netty.http.client.HttpClientConnect$HttpIOHandlerObserver.onStateChange(HttpClientConnect.java:424) ~[reactor-netty-http-1.0.10.jar:1.0.10]
    at reactor.netty.ReactorNetty$CompositeConnectionObserver.onStateChange(ReactorNetty.java:654) ~[reactor-netty-core-1.0.10.jar:1.0.10]
    at reactor.netty.resources.DefaultPooledConnectionProvider$DisposableAcquire.run(DefaultPooledConnectionProvider.java:287) ~[reactor-netty-core-1.0.10.jar:1.0.10]
    at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164) ~[netty-common-4.1.67.Final.jar:4.1.67.Final]
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:469) ~[netty-common-4.1.67.Final.jar:4.1.67.Final]
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:500) ~[netty-transport-4.1.67.Final.jar:4.1.67.Final]
    at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986) ~[netty-common-4.1.67.Final.jar:4.1.67.Final]
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.67.Final.jar:4.1.67.Final]
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.67.Final.jar:4.1.67.Final]
    at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

Is there a way to disable this validation?


Solution

  • Bear in mind header validation is in place for a reason by default - disabling it can leave you open to security vulnerabilities like CWE-113.

    That being said, yes, if you have a genuine reason to then you can disable header validation on the HttpClient side:

    HttpClient httpClient = HttpClient.create()
            .httpResponseDecoder(spec -> spec.validateHeaders(false));