diff --git a/src/jdk.httpserver/share/classes/module-info.java b/src/jdk.httpserver/share/classes/module-info.java index 15e9e2cc36d1e..ac147582b1421 100644 --- a/src/jdk.httpserver/share/classes/module-info.java +++ b/src/jdk.httpserver/share/classes/module-info.java @@ -23,6 +23,8 @@ * questions. */ +import com.sun.net.httpserver.*; + /** * Defines the JDK-specific HTTP server API, and provides the jwebserver tool * for running a minimal HTTP server. @@ -109,6 +111,14 @@ * and implementation of the server does not intend to be a full-featured, high performance * HTTP server. * + * @implNote + * Prior to JDK 26, in the JDK default implementation, the {@link HttpExchange} attribute map was + * shared with the enclosing {@link HttpContext}. + * Since JDK 26, by default, exchange attributes are per-exchange and the context attributes must + * be accessed by calling {@link HttpExchange#getHttpContext() getHttpContext()}{@link + * HttpContext#getAttributes() .getAttributes()}.
+ * A new system property, {@systemProperty jdk.httpserver.attributes} (default value: {@code ""}) + * allows to revert this new behavior. Set this property to "context" to restore the pre JDK 26 behavior. * @toolGuide jwebserver * * @uses com.sun.net.httpserver.spi.HttpServerProvider diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java index 1119d1e386b3e..0899952b4950f 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import java.net.*; import javax.net.ssl.*; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.lang.System.Logger; import java.lang.System.Logger.Level; import java.text.*; @@ -59,6 +60,9 @@ class ExchangeImpl { /* for formatting the Date: header */ private static final DateTimeFormatter FORMATTER; + private static final boolean perExchangeAttributes = + !System.getProperty("jdk.httpserver.attributes", "") + .equals("context"); static { String pattern = "EEE, dd MMM yyyy HH:mm:ss zzz"; FORMATTER = DateTimeFormatter.ofPattern(pattern, Locale.US) @@ -76,7 +80,7 @@ class ExchangeImpl { PlaceholderOutputStream uos_orig; boolean sentHeaders; /* true after response headers sent */ - Map attributes; + final Map attributes; int rcode = -1; HttpPrincipal principal; ServerImpl server; @@ -91,6 +95,9 @@ class ExchangeImpl { this.uri = u; this.connection = connection; this.reqContentLen = len; + this.attributes = perExchangeAttributes + ? new ConcurrentHashMap<>() + : getHttpContext().getAttributes(); /* ros only used for headers, body written directly to stream */ this.ros = req.outputStream(); this.ris = req.inputStream(); @@ -361,26 +368,15 @@ public SSLSession getSSLSession () { } public Object getAttribute (String name) { - if (name == null) { - throw new NullPointerException ("null name parameter"); - } - if (attributes == null) { - attributes = getHttpContext().getAttributes(); - } - return attributes.get (name); + return attributes.get(Objects.requireNonNull(name, "null name parameter")); } public void setAttribute (String name, Object value) { - if (name == null) { - throw new NullPointerException ("null name parameter"); - } - if (attributes == null) { - attributes = getHttpContext().getAttributes(); - } + var key = Objects.requireNonNull(name, "null name parameter"); if (value != null) { - attributes.put (name, value); + attributes.put(key, value); } else { - attributes.remove (name); + attributes.remove(key); } } diff --git a/test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java b/test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java index 2ce3dfd016d00..e7bea2814db03 100644 --- a/test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java +++ b/test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,9 @@ * @summary Tests for HttpExchange set/getAttribute * @library /test/lib * @run junit/othervm ExchangeAttributeTest + * @run junit/othervm -Djdk.httpserver.attributes=context ExchangeAttributeTest + * @run junit/othervm -Djdk.httpserver.attributes=random-string ExchangeAttributeTest + * @run junit/othervm -Djdk.httpserver.attributes ExchangeAttributeTest */ import com.sun.net.httpserver.HttpExchange; @@ -71,7 +74,7 @@ public static void setup() { public void testExchangeAttributes() throws Exception { var handler = new AttribHandler(); var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR,0), 10); - server.createContext("/", handler); + server.createContext("/", handler).getAttributes().put("attr", "context-val"); server.start(); try { var client = HttpClient.newBuilder().proxy(NO_PROXY).build(); @@ -101,8 +104,17 @@ static class AttribHandler implements HttpHandler { @java.lang.Override public void handle(HttpExchange exchange) throws IOException { try { - exchange.setAttribute("attr", "val"); - assertEquals("val", exchange.getAttribute("attr")); + if ("context".equals(System.getProperty("jdk.httpserver.attributes"))) { + exchange.setAttribute("attr", "val"); + assertEquals("val", exchange.getAttribute("attr")); + assertEquals("val", exchange.getHttpContext().getAttributes().get("attr")); + } else { + assertNull(exchange.getAttribute("attr")); + assertEquals("context-val", exchange.getHttpContext().getAttributes().get("attr")); + exchange.setAttribute("attr", "val"); + assertEquals("val", exchange.getAttribute("attr")); + assertEquals("context-val", exchange.getHttpContext().getAttributes().get("attr")); + } exchange.setAttribute("attr", null); assertNull(exchange.getAttribute("attr")); exchange.sendResponseHeaders(200, -1);