diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java index 66db59325e36..be10d2d8532b 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java @@ -25,6 +25,7 @@ import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; +import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; @@ -927,6 +928,13 @@ default int size() return size; } + static Map> asMap(HttpFields fields) + { + Map> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + fields.getFieldNamesCollection().forEach(name -> headers.putIfAbsent(name, fields.getValuesList(name))); + return headers; + } + /** * @return a sequential stream of the {@link HttpField}s in this instance */ diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-client/src/main/java/org/eclipse/jetty/websocket/client/internal/DelegatedJettyClientUpgradeRequest.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-client/src/main/java/org/eclipse/jetty/websocket/client/internal/DelegatedJettyClientUpgradeRequest.java index a76104afe63c..d45fbc626e78 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-client/src/main/java/org/eclipse/jetty/websocket/client/internal/DelegatedJettyClientUpgradeRequest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-client/src/main/java/org/eclipse/jetty/websocket/client/internal/DelegatedJettyClientUpgradeRequest.java @@ -22,6 +22,7 @@ import java.util.stream.Collectors; import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.io.EndPoint; @@ -78,7 +79,7 @@ public List getHeaders(String name) @Override public Map> getHeaders() { - return null; + return Collections.unmodifiableMap(HttpFields.asMap(delegate.getHeaders())); } @Override diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-client/src/main/java/org/eclipse/jetty/websocket/client/internal/DelegatedJettyClientUpgradeResponse.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-client/src/main/java/org/eclipse/jetty/websocket/client/internal/DelegatedJettyClientUpgradeResponse.java index 1b93d80ba20f..dfae74b6304e 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-client/src/main/java/org/eclipse/jetty/websocket/client/internal/DelegatedJettyClientUpgradeResponse.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-client/src/main/java/org/eclipse/jetty/websocket/client/internal/DelegatedJettyClientUpgradeResponse.java @@ -13,7 +13,6 @@ package org.eclipse.jetty.websocket.client.internal; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -21,6 +20,7 @@ import java.util.stream.Collectors; import org.eclipse.jetty.client.Response; +import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.websocket.api.ExtensionConfig; import org.eclipse.jetty.websocket.api.UpgradeResponse; @@ -65,9 +65,7 @@ public List getHeaders(String name) @Override public Map> getHeaders() { - Map> headers = getHeaderNames().stream() - .collect(Collectors.toMap((name) -> name, (name) -> new ArrayList<>(getHeaders(name)))); - return Collections.unmodifiableMap(headers); + return Collections.unmodifiableMap(HttpFields.asMap(delegate.getHeaders())); } @Override diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/internal/UpgradeRequestDelegate.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/internal/UpgradeRequestDelegate.java index 2006890f8010..921aea05a477 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/internal/UpgradeRequestDelegate.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/internal/UpgradeRequestDelegate.java @@ -21,7 +21,6 @@ import java.util.Map; import java.util.stream.Collectors; -import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpScheme; @@ -73,14 +72,7 @@ public int getHeaderInt(String name) @Override public Map> getHeaders() { - Map> result = new LinkedHashMap<>(); - HttpFields headers = request.getHeaders(); - for (HttpField header : headers) - { - String name = header.getName(); - result.put(name, headers.getValuesList(name)); - } - return result; + return HttpFields.asMap(request.getHeaders()); } @Override diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/internal/UpgradeResponseDelegate.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/internal/UpgradeResponseDelegate.java index c798e1e20a8f..46b704851647 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/internal/UpgradeResponseDelegate.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/internal/UpgradeResponseDelegate.java @@ -13,13 +13,11 @@ package org.eclipse.jetty.websocket.server.internal; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.websocket.api.ExtensionConfig; import org.eclipse.jetty.websocket.api.UpgradeResponse; @@ -64,14 +62,7 @@ public Set getHeaderNames() @Override public Map> getHeaders() { - Map> result = new LinkedHashMap<>(); - HttpFields.Mutable headers = response.getHeaders(); - for (HttpField header : headers) - { - String name = header.getName(); - result.put(name, headers.getValuesList(name)); - } - return result; + return HttpFields.asMap(response.getHeaders()); } @Override diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/client/internal/JsrUpgradeListener.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/client/internal/JsrUpgradeListener.java index 8406e494e4f5..1343570d318a 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/client/internal/JsrUpgradeListener.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/client/internal/JsrUpgradeListener.java @@ -13,9 +13,6 @@ package org.eclipse.jetty.ee10.websocket.jakarta.client.internal; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -41,16 +38,9 @@ public void onHandshakeRequest(Request request) if (configurator == null) return; - HttpFields fields = request.getHeaders(); - Map> originalHeaders = new HashMap<>(); - fields.forEach(field -> - { - originalHeaders.putIfAbsent(field.getName(), new ArrayList<>()); - List values = originalHeaders.get(field.getName()); - Collections.addAll(values, field.getValues()); - }); - // Give headers to configurator + HttpFields fields = request.getHeaders(); + Map> originalHeaders = HttpFields.asMap(fields); configurator.beforeRequest(originalHeaders); // Reset headers on HttpRequest per configurator @@ -67,18 +57,7 @@ public void onHandshakeResponse(Request request, Response response) if (configurator == null) return; - HandshakeResponse handshakeResponse = () -> - { - Map> ret = new HashMap<>(); - response.getHeaders().forEach(field -> - { - ret.putIfAbsent(field.getName(), new ArrayList<>()); - List values = ret.get(field.getName()); - Collections.addAll(values, field.getValues()); - }); - return ret; - }; - + HandshakeResponse handshakeResponse = () -> HttpFields.asMap(response.getHeaders()); configurator.afterResponse(handshakeResponse); } } diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JsrHandshakeRequest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JsrHandshakeRequest.java index a9f8ac6e7147..01364fb9a0cf 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JsrHandshakeRequest.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JsrHandshakeRequest.java @@ -15,16 +15,15 @@ import java.net.URI; import java.security.Principal; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import jakarta.servlet.http.HttpServletRequest; import jakarta.websocket.server.HandshakeRequest; import org.eclipse.jetty.ee10.websocket.jakarta.server.JakartaWebSocketServerContainer; +import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.pathmap.PathSpec; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.util.Fields; @@ -47,9 +46,7 @@ public JsrHandshakeRequest(ServerUpgradeRequest req) @Override public Map> getHeaders() { - Map> headers = delegate.getHeaders().getFieldNamesCollection().stream() - .collect(Collectors.toMap((name) -> name, (name) -> new ArrayList<>(delegate.getHeaders().getValuesList(name)))); - return Collections.unmodifiableMap(headers); + return Collections.unmodifiableMap(HttpFields.asMap(delegate.getHeaders())); } @Override diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JsrHandshakeResponse.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JsrHandshakeResponse.java index b4d5986dd106..e8d0b111d714 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JsrHandshakeResponse.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JsrHandshakeResponse.java @@ -13,12 +13,11 @@ package org.eclipse.jetty.ee10.websocket.jakarta.server.internal; -import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import jakarta.websocket.HandshakeResponse; +import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.websocket.core.server.ServerUpgradeResponse; public class JsrHandshakeResponse implements HandshakeResponse @@ -29,8 +28,7 @@ public class JsrHandshakeResponse implements HandshakeResponse public JsrHandshakeResponse(ServerUpgradeResponse resp) { this.delegate = resp; - this.headers = delegate.getHeaders().getFieldNamesCollection().stream() - .collect(Collectors.toMap((name) -> name, (name) -> new ArrayList<>(delegate.getHeaders().getValuesList(name)))); + this.headers = HttpFields.asMap(delegate.getHeaders()); } @Override diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/UpgradeHeadersTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/UpgradeHeadersTest.java new file mode 100644 index 000000000000..d19b00d8ba66 --- /dev/null +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/UpgradeHeadersTest.java @@ -0,0 +1,138 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee10.websocket.jakarta.tests; + +import java.net.URI; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import jakarta.websocket.ClientEndpointConfig; +import jakarta.websocket.Endpoint; +import jakarta.websocket.EndpointConfig; +import jakarta.websocket.HandshakeResponse; +import jakarta.websocket.Session; +import jakarta.websocket.server.HandshakeRequest; +import jakarta.websocket.server.ServerEndpointConfig; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.websocket.jakarta.client.JakartaWebSocketClientContainer; +import org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class UpgradeHeadersTest +{ + private Server _server; + private JakartaWebSocketClientContainer _client; + private ServerConnector _connector; + + public static class MyEndpoint extends Endpoint + { + @Override + public void onOpen(Session session, EndpointConfig config) + { + } + } + + public void start(ServerEndpointConfig.Configurator configurator) throws Exception + { + _server = new Server(); + _connector = new ServerConnector(_server); + _server.addConnector(_connector); + + ServletContextHandler contextHandler = new ServletContextHandler(); + _server.setHandler(contextHandler); + JakartaWebSocketServletContainerInitializer.configure(contextHandler, (context, container) -> + { + container.addEndpoint(ServerEndpointConfig.Builder + .create(MyEndpoint.class, "/") + .configurator(configurator) + .build()); + }); + + _server.start(); + _client = new JakartaWebSocketClientContainer(); + _client.start(); + } + + @AfterEach + public void after() throws Exception + { + _client.stop(); + _server.stop(); + } + + @Test + public void testCaseInsensitiveUpgradeHeaders() throws Exception + { + ClientEndpointConfig.Configurator configurator = new ClientEndpointConfig.Configurator() + { + @Override + public void beforeRequest(Map> headers) + { + // Verify that existing headers can be accessed in a case-insensitive way. + if (headers.get("cOnnEcTiOn") == null) + throw new IllegalStateException("No Connection Header on client Request"); + headers.put("sentHeader", List.of("value123")); + } + + @Override + public void afterResponse(HandshakeResponse hr) + { + if (hr.getHeaders().get("MyHeAdEr") == null) + throw new IllegalStateException("No custom Header on HandshakeResponse"); + if (hr.getHeaders().get("cOnnEcTiOn") == null) + throw new IllegalStateException("No Connection Header on HandshakeRequest"); + } + }; + + start(new ServerEndpointConfig.Configurator() + { + @Override + public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) + { + // Verify that existing headers can be accessed in a case-insensitive way. + if (request.getHeaders().get("cOnnEcTiOn") == null) + throw new IllegalStateException("No Connection Header on HandshakeRequest"); + if (response.getHeaders().get("sErVeR") == null) + throw new IllegalStateException("No Server Header on HandshakeResponse"); + + // Verify custom header sent from client. + if (request.getHeaders().get("SeNtHeadEr") == null) + throw new IllegalStateException("No sent Header on HandshakeResponse"); + + // Add custom response header. + response.getHeaders().put("myHeader", List.of("foobar")); + if (response.getHeaders().get("MyHeAdEr") == null) + throw new IllegalStateException("No custom Header on HandshakeResponse"); + + super.modifyHandshake(sec, request, response); + } + }); + + WSEndpointTracker clientEndpoint = new WSEndpointTracker(){}; + ClientEndpointConfig clientConfig = ClientEndpointConfig.Builder.create().configurator(configurator).build(); + URI uri = URI.create("ws://localhost:" + _connector.getLocalPort()); + + // If any of the above throw it would fail to upgrade to websocket. + Session session = _client.connectToServer(clientEndpoint, clientConfig, uri); + assertTrue(clientEndpoint.openLatch.await(5, TimeUnit.SECONDS)); + session.close(); + assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS)); + } +} diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/internal/DelegatedServerUpgradeRequest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/internal/DelegatedServerUpgradeRequest.java index a5dc510102ad..9d7d594ccf51 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/internal/DelegatedServerUpgradeRequest.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/internal/DelegatedServerUpgradeRequest.java @@ -18,7 +18,6 @@ import java.net.URI; import java.security.Principal; import java.security.cert.X509Certificate; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; @@ -33,6 +32,7 @@ import jakarta.servlet.http.HttpSession; import org.eclipse.jetty.ee10.websocket.server.JettyServerUpgradeRequest; import org.eclipse.jetty.http.BadMessageException; +import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.websocket.api.ExtensionConfig; @@ -121,9 +121,7 @@ public int getHeaderInt(String name) @Override public Map> getHeaders() { - Map> headers = upgradeRequest.getHeaders().getFieldNamesCollection().stream() - .collect(Collectors.toMap((name) -> name, (name) -> new ArrayList<>(getHeaders(name)))); - return Collections.unmodifiableMap(headers); + return Collections.unmodifiableMap(HttpFields.asMap(upgradeRequest.getHeaders())); } @Override diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/internal/DelegatedServerUpgradeResponse.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/internal/DelegatedServerUpgradeResponse.java index a1688d5749e0..aa29ba6b5002 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/internal/DelegatedServerUpgradeResponse.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/internal/DelegatedServerUpgradeResponse.java @@ -14,7 +14,6 @@ package org.eclipse.jetty.ee10.websocket.server.internal; import java.io.IOException; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -24,6 +23,7 @@ import jakarta.servlet.http.HttpServletResponse; import org.eclipse.jetty.ee10.servlet.ServletContextResponse; import org.eclipse.jetty.ee10.websocket.server.JettyServerUpgradeResponse; +import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.websocket.api.ExtensionConfig; @@ -91,9 +91,7 @@ public Set getHeaderNames() @Override public Map> getHeaders() { - Map> headers = getHeaderNames().stream() - .collect(Collectors.toMap((name) -> name, (name) -> new ArrayList<>(getHeaders(name)))); - return Collections.unmodifiableMap(headers); + return Collections.unmodifiableMap(HttpFields.asMap(upgradeResponse.getHeaders())); } @Override diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-client/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/client/internal/JsrUpgradeListener.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-client/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/client/internal/JsrUpgradeListener.java index f2185174426b..1c4c11b5fc9b 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-client/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/client/internal/JsrUpgradeListener.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-client/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/client/internal/JsrUpgradeListener.java @@ -13,9 +13,6 @@ package org.eclipse.jetty.ee9.websocket.jakarta.client.internal; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -41,16 +38,9 @@ public void onHandshakeRequest(Request request) if (configurator == null) return; - HttpFields fields = request.getHeaders(); - Map> originalHeaders = new HashMap<>(); - fields.forEach(field -> - { - originalHeaders.putIfAbsent(field.getName(), new ArrayList<>()); - List values = originalHeaders.get(field.getName()); - Collections.addAll(values, field.getValues()); - }); - // Give headers to configurator + HttpFields fields = request.getHeaders(); + Map> originalHeaders = HttpFields.asMap(fields); configurator.beforeRequest(originalHeaders); // Reset headers on HttpRequest per configurator @@ -67,18 +57,7 @@ public void onHandshakeResponse(Request request, Response response) if (configurator == null) return; - HandshakeResponse handshakeResponse = () -> - { - Map> ret = new HashMap<>(); - response.getHeaders().forEach(field -> - { - ret.putIfAbsent(field.getName(), new ArrayList<>()); - List values = ret.get(field.getName()); - Collections.addAll(values, field.getValues()); - }); - return ret; - }; - + HandshakeResponse handshakeResponse = () -> HttpFields.asMap(response.getHeaders()); configurator.afterResponse(handshakeResponse); } } diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/server/internal/JsrHandshakeRequest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/server/internal/JsrHandshakeRequest.java index a0ff17b5c7f8..01a15f48edfc 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/server/internal/JsrHandshakeRequest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/server/internal/JsrHandshakeRequest.java @@ -15,16 +15,15 @@ import java.net.URI; import java.security.Principal; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import jakarta.servlet.http.HttpServletRequest; import jakarta.websocket.server.HandshakeRequest; import org.eclipse.jetty.ee9.websocket.jakarta.server.JakartaWebSocketServerContainer; +import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.pathmap.PathSpec; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.util.Fields; @@ -47,9 +46,7 @@ public JsrHandshakeRequest(ServerUpgradeRequest req) @Override public Map> getHeaders() { - Map> headers = delegate.getHeaders().getFieldNamesCollection().stream() - .collect(Collectors.toMap((name) -> name, (name) -> new ArrayList<>(delegate.getHeaders().getValuesList(name)))); - return Collections.unmodifiableMap(headers); + return Collections.unmodifiableMap(HttpFields.asMap(delegate.getHeaders())); } @Override diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/server/internal/JsrHandshakeResponse.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/server/internal/JsrHandshakeResponse.java index 7be47d85817a..928fc1e34913 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/server/internal/JsrHandshakeResponse.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/server/internal/JsrHandshakeResponse.java @@ -13,12 +13,11 @@ package org.eclipse.jetty.ee9.websocket.jakarta.server.internal; -import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import jakarta.websocket.HandshakeResponse; +import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.websocket.core.server.ServerUpgradeResponse; public class JsrHandshakeResponse implements HandshakeResponse @@ -29,8 +28,7 @@ public class JsrHandshakeResponse implements HandshakeResponse public JsrHandshakeResponse(ServerUpgradeResponse resp) { this.delegate = resp; - this.headers = delegate.getHeaders().getFieldNamesCollection().stream() - .collect(Collectors.toMap((name) -> name, (name) -> new ArrayList<>(delegate.getHeaders().getValuesList(name)))); + this.headers = HttpFields.asMap(delegate.getHeaders()); } @Override diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/UpgradeHeadersTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/UpgradeHeadersTest.java new file mode 100644 index 000000000000..4f9b80c65bdb --- /dev/null +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/UpgradeHeadersTest.java @@ -0,0 +1,138 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee9.websocket.jakarta.tests; + +import java.net.URI; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import jakarta.websocket.ClientEndpointConfig; +import jakarta.websocket.Endpoint; +import jakarta.websocket.EndpointConfig; +import jakarta.websocket.HandshakeResponse; +import jakarta.websocket.Session; +import jakarta.websocket.server.HandshakeRequest; +import jakarta.websocket.server.ServerEndpointConfig; +import org.eclipse.jetty.ee9.servlet.ServletContextHandler; +import org.eclipse.jetty.ee9.websocket.jakarta.client.JakartaWebSocketClientContainer; +import org.eclipse.jetty.ee9.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class UpgradeHeadersTest +{ + private Server _server; + private JakartaWebSocketClientContainer _client; + private ServerConnector _connector; + + public static class MyEndpoint extends Endpoint + { + @Override + public void onOpen(Session session, EndpointConfig config) + { + } + } + + public void start(ServerEndpointConfig.Configurator configurator) throws Exception + { + _server = new Server(); + _connector = new ServerConnector(_server); + _server.addConnector(_connector); + + ServletContextHandler contextHandler = new ServletContextHandler(); + _server.setHandler(contextHandler); + JakartaWebSocketServletContainerInitializer.configure(contextHandler, (context, container) -> + { + container.addEndpoint(ServerEndpointConfig.Builder + .create(MyEndpoint.class, "/") + .configurator(configurator) + .build()); + }); + + _server.start(); + _client = new JakartaWebSocketClientContainer(); + _client.start(); + } + + @AfterEach + public void after() throws Exception + { + _client.stop(); + _server.stop(); + } + + @Test + public void testCaseInsensitiveUpgradeHeaders() throws Exception + { + ClientEndpointConfig.Configurator configurator = new ClientEndpointConfig.Configurator() + { + @Override + public void beforeRequest(Map> headers) + { + // Verify that existing headers can be accessed in a case-insensitive way. + if (headers.get("cOnnEcTiOn") == null) + throw new IllegalStateException("No Connection Header on client Request"); + headers.put("sentHeader", List.of("value123")); + } + + @Override + public void afterResponse(HandshakeResponse hr) + { + if (hr.getHeaders().get("MyHeAdEr") == null) + throw new IllegalStateException("No custom Header on HandshakeResponse"); + if (hr.getHeaders().get("cOnnEcTiOn") == null) + throw new IllegalStateException("No Connection Header on HandshakeRequest"); + } + }; + + start(new ServerEndpointConfig.Configurator() + { + @Override + public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) + { + // Verify that existing headers can be accessed in a case-insensitive way. + if (request.getHeaders().get("cOnnEcTiOn") == null) + throw new IllegalStateException("No Connection Header on HandshakeRequest"); + if (response.getHeaders().get("sErVeR") == null) + throw new IllegalStateException("No Server Header on HandshakeResponse"); + + // Verify custom header sent from client. + if (request.getHeaders().get("SeNtHeadEr") == null) + throw new IllegalStateException("No sent Header on HandshakeResponse"); + + // Add custom response header. + response.getHeaders().put("myHeader", List.of("foobar")); + if (response.getHeaders().get("MyHeAdEr") == null) + throw new IllegalStateException("No custom Header on HandshakeResponse"); + + super.modifyHandshake(sec, request, response); + } + }); + + WSEndpointTracker clientEndpoint = new WSEndpointTracker(){}; + ClientEndpointConfig clientConfig = ClientEndpointConfig.Builder.create().configurator(configurator).build(); + URI uri = URI.create("ws://localhost:" + _connector.getLocalPort()); + + // If any of the above throw it would fail to upgrade to websocket. + Session session = _client.connectToServer(clientEndpoint, clientConfig, uri); + assertTrue(clientEndpoint.openLatch.await(5, TimeUnit.SECONDS)); + session.close(); + assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS)); + } +} diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee9/websocket/client/impl/DelegatedJettyClientUpgradeResponse.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee9/websocket/client/impl/DelegatedJettyClientUpgradeResponse.java index 0c6c57b77dc3..e49d6d0aa1f5 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee9/websocket/client/impl/DelegatedJettyClientUpgradeResponse.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee9/websocket/client/impl/DelegatedJettyClientUpgradeResponse.java @@ -13,7 +13,6 @@ package org.eclipse.jetty.ee9.websocket.client.impl; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -23,6 +22,7 @@ import org.eclipse.jetty.client.Response; import org.eclipse.jetty.ee9.websocket.api.ExtensionConfig; import org.eclipse.jetty.ee9.websocket.api.UpgradeResponse; +import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; /** @@ -65,9 +65,7 @@ public List getHeaders(String name) @Override public Map> getHeaders() { - Map> headers = getHeaderNames().stream() - .collect(Collectors.toMap((name) -> name, (name) -> new ArrayList<>(getHeaders(name)))); - return Collections.unmodifiableMap(headers); + return Collections.unmodifiableMap(HttpFields.asMap(delegate.getHeaders())); } @Override diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee9/websocket/server/internal/DelegatedServerUpgradeRequest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee9/websocket/server/internal/DelegatedServerUpgradeRequest.java index 6f99d2229cb1..738cc2084745 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee9/websocket/server/internal/DelegatedServerUpgradeRequest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee9/websocket/server/internal/DelegatedServerUpgradeRequest.java @@ -18,7 +18,6 @@ import java.net.URI; import java.security.Principal; import java.security.cert.X509Certificate; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; @@ -35,6 +34,7 @@ import org.eclipse.jetty.ee9.websocket.common.JettyExtensionConfig; import org.eclipse.jetty.ee9.websocket.server.JettyServerUpgradeRequest; import org.eclipse.jetty.http.BadMessageException; +import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.websocket.core.WebSocketConstants; @@ -114,9 +114,7 @@ public int getHeaderInt(String name) @Override public Map> getHeaders() { - Map> headers = upgradeRequest.getHeaders().getFieldNamesCollection().stream() - .collect(Collectors.toMap((name) -> name, (name) -> new ArrayList<>(getHeaders(name)))); - return Collections.unmodifiableMap(headers); + return Collections.unmodifiableMap(HttpFields.asMap(upgradeRequest.getHeaders())); } @Override diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee9/websocket/server/internal/DelegatedServerUpgradeResponse.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee9/websocket/server/internal/DelegatedServerUpgradeResponse.java index 23af9f519bf7..86fe01d3dc5a 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee9/websocket/server/internal/DelegatedServerUpgradeResponse.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee9/websocket/server/internal/DelegatedServerUpgradeResponse.java @@ -14,7 +14,6 @@ package org.eclipse.jetty.ee9.websocket.server.internal; import java.io.IOException; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -25,6 +24,7 @@ import org.eclipse.jetty.ee9.websocket.api.ExtensionConfig; import org.eclipse.jetty.ee9.websocket.common.JettyExtensionConfig; import org.eclipse.jetty.ee9.websocket.server.JettyServerUpgradeResponse; +import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.websocket.core.WebSocketConstants; import org.eclipse.jetty.websocket.core.server.ServerUpgradeResponse; @@ -88,9 +88,7 @@ public Set getHeaderNames() @Override public Map> getHeaders() { - Map> headers = getHeaderNames().stream() - .collect(Collectors.toMap((name) -> name, (name) -> new ArrayList<>(getHeaders(name)))); - return Collections.unmodifiableMap(headers); + return Collections.unmodifiableMap(HttpFields.asMap(upgradeResponse.getHeaders())); } @Override