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..c810498886b2 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
@@ -927,6 +927,21 @@ default int size()
return size;
}
+ /**
+ *
Wraps an instance of {@link HttpFields} as a {@link Map}.
+ * If the provided {@link HttpFields} is an instance of {@link HttpFields.Mutable} then changes to the
+ * {@link Map} will be reflected in the underlying {@link HttpFields}.
+ * Otherwise, any modification to the {@link Map} will throw {@link UnsupportedOperationException}.
+ * @param fields the {@link HttpFields} to convert to a {@link Map}.
+ * @return an {@link Map} representing the contents of the {@link HttpFields}.
+ */
+ static Map> asMap(HttpFields fields)
+ {
+ return (fields instanceof HttpFields.Mutable mutable)
+ ? new HttpFieldsMap.Mutable(mutable)
+ : new HttpFieldsMap.Immutable(fields);
+ }
+
/**
* @return a sequential stream of the {@link HttpField}s in this instance
*/
diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFieldsMap.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFieldsMap.java
new file mode 100644
index 000000000000..f5d19855d9fa
--- /dev/null
+++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFieldsMap.java
@@ -0,0 +1,237 @@
+//
+// ========================================================================
+// 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.http;
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import org.eclipse.jetty.util.StringUtil;
+
+/**
+ * A {@link java.util.Map} which is backed by an instance of {@link HttpFields.Mutable}.
+ * @see HttpFieldsMap.Mutable
+ * @see HttpFieldsMap.Immutable
+ */
+abstract class HttpFieldsMap extends AbstractMap>
+{
+ /**
+ * A {@link java.util.Map} which is backed by an instance of {@link HttpFields.Mutable}.
+ * Any changes to the {@link java.util.Map} will be reflected in the underlying instance of {@link HttpFields.Mutable}.
+ */
+ public static class Mutable extends HttpFieldsMap
+ {
+ private final HttpFields.Mutable httpFields;
+
+ public Mutable(HttpFields.Mutable httpFields)
+ {
+ this.httpFields = httpFields;
+ }
+
+ @Override
+ public List get(Object key)
+ {
+ if (key instanceof String s)
+ return httpFields.getValuesList(s);
+ return null;
+ }
+
+ @Override
+ public List put(String key, List value)
+ {
+ List oldValue = get(key);
+ httpFields.put(key, value);
+ return oldValue;
+ }
+
+ @Override
+ public List remove(Object key)
+ {
+ if (key instanceof String s)
+ {
+ List oldValue = get(s);
+ httpFields.remove(s);
+ return oldValue;
+ }
+ return null;
+ }
+
+ @Override
+ public Set>> entrySet()
+ {
+ return new AbstractSet<>()
+ {
+ @Override
+ public Iterator>> iterator()
+ {
+ return new Iterator<>()
+ {
+ private final Iterator iterator = httpFields.getFieldNamesCollection().iterator();
+ private String name = null;
+
+ @Override
+ public boolean hasNext()
+ {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public Entry> next()
+ {
+ name = iterator.next();
+ return new HttpFieldsEntry(name);
+ }
+
+ @Override
+ public void remove()
+ {
+ if (name != null)
+ {
+ Mutable.this.remove(name);
+ name = null;
+ }
+ }
+ };
+ }
+
+ @Override
+ public int size()
+ {
+ return httpFields.getFieldNamesCollection().size();
+ }
+ };
+ }
+ }
+
+ /**
+ * A {@link java.util.Map} which is backed by an instance of {@link HttpFields.Mutable}.
+ * Any attempt to modify the map will throw {@link UnsupportedOperationException}.
+ */
+ public static class Immutable extends HttpFieldsMap
+ {
+ private final HttpFields httpFields;
+
+ public Immutable(HttpFields httpFields)
+ {
+ this.httpFields = httpFields;
+ }
+
+ @Override
+ public List get(Object key)
+ {
+ if (key instanceof String s)
+ return httpFields.getValuesList(s);
+ return null;
+ }
+
+ @Override
+ public List put(String key, List value)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List remove(Object key)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set>> entrySet()
+ {
+ return new AbstractSet<>()
+ {
+ @Override
+ public Iterator>> iterator()
+ {
+ return new Iterator<>()
+ {
+ private final Iterator iterator = httpFields.getFieldNamesCollection().iterator();
+
+ @Override
+ public boolean hasNext()
+ {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public Entry> next()
+ {
+ return new HttpFieldsEntry(iterator.next());
+ }
+
+ @Override
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ @Override
+ public int size()
+ {
+ return httpFields.getFieldNamesCollection().size();
+ }
+ };
+ }
+ }
+
+ private class HttpFieldsEntry implements Entry>
+ {
+ private final String _name;
+
+ public HttpFieldsEntry(String name)
+ {
+ _name = name;
+ }
+
+ @Override
+ public String getKey()
+ {
+ return _name;
+ }
+
+ @Override
+ public List getValue()
+ {
+ return HttpFieldsMap.this.get(_name);
+ }
+
+ @Override
+ public List setValue(List value)
+ {
+ return HttpFieldsMap.this.put(_name, value);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ return true;
+ if (o instanceof HttpFieldsEntry other)
+ return StringUtil.asciiEqualsIgnoreCase(_name, other.getKey());
+ return false;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hash(StringUtil.asciiToLowerCase(_name));
+ }
+ }
+}
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..0d5d67e62bed 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 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..0e05930cc2f7 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 HttpFields.asMap(delegate.getHeaders());
}
@Override
diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/ServerWebSocketContainer.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/ServerWebSocketContainer.java
index cf8a1755fad3..9d50cd242f5e 100644
--- a/jetty-core/jetty-websocket/jetty-websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/ServerWebSocketContainer.java
+++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/ServerWebSocketContainer.java
@@ -359,13 +359,12 @@ private org.eclipse.jetty.websocket.core.server.WebSocketCreator newWebSocketCre
{
try
{
- Object webSocket = creator.createWebSocket(new ServerUpgradeRequestDelegate(rq), new ServerUpgradeResponseDelegate(rq, rs), cb);
- if (webSocket == null)
- cb.succeeded();
- return webSocket;
+ return creator.createWebSocket(new ServerUpgradeRequestDelegate(rq), new ServerUpgradeResponseDelegate(rq, rs), cb);
}
catch (Throwable x)
{
+ if (LOG.isDebugEnabled())
+ LOG.debug("Could not create WebSocket endpoint", x);
cb.failed(x);
return null;
}
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..5c14b0bd5e98 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,23 +38,11 @@ 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
- configurator.beforeRequest(originalHeaders);
-
- // Reset headers on HttpRequest per configurator
request.headers(headers ->
{
- headers.clear();
- originalHeaders.forEach(headers::put);
+ // Give headers to configurator
+ Map> headersMap = HttpFields.asMap(headers);
+ configurator.beforeRequest(headersMap);
});
}
@@ -67,18 +52,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/JakartaWebSocketServerContainer.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/JakartaWebSocketServerContainer.java
index 041bf6b427af..c576cb79e27d 100644
--- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/JakartaWebSocketServerContainer.java
+++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/JakartaWebSocketServerContainer.java
@@ -322,9 +322,9 @@ public void upgradeHttpToWebSocket(Object httpServletRequest, Object httpServlet
servletContextRequest.setAttribute(WebSocketConstants.WEBSOCKET_WRAPPED_RESPONSE_ATTRIBUTE, response);
if (handshaker.upgradeRequest(negotiator, servletContextRequest, servletContextResponse, callback, components, defaultCustomizer))
- {
callback.block();
- }
+ else
+ throw new IllegalStateException("Invalid WebSocket Upgrade Request");
}
finally
{
diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JakartaWebSocketCreator.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JakartaWebSocketCreator.java
index 6382b8c77ee1..994ec93a9162 100644
--- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JakartaWebSocketCreator.java
+++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JakartaWebSocketCreator.java
@@ -156,8 +156,6 @@ public Map getUserProperties()
// [JSR] Step 5: Call modifyHandshake
configurator.modifyHandshake(config, jsrHandshakeRequest, jsrHandshakeResponse);
- // Set modified headers Map back into response properly
- jsrHandshakeResponse.setHeaders(jsrHandshakeResponse.getHeaders());
try
{
@@ -168,7 +166,8 @@ public Map getUserProperties()
}
catch (Throwable x)
{
- LOG.warn("Unable to create websocket: {}", config.getEndpointClass().getName(), x);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Unable to create websocket: {}", config.getEndpointClass().getName(), x);
callback.failed(x);
return null;
}
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..546794d54f5e 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,14 @@
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 +45,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 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..c1d9b1dfff2a 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,24 +13,20 @@
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
{
- private final ServerUpgradeResponse delegate;
private final Map> headers;
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(resp.getHeaders());
}
@Override
@@ -38,9 +34,4 @@ public Map> getHeaders()
{
return headers;
}
-
- public void setHeaders(Map> headers)
- {
- headers.forEach((key, values) -> delegate.getHeaders().put(key, values));
- }
}
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-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/AnnotatedClientEndpointTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/AnnotatedClientEndpointTest.java
index d68ff6bed0e2..83ac45d0cbee 100644
--- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/AnnotatedClientEndpointTest.java
+++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/AnnotatedClientEndpointTest.java
@@ -15,14 +15,12 @@
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.util.Collections;
import java.util.Date;
import jakarta.websocket.ClientEndpoint;
import jakarta.websocket.ClientEndpointConfig;
import jakarta.websocket.ContainerProvider;
import jakarta.websocket.EndpointConfig;
-import jakarta.websocket.HandshakeResponse;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
@@ -75,12 +73,6 @@ public Date onBinary(ByteBuffer buf)
public static class AnnotatedEndpointConfigurator extends ClientEndpointConfig.Configurator
{
- @Override
- public void afterResponse(HandshakeResponse hr)
- {
- hr.getHeaders().put("X-Test", Collections.singletonList("Extra"));
- super.afterResponse(hr);
- }
}
private static CoreServer server;
diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/JettyWebSocketServerContainer.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/JettyWebSocketServerContainer.java
index d9e329f0cdbb..9c896bebc301 100644
--- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/JettyWebSocketServerContainer.java
+++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/JettyWebSocketServerContainer.java
@@ -158,6 +158,8 @@ public void addMapping(String pathSpec, JettyWebSocketCreator creator)
}
catch (Throwable t)
{
+ if (LOG.isDebugEnabled())
+ LOG.debug("Could not create WebSocket endpoint", t);
cb.failed(t);
return null;
}
@@ -203,11 +205,14 @@ public boolean upgrade(JettyWebSocketCreator creator, HttpServletRequest request
try
{
Object webSocket = creator.createWebSocket(new DelegatedServerUpgradeRequest(req), new DelegatedServerUpgradeResponse(resp));
- cb.succeeded();
+ if (webSocket == null)
+ cb.succeeded();
return webSocket;
}
catch (Throwable t)
{
+ if (LOG.isDebugEnabled())
+ LOG.debug("Could not create WebSocket endpoint", t);
cb.failed(t);
return null;
}
diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/JettyWebSocketServlet.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/JettyWebSocketServlet.java
index 9d412559321f..b3c7bf38fb51 100644
--- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/JettyWebSocketServlet.java
+++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/JettyWebSocketServlet.java
@@ -303,7 +303,8 @@ public Object createWebSocket(ServerUpgradeRequest upgradeRequest, ServerUpgrade
try
{
Object webSocket = creator.createWebSocket(request, response);
- callback.succeeded();
+ if (webSocket == null)
+ callback.succeeded();
return webSocket;
}
catch (Throwable t)
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..3f8f0155fa45 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 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..51f0d8eed7f4 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;
@@ -35,13 +35,22 @@ public class DelegatedServerUpgradeResponse implements JettyServerUpgradeRespons
{
private final ServerUpgradeResponse upgradeResponse;
private final HttpServletResponse httpServletResponse;
+ private final boolean isUpgraded;
+ private final Map> headers;
public DelegatedServerUpgradeResponse(ServerUpgradeResponse response)
+ {
+ this(response, false);
+ }
+
+ public DelegatedServerUpgradeResponse(ServerUpgradeResponse response, boolean isUpgraded)
{
upgradeResponse = response;
+ this.isUpgraded = isUpgraded;
ServletContextResponse servletContextResponse = Response.as(response, ServletContextResponse.class);
this.httpServletResponse = (HttpServletResponse)servletContextResponse.getRequest()
.getAttribute(WebSocketConstants.WEBSOCKET_WRAPPED_RESPONSE_ATTRIBUTE);
+ headers = HttpFields.asMap(upgradeResponse.getHeaders());
}
@Override
@@ -55,13 +64,13 @@ public void addHeader(String name, String value)
@Override
public void setHeader(String name, String value)
{
- upgradeResponse.getHeaders().put(name, value);
+ headers.put(name, List.of(value));
}
@Override
public void setHeader(String name, List values)
{
- upgradeResponse.getHeaders().put(name, values);
+ headers.put(name, values);
}
@Override
@@ -91,9 +100,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 isUpgraded ? Collections.unmodifiableMap(headers) : headers;
}
@Override
diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/internal/JettyServerFrameHandlerFactory.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/internal/JettyServerFrameHandlerFactory.java
index 17f50e82a461..5fd68de68f75 100644
--- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/internal/JettyServerFrameHandlerFactory.java
+++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/internal/JettyServerFrameHandlerFactory.java
@@ -41,7 +41,7 @@ public FrameHandler newFrameHandler(Object websocketPojo, ServerUpgradeRequest u
{
JettyWebSocketFrameHandler frameHandler = super.newJettyFrameHandler(websocketPojo);
frameHandler.setUpgradeRequest(new DelegatedServerUpgradeRequest(upgradeRequest));
- frameHandler.setUpgradeResponse(new DelegatedServerUpgradeResponse(upgradeResponse));
+ frameHandler.setUpgradeResponse(new DelegatedServerUpgradeResponse(upgradeResponse, true));
return frameHandler;
}
}
diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/UpgradeHeadersTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/UpgradeHeadersTest.java
new file mode 100644
index 000000000000..6d228db7475c
--- /dev/null
+++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/UpgradeHeadersTest.java
@@ -0,0 +1,124 @@
+//
+// ========================================================================
+// 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.tests;
+
+import java.net.URI;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.client.Request;
+import org.eclipse.jetty.client.Response;
+import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee10.websocket.server.JettyWebSocketCreator;
+import org.eclipse.jetty.ee10.websocket.server.config.JettyWebSocketServletContainerInitializer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
+import org.eclipse.jetty.websocket.client.JettyUpgradeListener;
+import org.eclipse.jetty.websocket.client.WebSocketClient;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class UpgradeHeadersTest
+{
+ private Server _server;
+ private WebSocketClient _client;
+ private ServerConnector _connector;
+
+ public void start(JettyWebSocketCreator creator) throws Exception
+ {
+ _server = new Server();
+ _connector = new ServerConnector(_server);
+ _server.addConnector(_connector);
+
+ ServletContextHandler contextHandler = new ServletContextHandler();
+ JettyWebSocketServletContainerInitializer.configure(contextHandler, (servletContext, container) ->
+ container.addMapping("/", creator));
+ _server.setHandler(contextHandler);
+
+ _server.start();
+ _client = new WebSocketClient();
+ _client.start();
+ }
+
+ @AfterEach
+ public void after() throws Exception
+ {
+ _client.stop();
+ _server.stop();
+ }
+
+ @Test
+ public void testCaseInsensitiveUpgradeHeaders() throws Exception
+ {
+ start((request, 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");
+
+ return new EchoSocket();
+ });
+
+ EventSocket clientEndpoint = new EventSocket();
+ URI uri = URI.create("ws://localhost:" + _connector.getLocalPort());
+
+ ClientUpgradeRequest clientUpgradeRequest = new ClientUpgradeRequest();
+ clientUpgradeRequest.getHeaders().put("sentHeader", List.of("value123"));
+ if (clientUpgradeRequest.getHeaders().get("SenTHeaDer") == null)
+ throw new IllegalStateException("No custom Header on ClientUpgradeRequest");
+
+ JettyUpgradeListener upgradeListener = new JettyUpgradeListener()
+ {
+ @Override
+ public void onHandshakeRequest(Request request)
+ {
+ // 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 client Request");
+ if (request.getHeaders().get("SenTHeaDer") == null)
+ throw new IllegalStateException("No custom Header on ClientUpgradeRequest");
+ }
+
+ @Override
+ public void onHandshakeResponse(Request request, Response response)
+ {
+ if (response.getHeaders().get("MyHeAdEr") == null)
+ throw new IllegalStateException("No custom Header on HandshakeResponse");
+ if (response.getHeaders().get("cOnnEcTiOn") == null)
+ throw new IllegalStateException("No Connection Header on HandshakeRequest");
+ }
+ };
+
+ // If any of the above throw it would fail to upgrade to websocket.
+ assertNotNull(_client.connect(clientEndpoint, uri, clientUpgradeRequest, upgradeListener).get(5, TimeUnit.SECONDS));
+ assertTrue(clientEndpoint.openLatch.await(5, TimeUnit.SECONDS));
+ clientEndpoint.session.close();
+ assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
+ }
+}
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..35660cb92556 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,23 +38,11 @@ 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
- configurator.beforeRequest(originalHeaders);
-
- // Reset headers on HttpRequest per configurator
request.headers(headers ->
{
- headers.clear();
- originalHeaders.forEach(headers::put);
+ // Give headers to configurator
+ Map> headersMap = HttpFields.asMap(headers);
+ configurator.beforeRequest(headersMap);
});
}
@@ -67,18 +52,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/JakartaWebSocketServerContainer.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/server/JakartaWebSocketServerContainer.java
index e646f5eb141b..a0e2540450a0 100644
--- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/server/JakartaWebSocketServerContainer.java
+++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/server/JakartaWebSocketServerContainer.java
@@ -323,9 +323,9 @@ public void upgradeHttpToWebSocket(Object httpServletRequest, Object httpServlet
baseRequest.setAttribute(WebSocketConstants.WEBSOCKET_WRAPPED_RESPONSE_ATTRIBUTE, response);
if (handshaker.upgradeRequest(negotiator, baseRequest, baseResponse, callback, components, defaultCustomizer))
- {
callback.block();
- }
+ else
+ throw new IllegalStateException("Invalid WebSocket Upgrade Request");
}
finally
{
diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/server/internal/JakartaWebSocketCreator.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/server/internal/JakartaWebSocketCreator.java
index b2587594f6a0..1bde01dcbc95 100644
--- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/server/internal/JakartaWebSocketCreator.java
+++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/server/internal/JakartaWebSocketCreator.java
@@ -156,8 +156,6 @@ public Map getUserProperties()
// [JSR] Step 5: Call modifyHandshake
configurator.modifyHandshake(config, jsrHandshakeRequest, jsrHandshakeResponse);
- // Set modified headers Map back into response properly
- jsrHandshakeResponse.setHeaders(jsrHandshakeResponse.getHeaders());
try
{
@@ -168,7 +166,8 @@ public Map getUserProperties()
}
catch (Throwable x)
{
- LOG.warn("Unable to create websocket: {}", config.getEndpointClass().getName(), x);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Unable to create WebSocket: {}", config.getEndpointClass().getName(), x);
callback.failed(x);
return null;
}
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..840d718512dc 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,14 @@
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 +45,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 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..8de0a2d94bb7 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,24 +13,20 @@
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
{
- private final ServerUpgradeResponse delegate;
private final Map> headers;
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(resp.getHeaders());
}
@Override
@@ -38,9 +34,4 @@ public Map> getHeaders()
{
return headers;
}
-
- public void setHeaders(Map> headers)
- {
- headers.forEach((key, values) -> delegate.getHeaders().put(key, values));
- }
}
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-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/client/AnnotatedClientEndpointTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/client/AnnotatedClientEndpointTest.java
index 2fec835746dc..9e5b2d29853f 100644
--- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/client/AnnotatedClientEndpointTest.java
+++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/client/AnnotatedClientEndpointTest.java
@@ -15,14 +15,12 @@
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.util.Collections;
import java.util.Date;
import jakarta.websocket.ClientEndpoint;
import jakarta.websocket.ClientEndpointConfig;
import jakarta.websocket.ContainerProvider;
import jakarta.websocket.EndpointConfig;
-import jakarta.websocket.HandshakeResponse;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
@@ -75,12 +73,6 @@ public Date onBinary(ByteBuffer buf)
public static class AnnotatedEndpointConfigurator extends ClientEndpointConfig.Configurator
{
- @Override
- public void afterResponse(HandshakeResponse hr)
- {
- hr.getHeaders().put("X-Test", Collections.singletonList("Extra"));
- super.afterResponse(hr);
- }
}
private static CoreServer server;
diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee9/websocket/client/impl/DelegatedJettyClientUpgradeRequest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee9/websocket/client/impl/DelegatedJettyClientUpgradeRequest.java
index 27deb9bb3684..d0388a1d0e8b 100644
--- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee9/websocket/client/impl/DelegatedJettyClientUpgradeRequest.java
+++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee9/websocket/client/impl/DelegatedJettyClientUpgradeRequest.java
@@ -24,6 +24,7 @@
import org.eclipse.jetty.ee9.websocket.api.ExtensionConfig;
import org.eclipse.jetty.ee9.websocket.api.UpgradeRequest;
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 HttpFields.asMap(delegate.getHeaders());
}
@Override
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..2f14bce6288a 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 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/JettyWebSocketServerContainer.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee9/websocket/server/JettyWebSocketServerContainer.java
index f57c41314736..245fc7855641 100644
--- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee9/websocket/server/JettyWebSocketServerContainer.java
+++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee9/websocket/server/JettyWebSocketServerContainer.java
@@ -155,11 +155,14 @@ public void addMapping(String pathSpec, JettyWebSocketCreator creator)
try
{
Object webSocket = creator.createWebSocket(new DelegatedServerUpgradeRequest(req), new DelegatedServerUpgradeResponse(resp));
- cb.succeeded();
+ if (webSocket == null)
+ cb.succeeded();
return webSocket;
}
catch (Throwable t)
{
+ if (LOG.isDebugEnabled())
+ LOG.debug("Could not create WebSocket endpoint", t);
cb.failed(t);
return null;
}
@@ -205,11 +208,14 @@ public boolean upgrade(JettyWebSocketCreator creator, HttpServletRequest request
try
{
Object webSocket = creator.createWebSocket(new DelegatedServerUpgradeRequest(req), new DelegatedServerUpgradeResponse(resp));
- cb.succeeded();
+ if (webSocket == null)
+ cb.succeeded();
return webSocket;
}
catch (Throwable t)
{
+ if (LOG.isDebugEnabled())
+ LOG.debug("Could not create WebSocket endpoint", t);
cb.failed(t);
return null;
}
diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee9/websocket/server/JettyWebSocketServlet.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee9/websocket/server/JettyWebSocketServlet.java
index 1d8a8263f9ad..87d7c47a409f 100644
--- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee9/websocket/server/JettyWebSocketServlet.java
+++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee9/websocket/server/JettyWebSocketServlet.java
@@ -299,18 +299,22 @@ public boolean removeMapping(String pathSpec)
private record WrappedJettyCreator(JettyWebSocketCreator creator) implements WebSocketCreator
{
+
private JettyWebSocketCreator getJettyWebSocketCreator()
{
return creator;
}
@Override
- public Object createWebSocket(ServerUpgradeRequest request, ServerUpgradeResponse response, Callback callback)
+ public Object createWebSocket(ServerUpgradeRequest upgradeRequest, ServerUpgradeResponse upgradeResponse, Callback callback)
{
+ DelegatedServerUpgradeRequest request = new DelegatedServerUpgradeRequest(upgradeRequest);
+ DelegatedServerUpgradeResponse response = new DelegatedServerUpgradeResponse(upgradeResponse);
try
{
- Object webSocket = creator.createWebSocket(new DelegatedServerUpgradeRequest(request), new DelegatedServerUpgradeResponse(response));
- callback.succeeded();
+ Object webSocket = creator.createWebSocket(request, response);
+ if (webSocket == null)
+ callback.succeeded();
return webSocket;
}
catch (Throwable t)
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..256a3451d9ec 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 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..f1a1ff95b377 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,8 +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;
import java.util.Set;
@@ -25,6 +23,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 +87,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 HttpFields.asMap(upgradeResponse.getHeaders());
}
@Override
diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee9/websocket/tests/UpgradeHeadersTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee9/websocket/tests/UpgradeHeadersTest.java
new file mode 100644
index 000000000000..efea1435f2a4
--- /dev/null
+++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee9/websocket/tests/UpgradeHeadersTest.java
@@ -0,0 +1,124 @@
+//
+// ========================================================================
+// 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.tests;
+
+import java.net.URI;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.client.Request;
+import org.eclipse.jetty.client.Response;
+import org.eclipse.jetty.ee9.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee9.websocket.client.ClientUpgradeRequest;
+import org.eclipse.jetty.ee9.websocket.client.JettyUpgradeListener;
+import org.eclipse.jetty.ee9.websocket.client.WebSocketClient;
+import org.eclipse.jetty.ee9.websocket.server.JettyWebSocketCreator;
+import org.eclipse.jetty.ee9.websocket.server.config.JettyWebSocketServletContainerInitializer;
+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.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class UpgradeHeadersTest
+{
+ private Server _server;
+ private WebSocketClient _client;
+ private ServerConnector _connector;
+
+ public void start(JettyWebSocketCreator creator) throws Exception
+ {
+ _server = new Server();
+ _connector = new ServerConnector(_server);
+ _server.addConnector(_connector);
+
+ ServletContextHandler contextHandler = new ServletContextHandler();
+ JettyWebSocketServletContainerInitializer.configure(contextHandler, (servletContext, container) ->
+ container.addMapping("/", creator));
+ _server.setHandler(contextHandler);
+
+ _server.start();
+ _client = new WebSocketClient();
+ _client.start();
+ }
+
+ @AfterEach
+ public void after() throws Exception
+ {
+ _client.stop();
+ _server.stop();
+ }
+
+ @Test
+ public void testCaseInsensitiveUpgradeHeaders() throws Exception
+ {
+ start((request, 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");
+
+ return new EchoSocket();
+ });
+
+ EventSocket clientEndpoint = new EventSocket();
+ URI uri = URI.create("ws://localhost:" + _connector.getLocalPort());
+
+ ClientUpgradeRequest clientUpgradeRequest = new ClientUpgradeRequest();
+ clientUpgradeRequest.getHeaders().put("sentHeader", List.of("value123"));
+ if (clientUpgradeRequest.getHeaders().get("SenTHeaDer") == null)
+ throw new IllegalStateException("No custom Header on ClientUpgradeRequest");
+
+ JettyUpgradeListener upgradeListener = new JettyUpgradeListener()
+ {
+ @Override
+ public void onHandshakeRequest(Request request)
+ {
+ // 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 client Request");
+ if (request.getHeaders().get("SenTHeaDer") == null)
+ throw new IllegalStateException("No custom Header on ClientUpgradeRequest");
+ }
+
+ @Override
+ public void onHandshakeResponse(Request request, Response response)
+ {
+ if (response.getHeaders().get("MyHeAdEr") == null)
+ throw new IllegalStateException("No custom Header on HandshakeResponse");
+ if (response.getHeaders().get("cOnnEcTiOn") == null)
+ throw new IllegalStateException("No Connection Header on HandshakeRequest");
+ }
+ };
+
+ // If any of the above throw it would fail to upgrade to websocket.
+ assertNotNull(_client.connect(clientEndpoint, uri, clientUpgradeRequest, upgradeListener).get(5, TimeUnit.SECONDS));
+ assertTrue(clientEndpoint.openLatch.await(5, TimeUnit.SECONDS));
+ clientEndpoint.session.close();
+ assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
+ }
+}