|
|
|
|
@ -16,23 +16,46 @@
@@ -16,23 +16,46 @@
|
|
|
|
|
|
|
|
|
|
package org.springframework.boot.autoconfigure.websocket.servlet; |
|
|
|
|
|
|
|
|
|
import java.io.IOException; |
|
|
|
|
import java.util.Map; |
|
|
|
|
import java.util.stream.Stream; |
|
|
|
|
|
|
|
|
|
import jakarta.servlet.DispatcherType; |
|
|
|
|
import jakarta.servlet.Filter; |
|
|
|
|
import jakarta.servlet.FilterChain; |
|
|
|
|
import jakarta.servlet.ServletException; |
|
|
|
|
import jakarta.servlet.ServletRequest; |
|
|
|
|
import jakarta.servlet.ServletResponse; |
|
|
|
|
import jakarta.servlet.http.HttpServletResponse; |
|
|
|
|
import jakarta.websocket.DeploymentException; |
|
|
|
|
import jakarta.websocket.server.ServerContainer; |
|
|
|
|
import jakarta.websocket.server.ServerEndpoint; |
|
|
|
|
import org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter; |
|
|
|
|
import org.junit.jupiter.api.Test; |
|
|
|
|
import org.junit.jupiter.params.ParameterizedTest; |
|
|
|
|
import org.junit.jupiter.params.provider.Arguments; |
|
|
|
|
import org.junit.jupiter.params.provider.MethodSource; |
|
|
|
|
|
|
|
|
|
import org.springframework.boot.autoconfigure.AutoConfigurations; |
|
|
|
|
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; |
|
|
|
|
import org.springframework.boot.test.context.runner.WebApplicationContextRunner; |
|
|
|
|
import org.springframework.boot.test.web.client.TestRestTemplate; |
|
|
|
|
import org.springframework.boot.testsupport.classpath.ForkedClassPath; |
|
|
|
|
import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories; |
|
|
|
|
import org.springframework.boot.testsupport.web.servlet.Servlet5ClassPathOverrides; |
|
|
|
|
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; |
|
|
|
|
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; |
|
|
|
|
import org.springframework.boot.web.server.WebServer; |
|
|
|
|
import org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor; |
|
|
|
|
import org.springframework.boot.web.servlet.FilterRegistrationBean; |
|
|
|
|
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; |
|
|
|
|
import org.springframework.boot.web.servlet.server.ServletWebServerFactory; |
|
|
|
|
import org.springframework.context.annotation.Bean; |
|
|
|
|
import org.springframework.context.annotation.Configuration; |
|
|
|
|
import org.springframework.core.Ordered; |
|
|
|
|
import org.springframework.http.HttpStatus; |
|
|
|
|
import org.springframework.http.RequestEntity; |
|
|
|
|
import org.springframework.http.ResponseEntity; |
|
|
|
|
|
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat; |
|
|
|
|
|
|
|
|
|
@ -56,18 +79,90 @@ class WebSocketServletAutoConfigurationTests {
@@ -56,18 +79,90 @@ class WebSocketServletAutoConfigurationTests {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ParameterizedTest(name = "{0}") |
|
|
|
|
@MethodSource("testConfiguration") |
|
|
|
|
@ForkedClassPath |
|
|
|
|
void webSocketUpgradeDoesNotPreventAFilterFromRejectingTheRequest(String server, Class<?>... configuration) |
|
|
|
|
throws DeploymentException { |
|
|
|
|
try (AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext( |
|
|
|
|
configuration)) { |
|
|
|
|
ServerContainer serverContainer = (ServerContainer) context.getServletContext() |
|
|
|
|
.getAttribute("jakarta.websocket.server.ServerContainer"); |
|
|
|
|
serverContainer.addEndpoint(TestEndpoint.class); |
|
|
|
|
WebServer webServer = context.getWebServer(); |
|
|
|
|
int port = webServer.getPort(); |
|
|
|
|
TestRestTemplate rest = new TestRestTemplate(); |
|
|
|
|
RequestEntity<Void> request = RequestEntity.get("http://localhost:" + port) |
|
|
|
|
.header("Upgrade", "websocket") |
|
|
|
|
.header("Connection", "upgrade") |
|
|
|
|
.header("Sec-WebSocket-Version", "13") |
|
|
|
|
.header("Sec-WebSocket-Key", "key") |
|
|
|
|
.build(); |
|
|
|
|
ResponseEntity<Void> response = rest.exchange(request, Void.class); |
|
|
|
|
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
@SuppressWarnings("rawtypes") |
|
|
|
|
void whenCustomUpgradeFilterRegistrationIsDefinedAutoConfiguredRegistrationOfJettyUpgradeFilterBacksOff() { |
|
|
|
|
new WebApplicationContextRunner() |
|
|
|
|
.withConfiguration(AutoConfigurations.of(JettyConfiguration.class, |
|
|
|
|
WebSocketServletAutoConfiguration.JettyWebSocketConfiguration.class)) |
|
|
|
|
.withUserConfiguration(CustomUpgradeFilterRegistrationConfiguration.class) |
|
|
|
|
.run((context) -> { |
|
|
|
|
Map<String, FilterRegistrationBean> filterRegistrations = context |
|
|
|
|
.getBeansOfType(FilterRegistrationBean.class); |
|
|
|
|
assertThat(filterRegistrations).containsOnlyKeys("unauthorizedFilter", |
|
|
|
|
"customUpgradeFilterRegistration"); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
@SuppressWarnings("rawtypes") |
|
|
|
|
void whenCustomUpgradeFilterIsDefinedAutoConfiguredRegistrationOfJettyUpgradeFilterBacksOff() { |
|
|
|
|
new WebApplicationContextRunner() |
|
|
|
|
.withConfiguration(AutoConfigurations.of(JettyConfiguration.class, |
|
|
|
|
WebSocketServletAutoConfiguration.JettyWebSocketConfiguration.class)) |
|
|
|
|
.withUserConfiguration(CustomUpgradeFilterConfiguration.class) |
|
|
|
|
.run((context) -> { |
|
|
|
|
Map<String, FilterRegistrationBean> filterRegistrations = context |
|
|
|
|
.getBeansOfType(FilterRegistrationBean.class); |
|
|
|
|
assertThat(filterRegistrations).containsOnlyKeys("unauthorizedFilter"); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static Stream<Arguments> testConfiguration() { |
|
|
|
|
String response = "Tomcat"; |
|
|
|
|
return Stream.of( |
|
|
|
|
Arguments.of("Jetty", |
|
|
|
|
new Class<?>[] { JettyConfiguration.class, |
|
|
|
|
new Class<?>[] { JettyConfiguration.class, DispatcherServletAutoConfiguration.class, |
|
|
|
|
WebSocketServletAutoConfiguration.JettyWebSocketConfiguration.class }), |
|
|
|
|
Arguments.of("Tomcat", new Class<?>[] { TomcatConfiguration.class, |
|
|
|
|
WebSocketServletAutoConfiguration.TomcatWebSocketConfiguration.class })); |
|
|
|
|
Arguments.of(response, |
|
|
|
|
new Class<?>[] { TomcatConfiguration.class, DispatcherServletAutoConfiguration.class, |
|
|
|
|
WebSocketServletAutoConfiguration.TomcatWebSocketConfiguration.class })); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Configuration(proxyBeanMethods = false) |
|
|
|
|
static class CommonConfiguration { |
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
FilterRegistrationBean<Filter> unauthorizedFilter() { |
|
|
|
|
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>(new Filter() { |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) |
|
|
|
|
throws IOException, ServletException { |
|
|
|
|
((HttpServletResponse) response).sendError(HttpStatus.UNAUTHORIZED.value()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
}); |
|
|
|
|
registration.setOrder(Ordered.HIGHEST_PRECEDENCE); |
|
|
|
|
registration.addUrlPatterns("/*"); |
|
|
|
|
registration.setDispatcherTypes(DispatcherType.REQUEST); |
|
|
|
|
return registration; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
WebServerFactoryCustomizerBeanPostProcessor ServletWebServerCustomizerBeanPostProcessor() { |
|
|
|
|
return new WebServerFactoryCustomizerBeanPostProcessor(); |
|
|
|
|
@ -100,4 +195,31 @@ class WebSocketServletAutoConfigurationTests {
@@ -100,4 +195,31 @@ class WebSocketServletAutoConfigurationTests {
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Configuration(proxyBeanMethods = false) |
|
|
|
|
static class CustomUpgradeFilterRegistrationConfiguration { |
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
FilterRegistrationBean<WebSocketUpgradeFilter> customUpgradeFilterRegistration() { |
|
|
|
|
FilterRegistrationBean<WebSocketUpgradeFilter> registration = new FilterRegistrationBean<>( |
|
|
|
|
new WebSocketUpgradeFilter()); |
|
|
|
|
return registration; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Configuration(proxyBeanMethods = false) |
|
|
|
|
static class CustomUpgradeFilterConfiguration { |
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
WebSocketUpgradeFilter customUpgradeFilter() { |
|
|
|
|
return new WebSocketUpgradeFilter(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ServerEndpoint("/") |
|
|
|
|
public static class TestEndpoint { |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|