Thread-safe access to WebSocketServerFactory and WebSocketExtensions

See gh-24745
This commit is contained in:
Juergen Hoeller 2020-03-23 17:56:14 +01:00
parent b7b36891fb
commit fd1ca46ca1
1 changed files with 31 additions and 17 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,6 +20,7 @@ import java.io.IOException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.security.Principal; import java.security.Principal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -72,7 +73,7 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv
private WebSocketPolicy policy; private WebSocketPolicy policy;
@Nullable @Nullable
private WebSocketServerFactory factory; private volatile WebSocketServerFactory factory;
@Nullable @Nullable
private ServletContext servletContext; private ServletContext servletContext;
@ -122,17 +123,20 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv
if (!isRunning()) { if (!isRunning()) {
this.running = true; this.running = true;
try { try {
if (this.factory == null) { WebSocketServerFactory factory = this.factory;
this.factory = new WebSocketServerFactory(this.servletContext, this.policy); if (factory == null) {
Assert.state(this.servletContext != null, "No ServletContext set");
factory = new WebSocketServerFactory(this.servletContext, this.policy);
this.factory = factory;
} }
this.factory.setCreator((request, response) -> { factory.setCreator((request, response) -> {
WebSocketHandlerContainer container = containerHolder.get(); WebSocketHandlerContainer container = containerHolder.get();
Assert.state(container != null, "Expected WebSocketHandlerContainer"); Assert.state(container != null, "Expected WebSocketHandlerContainer");
response.setAcceptedSubProtocol(container.getSelectedProtocol()); response.setAcceptedSubProtocol(container.getSelectedProtocol());
response.setExtensions(container.getExtensionConfigs()); response.setExtensions(container.getExtensionConfigs());
return container.getHandler(); return container.getHandler();
}); });
this.factory.start(); factory.start();
} }
catch (Throwable ex) { catch (Throwable ex) {
throw new IllegalStateException("Unable to start Jetty WebSocketServerFactory", ex); throw new IllegalStateException("Unable to start Jetty WebSocketServerFactory", ex);
@ -144,9 +148,10 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv
public void stop() { public void stop() {
if (isRunning()) { if (isRunning()) {
this.running = false; this.running = false;
if (this.factory != null) { WebSocketServerFactory factory = this.factory;
if (factory != null) {
try { try {
this.factory.stop(); factory.stop();
} }
catch (Throwable ex) { catch (Throwable ex) {
throw new IllegalStateException("Unable to stop Jetty WebSocketServerFactory", ex); throw new IllegalStateException("Unable to stop Jetty WebSocketServerFactory", ex);
@ -168,10 +173,12 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv
@Override @Override
public List<WebSocketExtension> getSupportedExtensions(ServerHttpRequest request) { public List<WebSocketExtension> getSupportedExtensions(ServerHttpRequest request) {
if (this.supportedExtensions == null) { List<WebSocketExtension> extensions = this.supportedExtensions;
this.supportedExtensions = buildWebSocketExtensions(); if (extensions == null) {
extensions = buildWebSocketExtensions();
this.supportedExtensions = extensions;
} }
return this.supportedExtensions; return extensions;
} }
private List<WebSocketExtension> buildWebSocketExtensions() { private List<WebSocketExtension> buildWebSocketExtensions() {
@ -185,8 +192,10 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv
@SuppressWarnings({"unchecked", "deprecation"}) @SuppressWarnings({"unchecked", "deprecation"})
private Set<String> getExtensionNames() { private Set<String> getExtensionNames() {
WebSocketServerFactory factory = this.factory;
Assert.state(factory != null, "No WebSocketServerFactory available");
try { try {
return this.factory.getAvailableExtensionNames(); return factory.getAvailableExtensionNames();
} }
catch (IncompatibleClassChangeError ex) { catch (IncompatibleClassChangeError ex) {
// Fallback for versions prior to 9.4.21: // Fallback for versions prior to 9.4.21:
@ -194,7 +203,8 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv
// 9.4.21.v20190926: ExtensionFactory (interface -> abstract class) + deprecated // 9.4.21.v20190926: ExtensionFactory (interface -> abstract class) + deprecated
Class<?> clazz = org.eclipse.jetty.websocket.api.extensions.ExtensionFactory.class; Class<?> clazz = org.eclipse.jetty.websocket.api.extensions.ExtensionFactory.class;
Method method = ClassUtils.getMethod(clazz, "getExtensionNames"); Method method = ClassUtils.getMethod(clazz, "getExtensionNames");
return (Set<String>) ReflectionUtils.invokeMethod(method, this.factory.getExtensionFactory()); Set<String> result = (Set<String>) ReflectionUtils.invokeMethod(method, factory.getExtensionFactory());
return (result != null ? result : Collections.emptySet());
} }
} }
@ -209,7 +219,9 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv
Assert.isInstanceOf(ServletServerHttpResponse.class, response, "ServletServerHttpResponse required"); Assert.isInstanceOf(ServletServerHttpResponse.class, response, "ServletServerHttpResponse required");
HttpServletResponse servletResponse = ((ServletServerHttpResponse) response).getServletResponse(); HttpServletResponse servletResponse = ((ServletServerHttpResponse) response).getServletResponse();
Assert.isTrue(this.factory.isUpgradeRequest(servletRequest, servletResponse), "Not a WebSocket handshake"); WebSocketServerFactory factory = this.factory;
Assert.state(factory != null, "No WebSocketServerFactory available");
Assert.isTrue(factory.isUpgradeRequest(servletRequest, servletResponse), "Not a WebSocket handshake");
JettyWebSocketSession session = new JettyWebSocketSession(attributes, user); JettyWebSocketSession session = new JettyWebSocketSession(attributes, user);
JettyWebSocketHandlerAdapter handlerAdapter = new JettyWebSocketHandlerAdapter(wsHandler, session); JettyWebSocketHandlerAdapter handlerAdapter = new JettyWebSocketHandlerAdapter(wsHandler, session);
@ -219,7 +231,7 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv
try { try {
containerHolder.set(container); containerHolder.set(container);
this.factory.acceptWebSocket(servletRequest, servletResponse); factory.acceptWebSocket(servletRequest, servletResponse);
} }
catch (IOException ex) { catch (IOException ex) {
throw new HandshakeFailureException( throw new HandshakeFailureException(
@ -235,12 +247,13 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv
private final JettyWebSocketHandlerAdapter handler; private final JettyWebSocketHandlerAdapter handler;
@Nullable
private final String selectedProtocol; private final String selectedProtocol;
private final List<ExtensionConfig> extensionConfigs; private final List<ExtensionConfig> extensionConfigs;
public WebSocketHandlerContainer( public WebSocketHandlerContainer(JettyWebSocketHandlerAdapter handler,
JettyWebSocketHandlerAdapter handler, String protocol, List<WebSocketExtension> extensions) { @Nullable String protocol, List<WebSocketExtension> extensions) {
this.handler = handler; this.handler = handler;
this.selectedProtocol = protocol; this.selectedProtocol = protocol;
@ -259,6 +272,7 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv
return this.handler; return this.handler;
} }
@Nullable
public String getSelectedProtocol() { public String getSelectedProtocol() {
return this.selectedProtocol; return this.selectedProtocol;
} }