-
Bug
-
Resolution: Fixed
-
P4
-
None
Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-8368949 | 17.0.18-oracle | Sean Coffey | P4 | Resolved | Fixed | master |
The code of the HTTPServer was changed in Java 17, among the change a ConcurrentHashMap is now used to store the attributes of the HttpExchange.
The problem is that ConcurrentHashMap.put() does not allow the value to be null while the spec does not say that the value of an attribute can not be null.
To reproduce, this code works with Java 8 up to Java 16 but fails with Java 17+.
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
public class HttpServerRegression {
public static void main(String[] args) throws IOException {
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
server.createContext("/", exchange -> {
System.err.println("request " + exchange.getRequestMethod() + " " + exchange.getRequestURI());
try {
exchange.setAttribute("status", null); // throw a NPE if Java version >= 17
byte[] bytes = "<h2>Hello</h2>".getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, bytes.length);
try(OutputStream body = exchange.getResponseBody()) {
body.write(bytes);
}
} catch(Exception e) {
e.printStackTrace();
throw e;
}
});
server.setExecutor(null);
server.start();
}
}
Here is the error message:
java.lang.NullPointerException
at java.base/java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011)
at java.base/java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006)
at jdk.httpserver/sun.net.httpserver.ExchangeImpl.setAttribute(ExchangeImpl.java:381)
at jdk.httpserver/sun.net.httpserver.HttpExchangeImpl.setAttribute(HttpExchangeImpl.java:105)
at HttpServerRegression.lambda$main$0(HttpServerRegression.java:14)
at jdk.httpserver/com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:95)
at jdk.httpserver/sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:82)
at jdk.httpserver/com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:98)
at jdk.httpserver/sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:728)
at jdk.httpserver/com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:95)
at jdk.httpserver/sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:695)
at jdk.httpserver/sun.net.httpserver.ServerImpl$DefaultExecutor.execute(ServerImpl.java:159)
at jdk.httpserver/sun.net.httpserver.ServerImpl$Dispatcher.handle(ServerImpl.java:447)
at jdk.httpserver/sun.net.httpserver.ServerImpl$Dispatcher.run(ServerImpl.java:413)
at java.base/java.lang.Thread.run(Thread.java:833)
The problem is that ConcurrentHashMap.put() does not allow the value to be null while the spec does not say that the value of an attribute can not be null.
To reproduce, this code works with Java 8 up to Java 16 but fails with Java 17+.
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
public class HttpServerRegression {
public static void main(String[] args) throws IOException {
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
server.createContext("/", exchange -> {
System.err.println("request " + exchange.getRequestMethod() + " " + exchange.getRequestURI());
try {
exchange.setAttribute("status", null); // throw a NPE if Java version >= 17
byte[] bytes = "<h2>Hello</h2>".getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, bytes.length);
try(OutputStream body = exchange.getResponseBody()) {
body.write(bytes);
}
} catch(Exception e) {
e.printStackTrace();
throw e;
}
});
server.setExecutor(null);
server.start();
}
}
Here is the error message:
java.lang.NullPointerException
at java.base/java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011)
at java.base/java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006)
at jdk.httpserver/sun.net.httpserver.ExchangeImpl.setAttribute(ExchangeImpl.java:381)
at jdk.httpserver/sun.net.httpserver.HttpExchangeImpl.setAttribute(HttpExchangeImpl.java:105)
at HttpServerRegression.lambda$main$0(HttpServerRegression.java:14)
at jdk.httpserver/com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:95)
at jdk.httpserver/sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:82)
at jdk.httpserver/com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:98)
at jdk.httpserver/sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:728)
at jdk.httpserver/com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:95)
at jdk.httpserver/sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:695)
at jdk.httpserver/sun.net.httpserver.ServerImpl$DefaultExecutor.execute(ServerImpl.java:159)
at jdk.httpserver/sun.net.httpserver.ServerImpl$Dispatcher.handle(ServerImpl.java:447)
at jdk.httpserver/sun.net.httpserver.ServerImpl$Dispatcher.run(ServerImpl.java:413)
at java.base/java.lang.Thread.run(Thread.java:833)
- backported by
-
JDK-8368949 HttpExchangeImpl.setAttribute does not allow null value after JDK-8266897
-
- Resolved
-
- relates to
-
JDK-8266897 com/sun/net/httpserver/FilterTest.java fails intermittently with AssertionError
-
- Resolved
-
- links to
-
Commit openjdk/jdk/f9827ad1
-
Review openjdk/jdk/13264
-
Review(master) openjdk/jdk17u-dev/4012