Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/jdk.httpserver/share/classes/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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.*;
Expand Down Expand Up @@ -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)
Expand All @@ -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;
Expand All @@ -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();
Expand Down Expand Up @@ -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);
}
}

Expand Down
19 changes: 15 additions & 4 deletions test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -101,8 +104,16 @@ 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"));
}
exchange.setAttribute("attr", null);
assertNull(exchange.getAttribute("attr"));
exchange.sendResponseHeaders(200, -1);
Expand Down