javaspring-bootspring-mvcspring-webflux

The www-authenticate header breaks JettyClientHttpConnector


Environment

Old surroundings where it operated

Context

We have migrated from a Spring Boot 2.7.18 environment to Spring Boot 3.3.4 and we have detected that when using a WebClient with a JettyClientHttpConnector with its respective HttpClient, in case the call comes with a www-authenticate header, it gets stuck making calls infinitely and we never get it to respond correctly.

All this worked perfectly in the Spring Boot 2 version, where upon receiving the call, it responded without any problem.

We have tried to define the connector in the most basic way possible, but it always breaks, in case of receiving that header.

Examples and Code

We have done the test, simply by telling WebClient that the connector we are going to use is JettyClientHttpConnector.

WebClient client =
        WebClient.builder()
            .baseUrl("your-url-with-www-authenticate-header-in-response")
            .clientConnector(new JettyClientHttpConnector())
            .build();

If you try to make a call to any endpoint, which returns the www-authenticate header, you can see that it gets stuck and after passing the timeout, it throws this error:

onFillableFail SslConnection@10ac4a81::SocketChannelEndPoint@4d09e077[{l=null,r=null,CLOSED,fill=-,flush=-,to=7/30000}{io=1/1,kio=-1,kro=-1}]->[SslConnection@10ac4a81{NOT_HANDSHAKING,eio=-1/-1,di=-1,fill=INTERESTED,flush=IDLE}~>{l=null,r=null,CLOSED,fill=FI,flush=-,to=30027/30000}=>HttpConnectionOverHTTP@46f46e11(l:null <-> r:null,closed=true)=>HttpChannelOverHTTP@11d2c7ca(exchange=null)[send=HttpSenderOverHTTP@4f015fae(req=QUEUED,failure=null)[HttpGenerator@3d8bec8d{s=START}],recv=HttpReceiverOverHTTP@69f1010a(ex=null,rsp=IDLE,failure=null)[HttpParser{s=START,0 of -1}]]]
java.nio.channels.ClosedChannelException: null
    at org.eclipse.jetty.io.FillInterest.onClose(FillInterest.java:147)
    at org.eclipse.jetty.io.AbstractEndPoint.onClose(AbstractEndPoint.java:336)
    at org.eclipse.jetty.io.SelectableChannelEndPoint.onClose(SelectableChannelEndPoint.java:165)
    at org.eclipse.jetty.io.AbstractEndPoint.onClose(AbstractEndPoint.java:326)
    at org.eclipse.jetty.io.AbstractEndPoint.doOnClose(AbstractEndPoint.java:253)
    at org.eclipse.jetty.io.AbstractEndPoint.close(AbstractEndPoint.java:217)
    at org.eclipse.jetty.io.AbstractEndPoint.close(AbstractEndPoint.java:200)
    at org.eclipse.jetty.io.ssl.SslConnection$SslEndPoint.disconnect(SslConnection.java:1399)
    at org.eclipse.jetty.io.ssl.SslConnection$SslEndPoint.doShutdownOutput(SslConnection.java:1381)
    at org.eclipse.jetty.io.ssl.SslConnection$SslEndPoint.doClose(SslConnection.java:1449)
    at org.eclipse.jetty.io.AbstractEndPoint.doOnClose(AbstractEndPoint.java:248)
    at org.eclipse.jetty.io.AbstractEndPoint.close(AbstractEndPoint.java:217)
    at org.eclipse.jetty.io.AbstractEndPoint.close(AbstractEndPoint.java:200)
    at org.eclipse.jetty.client.transport.internal.HttpConnectionOverHTTP.close(HttpConnectionOverHTTP.java:298)
    at org.eclipse.jetty.client.transport.internal.HttpConnectionOverHTTP.onIdleExpired(HttpConnectionOverHTTP.java:237)
    at org.eclipse.jetty.io.ssl.SslConnection.onIdleExpired(SslConnection.java:371)
    at org.eclipse.jetty.io.AbstractEndPoint.onIdleExpired(AbstractEndPoint.java:389)
    at org.eclipse.jetty.io.IdleTimeout.checkIdleTimeout(IdleTimeout.java:172)
    at org.eclipse.jetty.io.IdleTimeout.idleCheck(IdleTimeout.java:113)

Also, it seems to retry the call until finally the error appears, but the call gets hung up and never resolves.

[DEBUG] [ o.e.j.u.t.ReservedThreadExecutor: 348 ]  waiting for task
[DEBUG] [ com.zaxxer.hikari.pool.HikariPool: 405 ]  HikariPool-1 - Pool stats (total=10, active=0, idle=10, waiting=0)
[DEBUG] [ com.zaxxer.hikari.pool.HikariPool: 510 ]  HikariPool-1 - Fill pool skipped, pool has sufficient level or currently being filled.
[DEBUG] [ o.e.j.util.thread.QueuedThreadPool: 1015 ]  Evict check, period=60000ms QueuedThreadPool[HttpClient@7218bb00]@42681628{STARTED,8<=12<=200,i=3,r=-1,t=-67ms,q=0}[ReservedThreadExecutor@7457d199{capacity=16,threads=ThreadIdPool@397ce9d8{capacity=16}}]
[DEBUG] [ o.e.j.util.thread.QueuedThreadPool: 1033 ]  Evict skipped, threshold=59932ms in the future QueuedThreadPool[HttpClient@7218bb00]@42681628{STARTED,8<=12<=200,i=3,r=-1,t=-67ms,q=0}[ReservedThreadExecutor@7457d199{capacity=16,threads=ThreadIdPool@397ce9d8{capacity=16}}]
[DEBUG] [ o.e.j.u.t.ReservedThreadExecutor: 348 ]  waiting for task
[DEBUG] [ o.e.j.util.thread.QueuedThreadPool: 1015 ]  Evict check, period=60000ms QueuedThreadPool[HttpClient@7218bb00]@42681628{STARTED,8<=12<=200,i=3,r=-1,t=-74ms,q=0}[ReservedThreadExecutor@7457d199{capacity=16,threads=ThreadIdPool@397ce9d8{capacity=16}}]
[DEBUG] [ o.e.j.util.thread.QueuedThreadPool: 1033 ]  Evict skipped, threshold=59925ms in the future QueuedThreadPool[HttpClient@7218bb00]@42681628{STARTED,8<=12<=200,i=3,r=-1,t=-75ms,q=0}[ReservedThreadExecutor@7457d199{capacity=16,threads=ThreadIdPool@397ce9d8{capacity=16}}]
[DEBUG] [ com.zaxxer.hikari.pool.PoolBase: 133 ]  HikariPool-1 - Closing connection oracle.jdbc.driver.T4CConnection@24c9581b: (connection has passed maxLifetime)
[DEBUG] [ com.zaxxer.hikari.pool.HikariPool: 728 ]  HikariPool-1 - Added connection oracle.jdbc.driver.T4CConnection@c485e55
[DEBUG] [ com.zaxxer.hikari.pool.HikariPool: 405 ]  HikariPool-1 - Connection not added, stats (total=10, active=0, idle=10, waiting=0)

However, with exactly the same code in Spring Boot 2, it resolves the call without a problem.

Thanks for help!


Solution

  • We opened an issue in the spring-framework project and it seems to be a problem with Jetty's HttpClient, who are currently working to fix the problem.

    But a solution was proposed in that same thread that has worked for us in.

    I hope it works for you.