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 in GzipHandler from race condition #12159

Open
mperktold opened this issue Aug 13, 2024 · 2 comments
Open

NPE in GzipHandler from race condition #12159

mperktold opened this issue Aug 13, 2024 · 2 comments
Labels
Bug For general bugs on Jetty side

Comments

@mperktold
Copy link

Jetty version(s)
12.0.12

Jetty Environment
core

Java version/vendor (use: java -version)
openjdk version "21.0.3" 2024-04-16 LTS
OpenJDK Runtime Environment Temurin-21.0.3+9 (build 21.0.3+9-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.3+9 (build 21.0.3+9-LTS, mixed mode, sharing)

OS type/version
Windows 11

Description
In production, we often get the following NPEs:

java.lang.NullPointerException: Cannot invoke "org.eclipse.jetty.util.compression.DeflaterPool.acquire()" because "this._deflaterPool" is null
 
Cannot invoke "org.eclipse.jetty.util.compression.DeflaterPool.acquire()" because "this._deflaterPool" is null
    java.lang.NullPointerException: Cannot invoke "org.eclipse.jetty.util.compression.DeflaterPool.acquire()" because "this._deflaterPool" is null
        at org.eclipse.jetty.server.handler.gzip.GzipHandler.getDeflaterEntry(GzipHandler.java:396)
        at org.eclipse.jetty.server.handler.gzip.GzipResponseAndCallback.commit(GzipResponseAndCallback.java:238)
        at org.eclipse.jetty.server.handler.gzip.GzipResponseAndCallback.write(GzipResponseAndCallback.java:132)
        at org.eclipse.jetty.io.content.ContentSinkOutputStream.write(ContentSinkOutputStream.java:54)
        at java.base/sun.nio.cs.StreamEncoder.writeBytes(Unknown Source)
        at java.base/sun.nio.cs.StreamEncoder.implFlushBuffer(Unknown Source)
        at java.base/sun.nio.cs.StreamEncoder.implFlush(Unknown Source)
        at java.base/sun.nio.cs.StreamEncoder.lockedFlush(Unknown Source)
        at java.base/sun.nio.cs.StreamEncoder.flush(Unknown Source)
        at java.base/java.io.OutputStreamWriter.flush(Unknown Source)
        at java.base/java.io.BufferedWriter.implFlush(Unknown Source)
        at java.base/java.io.BufferedWriter.flush(Unknown Source)
		...

Apparently, this comes from a race condition between writing the response and stopping the server.

How to reproduce?
You can use the following snippet together with breakpoints in your IDE to reproduce the issue:

Server server = new Server(8080);
server.setHandler(new GzipHandler(new Handler.Abstract() {
    @Override
    public boolean handle(Request request, Response response, Callback callback) throws Exception {
        try (var out = Content.Sink.asOutputStream(response)) {
            out.write("Hello, Jetty".getBytes(StandardCharsets.UTF_8));
        }
        return true;
    }
}));
server.start();
server.stop();

One breakpoint can be set on the line server.stop() to control when the server will be stopped.

The other one can be set here were the deflater pool is used for writing back the response, and where the NPE is thrown:

Now you can use these breakpoints to ensure the following steps are executed in the "right" in order:

  1. Make a request. Let it wait in GzipResponseAndCallback.commit.
  2. Stop the server.
  3. Let GzipResponseAndCallback.commit proceed.
@mperktold mperktold added the Bug For general bugs on Jetty side label Aug 13, 2024
@joakime
Copy link
Contributor

joakime commented Aug 14, 2024

I think we should at least make this more friendly for graceful shutdown for sure. (which is not what the OP shows in his example)

The standard stop behaviors are rather racy towards active requests / connections anyway.

@mperktold
Copy link
Author

What would graceful shutdown look like?

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
Status: No status
Development

No branches or pull requests

2 participants