Closed
Description
Affects: <Spring Framework version 5.3.22, 5.3.23, maybe earlier>
my codes
step 0(optional): prepare the spring boot project, add dependency: org.springframework.boot:spring-boot-starter-websocket
step 1: custom HandshakeInterceptor
@Component
public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
map.put("uid", null);// null value not controlled from some logic
return true;
}
@Override
public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
}
}
step2: WebSocketConfigurer
@EnableWebSocket
@Configuration
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry.addHandler(new TextWebSocketHandler(), "/ws/**")
.addInterceptors(new WebSocketHandshakeInterceptor())
.setAllowedOrigins("*");
}
}
step3: client html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebSocker Client</title>
</head>
<body>
<button onclick="contactServer">Click Here</button>
</body>
<script>
const socket = new WebSocket('ws://localhost:8080/ws');
socket.addEventListener('open', function (event) {
socket.send('Connection Established');
});
socket.addEventListener('message', function (event) {
console.log(event.data);
});
const contactServer = () => {
socket.send("Initialize");
}
</script>
</html>
error log
nested exception is org.springframework.web.socket.server.HandshakeFailureException: Uncaught failure for request http://localhost:8080/ws; nested exception is java.lang.NullPointerException] with root cause
------- origin log -------
2022-10-13 14:57:21.509 DEBUG 8271 --- [nio-8080-exec-1] o.s.w.s.s.s.WebSocketHttpRequestHandler : GET /ws
2022-10-13 14:57:21.535 TRACE 8271 --- [nio-8080-exec-1] o.s.w.s.s.s.DefaultHandshakeHandler : Processing request http://localhost:8080/ws with headers=[host:"localhost:8080", connection:"Upgrade", pragma:"no-cache", cache-control:"no-cache", user-agent:"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", upgrade:"websocket", origin:"null", sec-websocket-version:"13", accept-encoding:"gzip, deflate, br", accept-language:"zh-CN,zh;q=0.9,ko;q=0.8,ja;q=0.7,en;q=0.6", sec-websocket-key:"egMr7FKax7k2Vtkgcv8Ksw==", sec-websocket-extensions:"permessage-deflate; client_max_window_bits"]
2022-10-13 14:57:21.537 TRACE 8271 --- [nio-8080-exec-1] o.s.w.s.s.s.DefaultHandshakeHandler : Upgrading to WebSocket, subProtocol=null, extensions=[]
2022-10-13 14:57:24.156 ERROR 8271 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.socket.server.HandshakeFailureException: Uncaught failure for request http://localhost:8080/ws; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException: null
at java.base/java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011) ~[na:na]
at java.base/java.util.concurrent.ConcurrentHashMap.putAll(ConcurrentHashMap.java:1089) ~[na:na]
at org.springframework.web.socket.adapter.AbstractWebSocketSession.<init>(AbstractWebSocketSession.java:65) ~[spring-websocket-5.3.23.jar:5.3.23]
at org.springframework.web.socket.adapter.standard.StandardWebSocketSession.<init>(StandardWebSocketSession.java:104) ~[spring-websocket-5.3.23.jar:5.3.23]
at org.springframework.web.socket.server.standard.AbstractStandardUpgradeStrategy.upgrade(AbstractStandardUpgradeStrategy.java:129) ~[spring-websocket-5.3.23.jar:5.3.23]
at org.springframework.web.socket.server.support.AbstractHandshakeHandler.doHandshake(AbstractHandshakeHandler.java:297) ~[spring-websocket-5.3.23.jar:5.3.23]
at org.springframework.web.socket.server.support.WebSocketHttpRequestHandler.handleRequest(WebSocketHttpRequestHandler.java:178) ~[spring-websocket-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter.handle(HttpRequestHandlerAdapter.java:52) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1071) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.23.jar:5.3.23]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:655) ~[tomcat-embed-core-9.0.65.jar:4.0.FR]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.23.jar:5.3.23]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) ~[tomcat-embed-core-9.0.65.jar:4.0.FR]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.23.jar:5.3.23]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.23.jar:5.3.23]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.23.jar:5.3.23]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1789) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
tracking & analysis
found code call path:
AbstractStandardUpgradeStrategy#upgrade
->
StandardWebSocketSession session = new StandardWebSocketSession(headers, attrs, localAddr, remoteAddr, user);
->StandardWebSocketSession#constructor
->first line super(attributes);
The code where the exception finally occurs:
// org.springframework.web.socket.adapter.AbstractWebSocketSession.java
public AbstractWebSocketSession(@Nullable Map<String, Object> attributes) {
if (attributes != null) {
this.attributes.putAll(attributes); // <----- HERE, java.util.concurrent.ConcurrentHashMap put null value occurs NPE
}
}
It is recommended to add a null value judgment and throw an exception or filter none-null key-value here。