Skip to content

Support configuring Jetty WebSocket server parameters #30344

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Simon3 opened this issue Apr 17, 2023 · 8 comments
Closed

Support configuring Jetty WebSocket server parameters #30344

Simon3 opened this issue Apr 17, 2023 · 8 comments
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Milestone

Comments

@Simon3
Copy link

Simon3 commented Apr 17, 2023

Affects: 6.0.7

It is not clear how to configure a WebSocket server using Jetty and Spring Boot 3 / Spring framework 6. More specifically, I want to configure stuff like idleTimeout.

The doc explained it well for Spring Boot 2 / Spring framework 5, but that doc is obsolete now that WebSocketPolicy can't be instantiated anymore, WebSocketServerFactory doesn't exist anymore, and JettyRequestUpgradeStrategy has been refactored with only a no-arg constructor. So it seems like that doc needs an update.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Apr 17, 2023
@rstoyanchev
Copy link
Contributor

Good point that we need to update the documentation there. From what I can see in this example, it involves a ServletContext initialization hook. However, I'm also wondering if you've tried this in Server Configuration that now probably also works for Jetty.

@rstoyanchev rstoyanchev added type: documentation A documentation task and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Apr 18, 2023
@rstoyanchev rstoyanchev added this to the 6.0.9 milestone Apr 18, 2023
@rstoyanchev rstoyanchev self-assigned this Apr 18, 2023
@jhoeller jhoeller added the in: web Issues in web modules (web, webmvc, webflux, websocket) label Apr 18, 2023
@Simon3
Copy link
Author

Simon3 commented Apr 18, 2023

Yes I have tried to define a ServletServerContainerFactoryBean bean but it didn't seem to be used. But maybe I have done something wrong.

@rstoyanchev
Copy link
Contributor

Actually I meant have you tried what's in the example I referenced under jetty-project?

@rstoyanchev rstoyanchev modified the milestones: 6.0.9, 6.0.10 May 9, 2023
@Simon3
Copy link
Author

Simon3 commented May 26, 2023

Not sure how to apply that in a Spring Boot environment... sorry.

@jhoeller jhoeller modified the milestones: 6.0.10, 6.0.11 Jun 14, 2023
@jhoeller jhoeller modified the milestones: 6.0.11, 6.0.12 Jul 12, 2023
@peterhalicky
Copy link

I have the same problem. The example referenced by @rstoyanchev didn't help. Also when trying to use the Server Configuration, first I had to add jakarta.websocket-api 2.0.0 to classpath, because it was looking for class jakarta.websocket.WebSocketContainer that it didn't find (ClassNotFoundException). Then I got "Attribute 'jakarta.websocket.server.ServerContainer' not found in ServletContext", which -- I suppose -- means that no, this approach doesn't work on Jetty.

@djivko
Copy link

djivko commented Sep 1, 2023

For anyone finding this issue. After pouring through spring code I ended up with 2 approaches for setting the configuration for Jetty.

Approach 1:

@Bean
public JettyWebSocketServletWebServerCustomizer websocketServletWebServerCustomizer() {
  return new JettyCustomizer();
}

public class JettyCustomizer extends JettyWebSocketServletWebServerCustomizer {
  public JettyCustomizer() {
  }

  @Override
  public void customize(JettyServletWebServerFactory factory) {
    super.customize(factory);
    factory.addConfigurations(new AbstractConfiguration() {

      @Override
      public void configure(WebAppContext context) throws Exception {
        JettyWebSocketServerContainer container =
            JettyWebSocketServerContainer.getContainer(context.getServletContext());
        if (container != null) {
          container.setInputBufferSize(textMessageMaxSize);
          container.setMaxTextMessageSize(textMessageMaxSize);
        }
      }
    });
  }
}

This will override the customizer provided by WebSocketServletAutoConfiguration

Approach 2 using a handshake handler:

@Bean
public DefaultHandshakeHandler handshakeHandler() {
  return new DefaultHandshakeHandler(new ServletContextAwareJettyRequestUpgradeStrategy());
}

public class ServletContextAwareJettyRequestUpgradeStrategy extends JettyRequestUpgradeStrategy
    implements ServletContextAware {
  private ServletContext servletContext;

  public ServletContextAwareJettyRequestUpgradeStrategy() {
    super();
  }

  @Override
  public void setServletContext(ServletContext servletContext) {
    this.servletContext = servletContext;
  }

  @Override
  public void upgrade(ServerHttpRequest request, ServerHttpResponse response, @Nullable String selectedProtocol,
      List<WebSocketExtension> selectedExtensions, @Nullable Principal user,
      org.springframework.web.socket.WebSocketHandler handler, Map<String, Object> attributes)
      throws HandshakeFailureException {
    if (servletContext != null) {
      JettyWebSocketServerContainer container = JettyWebSocketServerContainer.getContainer(servletContext);
      if (container != null) {
        container.setInputBufferSize(textMessageMaxSize);
        container.setMaxTextMessageSize(textMessageMaxSize);
      }
    }
    super.upgrade(request, response, selectedProtocol, selectedExtensions, user, handler, attributes);
  }
}

I would love to hear somebody from Spring devs commenting whether there is more elegant way of setting the configuration.

Note: Ideally my expectation was that Spring provides a way for devs to provide a bean implementation of JettyWebSocketServletContainerInitializer.Configurator interface that could take care of that config, but I wasn't able to find how to do that.

@rstoyanchev
Copy link
Contributor

rstoyanchev commented Sep 8, 2023

Thanks for the feedback @djivko.

We can probably expose a Consumer<JettyWebSocketServerContainer> on JettyRequestUpgradeStrategy along the lines of your approach 2. Ideally it should be applied once on startup rather than on every upgrade. Have you tried to access it from ServletContextAware#setServletContext?

JettyWebSocketServerContainer seems to be initialized pretty early through a ServletContainerInitializer, and I expect that JettyWebSocketServerContainer getContainer(servletContext), which relies on a ServletContext attribute might work already at that point.

@rstoyanchev rstoyanchev changed the title WebSocket server configuration for Jetty Support configuring Jetty WebSocket server parameters Sep 13, 2023
@rstoyanchev
Copy link
Contributor

I have confirmed that JettyWebSocketServerContainer is initialized and set in a ServletContext attribute by the time of the ServletContextAware callback. So I'm going to expose a Consumer<Configurable> property on JettyRequestUpgradeStrategy, similar to how it was possible to configure a WebSocketPolicy in the same place.

Unfortunately this cannot be done in 6.0.x since the Configurable type is new in Jetty 12 but it's possible to work around it along the lines of #30344 (comment).

@rstoyanchev rstoyanchev modified the milestones: 6.0.12, 6.1.0-M5 Sep 13, 2023
@rstoyanchev rstoyanchev added type: enhancement A general enhancement and removed type: documentation A documentation task labels Sep 13, 2023
rstoyanchev added a commit that referenced this issue Sep 13, 2023
For WebFlux we can't use ServletContextAware.

See gh-30344
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

6 participants