Sorry, that was incorrect - the api call is on the exchange, but it get/sets 
the attributes on the related context.

This seems wrong to me as well, and I’ll probably change it in the robaho 
version, but this would be a breaking change with the JDK implementation.

I agree with Ethan, that I think this should be fixed in the JDK, or at least 
be marked that the JDK impl is non-compliant.

> On Dec 6, 2024, at 9:15 AM, robert engels <reng...@ix.netcom.com> wrote:
> 
> I am confused though - I reviewed the robaho source code which came from the 
> JDK, and the setAttribute is on the exchange, which is per request, not per 
> context.
> 
>> On Dec 6, 2024, at 8:41 AM, robert engels <reng...@ix.netcom.com> wrote:
>> 
>> In minor defense of the JDK (I am worried more about ambiguity) - I’ve seen 
>> the JDK users implement this by add to the request headers - but with the 
>> JDK making this read-only, this is no longer possible, so the get/set 
>> attribute is the only viable way to pass per request information between 
>> layers.
>> 
>>> On Dec 5, 2024, at 7:27 PM, Ethan McCue <et...@mccue.dev> wrote:
>>> 
>>> After mulling it over some more, I think that as is there is actually no 
>>> valid use for .setAttribute as implemented by the JDK
>>> 
>>> Even the most trivial usages of it are broken under moderate load. This 
>>> includes the usage in SimpleFileServer.createOutputFilter and 
>>> SimpleFileServer.createFileHandler
>>> 
>>> import com.sun.net.httpserver.HttpServer;
>>> import com.sun.net.httpserver.SimpleFileServer;
>>> 
>>> import java.io.ByteArrayOutputStream;
>>> import java.net.InetSocketAddress;
>>> import java.net.URI;
>>> import java.net.http.HttpClient;
>>> import java.net.http.HttpRequest;
>>> import java.net.http.HttpResponse;
>>> import java.nio.charset.StandardCharsets;
>>> import java.nio.file.Files;
>>> import java.nio.file.Path;
>>> import java.util.concurrent.Executors;
>>> import java.util.concurrent.atomic.AtomicInteger;
>>> import java.util.regex.Pattern;
>>> 
>>> public class Main {
>>>     public static void main(String[] args) throws Exception {
>>>         Files.writeString(Path.of("./a"), "a".repeat(100000));
>>>         Files.writeString(Path.of("./b"), "b".repeat(100000));
>>> 
>>>         var serverExecutor = Executors.newFixedThreadPool(2);
>>>         var baos = new ByteArrayOutputStream();
>>>         var server = HttpServer.create(new InetSocketAddress(8841), 0);
>>>         server.createContext("/", 
>>> SimpleFileServer.createFileHandler(Path.of(".").toAbsolutePath()))
>>>                 .getFilters().add(SimpleFileServer.createOutputFilter(baos, 
>>> SimpleFileServer.OutputLevel.VERBOSE));
>>>         server.setExecutor(serverExecutor);
>>>         server.start();
>>> 
>>>         Thread.sleep(1000);
>>> 
>>>         var executor = Executors.newFixedThreadPool(2);
>>> 
>>>         int total = 10000;
>>> 
>>>         AtomicInteger failures = new AtomicInteger();
>>>         for (int i = 0; i < total; i++) {
>>>             String file = i % 2 == 0 ? "a" : "b";
>>>             executor.submit(() -> {
>>>                 try {
>>>                     var client = HttpClient.newHttpClient();
>>>                     client.send(
>>>                             
>>> HttpRequest.newBuilder(URI.create("http://0.0.0.0:8841/"; + file)).build(),
>>>                             HttpResponse.BodyHandlers.ofString()
>>>                     );
>>>                 } catch (Exception e) {
>>>                     e.printStackTrace(System.out);
>>>                     failures.getAndIncrement();
>>>                 }
>>>                 return null;
>>>             });
>>>         }
>>> 
>>>         executor.close();
>>> 
>>>         int a = 0;
>>>         int b = 0;
>>>         var s = baos.toString(StandardCharsets.UTF_8).split("\n");
>>>         for (var line : s) {
>>>             Pattern aPattern = Pattern.compile("Resource requested: 
>>> (.+)/a");
>>>             Pattern bPattern = Pattern.compile("Resource requested: 
>>> (.+)/b");
>>>             if (aPattern.matcher(line).find()) {
>>>                 a++;
>>>             }
>>>             else if (bPattern.matcher(line).find()) {
>>>                 b++;
>>>             }
>>>         }
>>>         System.out.println("Reported a request to /a: " + a);
>>>         System.out.println("Reported a request to /b: " + b);
>>>         System.out.println("Failures: " + failures);
>>> 
>>>         server.stop(0);
>>>         serverExecutor.close();
>>>     }
>>> }
>>> 
>>> Despite an equal number of requests being made to /a and /b the output 
>>> filter will report a randomly diverging amount. This is because there is 
>>> simply no way to avoid concurrent requests clobbering each-others state 
>>> while calling setAttribute on an exchange does not actually set an 
>>> attribute on that exchange.
>>> 
>>> On Thu, Dec 5, 2024 at 11:15 PM Ethan McCue <et...@mccue.dev 
>>> <mailto:et...@mccue.dev>> wrote:
>>>> Sorry, meant to send this to the list:
>>>> 
>>>> I will add as maybe obvious context that the way the JDK currently 
>>>> implements this is (I think, correct me if I am wrong) a security 
>>>> nightmare. That it might not be obvious (or uniform across an ecosystem of 
>>>> implementations) that 
>>>> exchange.setAttribute("CURRENTLY_AUTHENTICATED_USER", "..."); will not 
>>>> actually be setting an attribute on the exchange, but instead a global 
>>>> thing, is an issue
>>>> 
>>>> If this is a deliberate choice in the existing implementation, I am 
>>>> curious to know how it came about.
>>>> 
>>>> On Thu, Dec 5, 2024 at 11:13 PM Robert Engels <reng...@ix.netcom.com 
>>>> <mailto:reng...@ix.netcom.com>> wrote:
>>>>> Hi,
>>>>> 
>>>>> I read the bug report. I don’t think this is sufficient. This is a 
>>>>> specification so in order to have compliant behavior no matter the 
>>>>> implementation there cannot be a different set of rules for the reference 
>>>>> implementation vs others. 
>>>>> 
>>>>> So the api should be clarified in a non ambiguous manner and then one or 
>>>>> more implementations can be classified as non compliant. 
>>>>> 
>>>>> Robert
>>>>> 
>>>>>> On Dec 5, 2024, at 6:31 AM, Jaikiran Pai <jai.forums2...@gmail.com 
>>>>>> <mailto:jai.forums2...@gmail.com>> wrote:
>>>>>> 
>>>>>> 
>>>>>> Hello Ethan,
>>>>>> 
>>>>>> Thank you for noticing this and bringing this up here. I've raised 
>>>>>> https://bugs.openjdk.org/browse/JDK-8345577 and we will address this 
>>>>>> shortly.
>>>>>> 
>>>>>> -Jaikiran
>>>>>> 
>>>>>> On 05/12/24 3:22 am, Ethan McCue wrote:
>>>>>>> Sorry
>>>>>>> 
>>>>>>> Before:
>>>>>>> 
>>>>>>>      * {@link Filter} modules may store arbitrary objects with {@code 
>>>>>>> HttpExchange}
>>>>>>>      * instances as an out-of-band communication mechanism. Other 
>>>>>>> filters
>>>>>>>      * or the exchange handler may then access these objects.
>>>>>>> 
>>>>>>> Bungled the copy-paste
>>>>>>> 
>>>>>>> On Thu, Dec 5, 2024 at 6:49 AM Ethan McCue <et...@mccue.dev 
>>>>>>> <mailto:et...@mccue.dev>> wrote:
>>>>>>>> Hi all,
>>>>>>>> 
>>>>>>>> This change 
>>>>>>>> (https://github.com/openjdk/jdk/commit/40ae4699622cca72830acd146b7b5c4efd5a43ec)
>>>>>>>>  makes the Jetty implementation of the SPI be no longer fit the 
>>>>>>>> Javadoc.
>>>>>>>> 
>>>>>>>> HttpContexts are not per-request while the previous Javadoc implied 
>>>>>>>> that the attribute mechanism on exchanges was.
>>>>>>>> 
>>>>>>>> Before:
>>>>>>>> 
>>>>>>>>      * Sets an attribute with the given {@code name} and {@code value} 
>>>>>>>> in this exchange's
>>>>>>>>      * {@linkplain HttpContext#getAttributes() context attributes}.
>>>>>>>>      * or the exchange handler may then access these objects.
>>>>>>>> 
>>>>>>>> After:
>>>>>>>> 
>>>>>>>>      * Sets an attribute with the given {@code name} and {@code value} 
>>>>>>>> in this exchange's
>>>>>>>>      * {@linkplain HttpContext#getAttributes() context attributes}.
>>>>>>>>      *
>>>>>>>>      * @apiNote {@link Filter} modules may store arbitrary objects as 
>>>>>>>> attributes through
>>>>>>>>      * {@code HttpExchange} instances as an out-of-band communication 
>>>>>>>> mechanism. Other filters
>>>>>>>>      * or the exchange handler may then access these objects.
>>>>>>>> 
>>>>>>>> The Jetty implementation, I think rightfully, assumed that this 
>>>>>>>> context was per-request and implemented it as so.
>>>>>>>> 
>>>>>>>> https://github.com/jetty/jetty.project/blob/jetty-12.0.x/jetty-core/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchangeDelegate.java#L223
>>>>>>>> 
>>>>>>>> As such, I don't think simply a javadoc change as a resolution to 
>>>>>>>> these issues is applicable
>>>>>>>> 
>>>>>>>> https://bugs.openjdk.org/browse/JDK-8345233
>>>>>>>> https://bugs.openjdk.org/browse/JDK-8235786
>>>>>>>> 
>> 
> 

Reply via email to