This is an automated email from the ASF dual-hosted git repository.

Arsnael pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git


The following commit(s) were added to refs/heads/master by this push:
     new 274f65f94d [ENHANCEMENT] Prevent verbose logs when WS client 
disconnect during handshake
274f65f94d is described below

commit 274f65f94ddcae706d1d1017fe8d6afefb2daee5
Author: Benoit TELLIER <[email protected]>
AuthorDate: Fri Sep 25 15:43:28 2026 +0200

    [ENHANCEMENT] Prevent verbose logs when WS client disconnect during 
handshake
    
    Solves this:
    
    java.lang.NullPointerException: Cannot invoke 
"java.lang.Boolean.booleanValue()" because the return value of 
"io.netty.util.Attribute.get()" is null
            at reactor.netty.Connection.isPersistent(Connection.java:248)
            at 
reactor.netty.channel.ChannelOperations.isPersistent(ChannelOperations.java:607)
            at 
reactor.netty.ReactorNetty.addHandlerBeforeReactorEndHandlers(ReactorNetty.java:390)
            at reactor.netty.Connection.addHandlerLast(Connection.java:165)
            at 
reactor.netty.http.websocket.WebsocketInbound.lambda$aggregateFrames$0(WebsocketInbound.java:86)
            at 
reactor.netty.http.server.HttpServerOperations.withConnection(HttpServerOperations.java:250)
            at 
reactor.netty.http.server.HttpServerOperations.withConnection(HttpServerOperations.java:124)
            at 
reactor.netty.http.websocket.WebsocketInbound.aggregateFrames(WebsocketInbound.java:86)
            at 
reactor.netty.http.websocket.WebsocketInbound.aggregateFrames(WebsocketInbound.java:74)
            at 
org.apache.james.jmap.routes.WebSocketRoutes.handleWebSocketConnection(WebSocketRoutes.scala:119)
---
 .../apache/james/jmap/routes/WebSocketRoutes.scala | 22 +++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/WebSocketRoutes.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/WebSocketRoutes.scala
index fa8f92f09f..68e4998c3f 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/WebSocketRoutes.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/WebSocketRoutes.scala
@@ -20,7 +20,7 @@
 package org.apache.james.jmap.routes
 
 import java.nio.charset.StandardCharsets
-import java.util.concurrent.atomic.AtomicReference
+import java.util.concurrent.atomic.{AtomicBoolean, AtomicReference}
 import java.util.function.Predicate
 import java.util.{Optional, stream}
 
@@ -107,7 +107,7 @@ class WebSocketRoutes @Inject() 
(@Named(InjectionKeys.RFC_8621) val authenticato
         .`then`
         
.`then`(SMono(httpServerResponse.addHeader(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL,
 "jmap")
           .sendWebsocket((in: WebsocketInbound, out: WebsocketOutbound) => 
handleWebSocketConnection(mailboxSession)(in, out), websocketServerSpec))))
-      .onErrorResume(throwable => handleHttpHandshakeError(throwable, 
httpServerResponse))
+      .onErrorResume(throwable => handleHttpHandshakeError(throwable, 
httpServerRequest, httpServerResponse))
       .asJava()
       .`then`()
 
@@ -196,9 +196,21 @@ class WebSocketRoutes @Inject() 
(@Named(InjectionKeys.RFC_8621) val authenticato
       })
   }
 
-  private def handleHttpHandshakeError(throwable: Throwable, response: 
HttpServerResponse): SMono[Void] = throwable match {
-    case e: UnauthorizedException => respondDetails(e.addHeaders(response), 
ProblemDetails.forThrowable(throwable))
-    case _ => respondDetails(response, ProblemDetails.forThrowable(throwable))
+  private def handleHttpHandshakeError(throwable: Throwable, request: 
HttpServerRequest, response: HttpServerResponse): SMono[Void] =
+    if (isClientDisconnected(request)) {
+      WebSocketRoutes.LOGGER.info("Client disconnected during WebSocket 
handshake: {}", throwable.getMessage)
+      SMono.empty
+    } else {
+      throwable match {
+        case e: UnauthorizedException => 
respondDetails(e.addHeaders(response), ProblemDetails.forThrowable(throwable))
+        case _ => respondDetails(response, 
ProblemDetails.forThrowable(throwable))
+      }
+    }
+
+  private def isClientDisconnected(request: HttpServerRequest): Boolean = {
+    val disconnected = new AtomicBoolean(false)
+    request.withConnection(connection => 
disconnected.set(connection.isDisposed || !connection.channel().isActive))
+    disconnected.get()
   }
 
   private def asError(requestId: Option[RequestId])(throwable: Throwable): 
WebSocketError =


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to