Skip to content
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

NPE Cannot invoke "org.eclipse.jetty.server.Request.getSession(boolean)" because the return value of "org.eclipse.jetty.ee10.servlet.ServletApiRequest.getRequest()" is null #11809

Closed
donniexyz opened this issue May 19, 2024 · 6 comments
Labels
Bug For general bugs on Jetty side

Comments

@donniexyz
Copy link

donniexyz commented May 19, 2024

Jetty version(s)
12.0.9

Jetty Environment
ee10

Java version/vendor (use: java -version)
java 21.0.1 2023-10-17 LTS Java(TM) SE Runtime Environment (build 21.0.1+12-LTS-29) Java HotSpot(TM) 64-Bit Server VM (build 21.0.1+12-LTS-29, mixed mode, sharing)

OS type/version
Microsoft Windows [Version 10.0.22631.3593]

Description
Jetty-12 embedded with websocket, every 30 seconds logged this error:

12:53:27.586  WARN      mj-virt      o.moqui.i.w.MoquiAbstractEndpoint Error in WebSocket Session 21abd383-85cd-48c0-ab3e-878ecbb56667, User null (null)
org.eclipse.jetty.websocket.core.exception.CloseException: org.eclipse.jetty.websocket.core.exception.WebSocketException: NotificationEndpoint OPEN method error: Cannot invoke "org.eclipse.jetty.server.Request.getSession(boolean)" because the return value of "org.eclipse.jetty.ee10.servlet.ServletApiRequest.getRequest()" is null
	at org.eclipse.jetty.websocket.core.WebSocketCoreSession.lambda$onOpen$5(WebSocketCoreSession.java:394) ~[jetty-websocket-core-common-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.util.Callback$3.failed(Callback.java:178) ~[jetty-util-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.ee10.websocket.jakarta.common.JakartaWebSocketFrameHandler.onOpen(JakartaWebSocketFrameHandler.java:186) ~[jetty-ee10-websocket-jakarta-common-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.websocket.core.WebSocketCoreSession.lambda$onOpen$6(WebSocketCoreSession.java:400) ~[jetty-websocket-core-common-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.server.handler.ContextHandler$ScopedContext.run(ContextHandler.java:1298) ~[jetty-server-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.server.handler.ContextHandler$ScopedContext.run(ContextHandler.java:1285) ~[jetty-server-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.websocket.core.server.internal.AbstractHandshaker$1.handle(AbstractHandshaker.java:179) ~[jetty-websocket-core-server-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.websocket.core.WebSocketCoreSession.onOpen(WebSocketCoreSession.java:400) ~[jetty-websocket-core-common-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.websocket.core.WebSocketConnection.onOpen(WebSocketConnection.java:531) ~[jetty-websocket-core-common-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.io.AbstractEndPoint.upgrade(AbstractEndPoint.java:435) ~[jetty-io-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.server.internal.HttpConnection$HttpStreamOverHTTP1.succeeded(HttpConnection.java:1527) ~[jetty-server-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.server.HttpStream$Wrapper.succeeded(HttpStream.java:221) ~[jetty-server-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.server.internal.CompletionStreamWrapper.succeeded(CompletionStreamWrapper.java:45) ~[jetty-server-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.server.HttpStream$Wrapper.succeeded(HttpStream.java:221) ~[jetty-server-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.session.AbstractSessionManager$SessionStreamWrapper.succeeded(AbstractSessionManager.java:1468) ~[jetty-session-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.server.internal.HttpChannelState$HandlerInvoker.completeStream(HttpChannelState.java:744) ~[jetty-server-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.server.internal.HttpChannelState$HandlerInvoker.run(HttpChannelState.java:670) ~[jetty-server-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.server.internal.HttpConnection.onFillable(HttpConnection.java:411) ~[jetty-server-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:322) ~[jetty-io-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:99) ~[jetty-io-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53) ~[jetty-io-12.0.9.jar:12.0.9]
	at java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314) ~[?:?]
	at java.base/java.lang.VirtualThread.run(VirtualThread.java:309) ~[?:?]
Caused by: org.eclipse.jetty.websocket.core.exception.WebSocketException: NotificationEndpoint OPEN method error: Cannot invoke "org.eclipse.jetty.server.Request.getSession(boolean)" because the return value of "org.eclipse.jetty.ee10.servlet.ServletApiRequest.getRequest()" is null
	at org.eclipse.jetty.ee10.websocket.jakarta.common.JakartaWebSocketFrameHandler.onOpen(JakartaWebSocketFrameHandler.java:185) ~[jetty-ee10-websocket-jakarta-common-12.0.9.jar:12.0.9]
	... 20 more
Caused by: java.lang.NullPointerException: Cannot invoke "org.eclipse.jetty.server.Request.getSession(boolean)" because the return value of "org.eclipse.jetty.ee10.servlet.ServletApiRequest.getRequest()" is null
	at org.eclipse.jetty.ee10.servlet.ServletApiRequest.getSession(ServletApiRequest.java:519) ~[jetty-ee10-servlet-12.0.9.jar:12.0.9]
	at org.eclipse.jetty.ee10.websocket.jakarta.server.internal.JsrHandshakeRequest.getHttpSession(JsrHandshakeRequest.java:58) ~[jetty-ee10-websocket-jakarta-server-12.0.9.jar:12.0.9]
	at org.moqui.impl.webapp.MoquiAbstractEndpoint.onOpen(MoquiAbstractEndpoint.java:65) ~[classes/:?]
	at org.moqui.impl.webapp.NotificationEndpoint.onOpen(NotificationEndpoint.java:33) ~[classes/:?]
	at org.eclipse.jetty.ee10.websocket.jakarta.common.JakartaWebSocketFrameHandler.onOpen(JakartaWebSocketFrameHandler.java:175) ~[jetty-ee10-websocket-jakarta-common-12.0.9.jar:12.0.9]
	... 20 more

Codes that triggers the error:

class XXXAbstractEndpoint extends Endpoint {
    @Override
    public void onOpen(final Session session, EndpointConfig config) {
        this.session = session;
        handshakeRequest = (HandshakeRequest) config.getUserProperties().get("handshakeRequest");
        httpSession = handshakeRequest != null ? (HttpSession) handshakeRequest.getHttpSession() : (HttpSession) config.getUserProperties().get("httpSession");

How to reproduce?
Most probably this is the sequence of the events leads to this situation:

  1. get an active http session
  2. open websocket
  3. invalidate http session
@donniexyz donniexyz added the Bug For general bugs on Jetty side label May 19, 2024
@sbordet
Copy link
Contributor

sbordet commented May 19, 2024

@donniexyz I would not try to access HTTP objects from within a WebSocket endpoint.

There are other APIs that allow you to access the upgrade request and response before Endpoint.onOpen().
If you use those, do you still have no HTTP session?

I'm referring to ServerEndpointConfig.Configurator.modifyHandshake().

@janbartel
Copy link
Contributor

Also @donniexyz you may be interested in the upcoming servlet 6.1 feature that allows for access to a HttpSession outside of a HttpServletRequest. See the api doco for Accessor here: https://github.com/jakartaee/servlet/blob/master/api/src/main/java/jakarta/servlet/http/HttpSession.java. This will be released in jetty-12.1.0 in the coming months.

@donniexyz
Copy link
Author

@donniexyz I would not try to access HTTP objects from within a WebSocket endpoint.

There are other APIs that allow you to access the upgrade request and response before Endpoint.onOpen(). If you use those, do you still have no HTTP session?

I'm referring to ServerEndpointConfig.Configurator.modifyHandshake().

It already use the ServerEndpointConfig.Configurator.modifyHandshake(). The "handshakeRequest" property was set by it (forgot did not mention this yesterday).

  @Override
  public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
      config.getUserProperties().put("handshakeRequest", request);
      config.getUserProperties().put("httpSession", request.getHttpSession());
      if (maxIdleTimeout != null) config.getUserProperties().put("maxIdleTimeout", maxIdleTimeout);
  }

@donniexyz
Copy link
Author

donniexyz commented May 20, 2024

Also @donniexyz you may be interested in the upcoming servlet 6.1 feature that allows for access to a HttpSession outside of a HttpServletRequest. See the api doco for Accessor here: https://github.com/jakartaee/servlet/blob/master/api/src/main/java/jakarta/servlet/http/HttpSession.java. This will be released in jetty-12.1.0 in the coming months.

Currently it does feels like chicken and egg problem, which one supposed to come first, websocket or request or session. And thanks for pointing that out, Accessor seems the right answer. However the code above works on Jetty 9, and initially I thought we can just return null on org.eclipse.jetty.ee10.servlet.ServletApiRequest#getSession(boolean) when getRequest() is null.

@sbordet
Copy link
Contributor

sbordet commented May 20, 2024

@donniexyz your modifyHandshake() stores a reference to the HttpSession, but by the time you arrive to a WebSocket endpoint, you are not anymore in HTTP land, and that reference might have been recycled, or cleared, or otherwise made unavailable. Same for the request object.

You should either copy the values you need out of the HttpSession into user properties, or do all the work in modifyHandshake().

HTTP comes before WebSocket.
A correct HTTP upgrade request is replied with an HTTP response 101, and then the HTTP protocol is over and WebSocket begins.
modifyHandshake() is your last chance in HTTP land; after that, it's WebSocket and things may work, but you should not rely on being able to access HTTP objects from WebSocket.

If you need to access HTTP request headers, you can copy those you need into a Map, and same for HttpSession attributes, and share them via user properties.

@donniexyz
Copy link
Author

I see. Okay, now I copy the to be used variables into user properties on modifyHandshake().

Thank you @sbordet @janbartel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug For general bugs on Jetty side
Projects
None yet
Development

No branches or pull requests

3 participants