7105350: HttpExchange's attributes are the same as HttpContext's attributes

Reviewed-by: michaelm, jpai, dfuchs
This commit is contained in:
Josiah Noel 2025-10-24 23:03:50 +00:00 committed by Michael McMahon
parent a4eaeb47c9
commit 35fdda0889
3 changed files with 39 additions and 21 deletions

View File

@ -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()}. <br>
* A new system property, <b>{@systemProperty jdk.httpserver.attributes}</b> (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

View File

@ -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.io.*;
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<String,Object> attributes;
final Map<String,Object> 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 @@ class ExchangeImpl {
}
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);
}
}

View File

@ -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 class ExchangeAttributeTest {
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 @@ public class ExchangeAttributeTest {
@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);