Encountered behaviour

After the upgrade to spring boot 3.2.0 or 3.2.1 (jetty 12) the HTTP connections always time out after 30000 ms, regardless of the value set through the server.jetty.connection-idle-timout property.

java.util.concurrent.TimeoutException: Idle timeout expired: 30004/30000 ms

It seems that the timeout value was hardcoded during the upgrade to jetty 12 with this change.

Behaviour before upgrade

Previously (with spring-boot 3.1.5 in combination with jetty 11), no http idle timeout was set in the HttpConfiguration, so the default of jetty is to use the connection idle timeout, set via the property server.jetty.connection-idle-timout.

How to reproduce

If needed, I can provide an example and steps how to reproduce the issue.

Conclusion

Was this change intentional or an oversight? Either way, imo this idle-timeout should be somehow configurable and not hard-coded.

Comment From: wilkinsona

Any change in this area wasn't intentional but as far as I can tell it continues to be configurable. The configured idle timeout is applied to each of the server's connectors:

https://github.com/spring-projects/spring-boot/blob/7216d2b031be648763744a371b871fa7c320c722/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java#L115-L123

The result of this can be seen when enabling debug-level logging for org.eclipse.jetty.io. For example, here's the output with an idle timeout of 1000ms:

2024-01-03T09:48:19.999Z DEBUG 93967 --- [tp1746578747-39] org.eclipse.jetty.io.IdleTimeout         : Setting idle timeout 0 -> 1000 on SocketChannelEndPoint@d98ae3b[{l=/[0:0:0:0:0:0:0:1]:8080,r=/[0:0:0:0:0:0:0:1]:61039,OPEN,fill=-,flush=-,to=2/1000}{io=0/0,kio=0,kro=0}]->[<null>]

If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.

Comment From: AElmecker

Yes, the connector itself gets configured with the right idle-timeout through the property and the timeout is also applied: https://github.com/jetty/jetty.project/blob/jetty-12.0.x/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java#L445-L450

But at a later point when the HTTP request is initiated, the timeout gets updated with the value of the HttpConfiguration as you can see here: https://github.com/jetty/jetty.project/blob/jetty-12.0.x/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java#L259-L262

Previously in spring-boot 3.1.5 the value for the http idle-timeout (in HttpConfiguration) was not overridden thus being jettys default value of -1, which means that it won't override the connection idle-timeout.

Sure, I will create a small example and provide it here for you.

Comment From: wilkinsona

Sure, I will create a small example and provide it here for you.

No need now, thank you. The additional explanation above has made the cause clear.

Unfortunately, GitHub's collapsing of larger diffs meant that https://github.com/spring-projects/spring-boot/commit/ed5d16de8408be54b19134e00f24fb1bd2ab9d85#diff-ac1beefcaa8b25ba950438b71795d0ff1b585ed0589d8151755b5aaace5c69e9R216 didn't link to a specific line.

Comment From: AElmecker

You're welcome, if you need any more help/insight I can provide, please say so. I already did some investigation before creating the issue, so I might be able to provide useful info.

Oh I see, that's a bummer with the large diffs.

Comment From: AElmecker

As a short term fix for now, I came up with this:

@Override
    public void customize(ConfigurableJettyWebServerFactory factory) {
        factory.addServerCustomizers(
            server -> {
                for (Connector connector : server.getConnectors()) {
                    connector.getConnectionFactories().forEach(c -> {
                        if (c instanceof HttpConnectionFactory) {
                            // -1 for idle-timeout means use the connection-idle-timeout, in our case defined by the
                            // spring property: jetty.connection-idle-timeout
                            ((HttpConnectionFactory) c).getHttpConfiguration().setIdleTimeout(-1);
                        }
                    });
                }
            }
        );
    }

To override the hardcoded value in the HttpConfiguration, not sure if that's the smartest way...

Comment From: GitXm123

Hi @AElmecker  Thanks for raising this issue. I encountered the same problem in my springboot 3.2 project and using RestTemplate when calling an external service that takes more than 30 seconds to complete. Can you please explain a bit more about the fix you proposed. Are you creating a bean that extends the JettyWebServerFactoryCustomizer class and override the customize method? I tried to do this but I got application startup-error "A bean with that name has already been defined "