PR #12441 - Allow HttpFields.asMap to modify the underlying HttpFields
Signed-off-by: Lachlan Roberts <lachlan.p.roberts@gmail.com>
This commit is contained in:
parent
ec94ca551e
commit
7757941aad
|
@ -928,6 +928,10 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
|
|||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fields the {@link HttpFields} to convert to a {@link Map}.
|
||||
* @return an unmodifiable {@link Map} representing the contents of the {@link HttpFields}.
|
||||
*/
|
||||
static Map<String, List<String>> asMap(HttpFields fields)
|
||||
{
|
||||
Map<String, List<String>> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
|
@ -936,10 +940,20 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
|
|||
if (!headers.containsKey(f.getName()))
|
||||
{
|
||||
HttpHeader header = f.getHeader();
|
||||
headers.put(f.getName(), header == null ? fields.getValuesList(f.getName()) : fields.getValuesList(header));
|
||||
List<String> values = header == null ? fields.getValuesList(f.getName()) : fields.getValuesList(header);
|
||||
headers.put(f.getName(), Collections.unmodifiableList(values));
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
return Collections.unmodifiableMap(headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fields the {@link HttpFields} to convert to a {@link Map}.
|
||||
* @return a {@link Map} where changes to the contents will be reflected in the supplied {@link HttpFields}.
|
||||
*/
|
||||
static Map<String, List<String>> asMap(HttpFields.Mutable fields)
|
||||
{
|
||||
return new HttpFieldsMap(fields);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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;
|
||||
|
||||
class HttpFieldsMap extends AbstractMap<String, List<String>>
|
||||
{
|
||||
private final HttpFields.Mutable httpFields;
|
||||
|
||||
public HttpFieldsMap(HttpFields.Mutable httpFields)
|
||||
{
|
||||
this.httpFields = httpFields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> get(Object key)
|
||||
{
|
||||
if (key instanceof String s)
|
||||
return httpFields.getValuesList(s);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> put(String key, List<String> value)
|
||||
{
|
||||
List<String> oldValue = get(key);
|
||||
httpFields.put(key, value);
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> remove(Object key)
|
||||
{
|
||||
if (key instanceof String s)
|
||||
{
|
||||
List<String> oldValue = get(s);
|
||||
httpFields.remove(s);
|
||||
return oldValue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<String, List<String>>> entrySet()
|
||||
{
|
||||
return new AbstractSet<>()
|
||||
{
|
||||
@Override
|
||||
public Iterator<Entry<String, List<String>>> iterator()
|
||||
{
|
||||
return new Iterator<>()
|
||||
{
|
||||
private final Iterator<String> iterator = httpFields.getFieldNamesCollection().iterator();
|
||||
private String name = null;
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<String, List<String>> next()
|
||||
{
|
||||
name = iterator.next();
|
||||
return new HttpFieldsEntry(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove()
|
||||
{
|
||||
if (name != null)
|
||||
{
|
||||
HttpFieldsMap.this.remove(name);
|
||||
name = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
return httpFields.getFieldNamesCollection().size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class HttpFieldsEntry implements Entry<String, List<String>>
|
||||
{
|
||||
private final String _name;
|
||||
|
||||
public HttpFieldsEntry(String name)
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey()
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getValue()
|
||||
{
|
||||
return HttpFieldsMap.this.get(_name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> setValue(List<String> 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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -79,7 +79,7 @@ public class DelegatedJettyClientUpgradeRequest implements UpgradeRequest
|
|||
@Override
|
||||
public Map<String, List<String>> getHeaders()
|
||||
{
|
||||
return Collections.unmodifiableMap(HttpFields.asMap(delegate.getHeaders()));
|
||||
return HttpFields.asMap(delegate.getHeaders());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -65,7 +65,7 @@ public class DelegatedJettyClientUpgradeResponse implements UpgradeResponse
|
|||
@Override
|
||||
public Map<String, List<String>> getHeaders()
|
||||
{
|
||||
return Collections.unmodifiableMap(HttpFields.asMap(delegate.getHeaders()));
|
||||
return HttpFields.asMap(delegate.getHeaders());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,7 +16,6 @@ package org.eclipse.jetty.websocket.server.internal;
|
|||
import java.net.HttpCookie;
|
||||
import java.net.URI;
|
||||
import java.security.Principal;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -73,7 +72,7 @@ class UpgradeRequestDelegate implements UpgradeRequest
|
|||
@Override
|
||||
public Map<String, List<String>> getHeaders()
|
||||
{
|
||||
return Collections.unmodifiableMap(HttpFields.asMap(request.getHeaders()));
|
||||
return HttpFields.asMap(request.getHeaders());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
package org.eclipse.jetty.ee10.websocket.jakarta.client.internal;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -39,16 +38,11 @@ public class JsrUpgradeListener implements UpgradeListener
|
|||
if (configurator == null)
|
||||
return;
|
||||
|
||||
// Give headers to configurator
|
||||
HttpFields fields = request.getHeaders();
|
||||
Map<String, List<String>> originalHeaders = HttpFields.asMap(fields);
|
||||
configurator.beforeRequest(originalHeaders);
|
||||
|
||||
// Reset headers on HttpRequest per configurator
|
||||
request.headers(headers ->
|
||||
{
|
||||
headers.clear();
|
||||
originalHeaders.forEach(headers::put);
|
||||
// Give headers to configurator
|
||||
Map<String, List<String>> headersMap = HttpFields.asMap(headers);
|
||||
configurator.beforeRequest(headersMap);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -58,7 +52,7 @@ public class JsrUpgradeListener implements UpgradeListener
|
|||
if (configurator == null)
|
||||
return;
|
||||
|
||||
HandshakeResponse handshakeResponse = () -> Collections.unmodifiableMap(HttpFields.asMap(response.getHeaders()));
|
||||
HandshakeResponse handshakeResponse = () -> HttpFields.asMap(response.getHeaders());
|
||||
configurator.afterResponse(handshakeResponse);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,8 +156,6 @@ public class JakartaWebSocketCreator implements WebSocketCreator
|
|||
|
||||
// [JSR] Step 5: Call modifyHandshake
|
||||
configurator.modifyHandshake(config, jsrHandshakeRequest, jsrHandshakeResponse);
|
||||
// Set modified headers Map back into response properly
|
||||
jsrHandshakeResponse.setHeaders(jsrHandshakeResponse.getHeaders());
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
@ -15,7 +15,6 @@ package org.eclipse.jetty.ee10.websocket.jakarta.server.internal;
|
|||
|
||||
import java.net.URI;
|
||||
import java.security.Principal;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -46,7 +45,7 @@ public class JsrHandshakeRequest implements HandshakeRequest
|
|||
@Override
|
||||
public Map<String, List<String>> getHeaders()
|
||||
{
|
||||
return Collections.unmodifiableMap(HttpFields.asMap(delegate.getHeaders()));
|
||||
return HttpFields.asMap(delegate.getHeaders());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -22,13 +22,11 @@ import org.eclipse.jetty.websocket.core.server.ServerUpgradeResponse;
|
|||
|
||||
public class JsrHandshakeResponse implements HandshakeResponse
|
||||
{
|
||||
private final ServerUpgradeResponse delegate;
|
||||
private final Map<String, List<String>> headers;
|
||||
|
||||
public JsrHandshakeResponse(ServerUpgradeResponse resp)
|
||||
{
|
||||
this.delegate = resp;
|
||||
this.headers = HttpFields.asMap(delegate.getHeaders());
|
||||
this.headers = HttpFields.asMap(resp.getHeaders());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -36,9 +34,4 @@ public class JsrHandshakeResponse implements HandshakeResponse
|
|||
{
|
||||
return headers;
|
||||
}
|
||||
|
||||
public void setHeaders(Map<String, List<String>> headers)
|
||||
{
|
||||
headers.forEach((key, values) -> delegate.getHeaders().put(key, values));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,7 +121,7 @@ public class DelegatedServerUpgradeRequest implements JettyServerUpgradeRequest
|
|||
@Override
|
||||
public Map<String, List<String>> getHeaders()
|
||||
{
|
||||
return Collections.unmodifiableMap(HttpFields.asMap(upgradeRequest.getHeaders()));
|
||||
return HttpFields.asMap(upgradeRequest.getHeaders());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
package org.eclipse.jetty.ee9.websocket.jakarta.client.internal;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -39,16 +38,11 @@ public class JsrUpgradeListener implements UpgradeListener
|
|||
if (configurator == null)
|
||||
return;
|
||||
|
||||
// Give headers to configurator
|
||||
HttpFields fields = request.getHeaders();
|
||||
Map<String, List<String>> originalHeaders = HttpFields.asMap(fields);
|
||||
configurator.beforeRequest(originalHeaders);
|
||||
|
||||
// Reset headers on HttpRequest per configurator
|
||||
request.headers(headers ->
|
||||
{
|
||||
headers.clear();
|
||||
originalHeaders.forEach(headers::put);
|
||||
// Give headers to configurator
|
||||
Map<String, List<String>> headersMap = HttpFields.asMap(headers);
|
||||
configurator.beforeRequest(headersMap);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -58,7 +52,7 @@ public class JsrUpgradeListener implements UpgradeListener
|
|||
if (configurator == null)
|
||||
return;
|
||||
|
||||
HandshakeResponse handshakeResponse = () -> Collections.unmodifiableMap(HttpFields.asMap(response.getHeaders()));
|
||||
HandshakeResponse handshakeResponse = () -> HttpFields.asMap(response.getHeaders());
|
||||
configurator.afterResponse(handshakeResponse);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,8 +156,6 @@ public class JakartaWebSocketCreator implements WebSocketCreator
|
|||
|
||||
// [JSR] Step 5: Call modifyHandshake
|
||||
configurator.modifyHandshake(config, jsrHandshakeRequest, jsrHandshakeResponse);
|
||||
// Set modified headers Map back into response properly
|
||||
jsrHandshakeResponse.setHeaders(jsrHandshakeResponse.getHeaders());
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
@ -15,7 +15,6 @@ package org.eclipse.jetty.ee9.websocket.jakarta.server.internal;
|
|||
|
||||
import java.net.URI;
|
||||
import java.security.Principal;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -46,7 +45,7 @@ public class JsrHandshakeRequest implements HandshakeRequest
|
|||
@Override
|
||||
public Map<String, List<String>> getHeaders()
|
||||
{
|
||||
return Collections.unmodifiableMap(HttpFields.asMap(delegate.getHeaders()));
|
||||
return HttpFields.asMap(delegate.getHeaders());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -22,13 +22,11 @@ import org.eclipse.jetty.websocket.core.server.ServerUpgradeResponse;
|
|||
|
||||
public class JsrHandshakeResponse implements HandshakeResponse
|
||||
{
|
||||
private final ServerUpgradeResponse delegate;
|
||||
private final Map<String, List<String>> headers;
|
||||
|
||||
public JsrHandshakeResponse(ServerUpgradeResponse resp)
|
||||
{
|
||||
this.delegate = resp;
|
||||
this.headers = HttpFields.asMap(delegate.getHeaders());
|
||||
this.headers = HttpFields.asMap(resp.getHeaders());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -36,9 +34,4 @@ public class JsrHandshakeResponse implements HandshakeResponse
|
|||
{
|
||||
return headers;
|
||||
}
|
||||
|
||||
public void setHeaders(Map<String, List<String>> headers)
|
||||
{
|
||||
headers.forEach((key, values) -> delegate.getHeaders().put(key, values));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ public class DelegatedJettyClientUpgradeRequest implements UpgradeRequest
|
|||
@Override
|
||||
public Map<String, List<String>> getHeaders()
|
||||
{
|
||||
return Collections.unmodifiableMap(HttpFields.asMap(delegate.getHeaders()));
|
||||
return HttpFields.asMap(delegate.getHeaders());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -65,7 +65,7 @@ public class DelegatedJettyClientUpgradeResponse implements UpgradeResponse
|
|||
@Override
|
||||
public Map<String, List<String>> getHeaders()
|
||||
{
|
||||
return Collections.unmodifiableMap(HttpFields.asMap(delegate.getHeaders()));
|
||||
return HttpFields.asMap(delegate.getHeaders());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -314,7 +314,6 @@ public abstract class JettyWebSocketServlet extends HttpServlet
|
|||
try
|
||||
{
|
||||
Object webSocket = creator.createWebSocket(request, response);
|
||||
response.copyHeaders();
|
||||
if (webSocket == null)
|
||||
callback.succeeded();
|
||||
return webSocket;
|
||||
|
|
|
@ -114,7 +114,7 @@ public class DelegatedServerUpgradeRequest implements JettyServerUpgradeRequest
|
|||
@Override
|
||||
public Map<String, List<String>> getHeaders()
|
||||
{
|
||||
return Collections.unmodifiableMap(HttpFields.asMap(upgradeRequest.getHeaders()));
|
||||
return HttpFields.asMap(upgradeRequest.getHeaders());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
package org.eclipse.jetty.ee9.websocket.server.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -33,17 +32,10 @@ public class DelegatedServerUpgradeResponse implements JettyServerUpgradeRespons
|
|||
{
|
||||
private final ServerUpgradeResponse upgradeResponse;
|
||||
private final HttpServletResponse httpServletResponse;
|
||||
private final boolean isUpgraded;
|
||||
|
||||
public DelegatedServerUpgradeResponse(ServerUpgradeResponse response)
|
||||
{
|
||||
this(response, false);
|
||||
}
|
||||
|
||||
public DelegatedServerUpgradeResponse(ServerUpgradeResponse response, boolean isUpgraded)
|
||||
{
|
||||
this.upgradeResponse = response;
|
||||
this.isUpgraded = isUpgraded;
|
||||
this.httpServletResponse = (HttpServletResponse)response.getRequest()
|
||||
.getAttribute(WebSocketConstants.WEBSOCKET_WRAPPED_RESPONSE_ATTRIBUTE);
|
||||
}
|
||||
|
@ -56,11 +48,6 @@ public class DelegatedServerUpgradeResponse implements JettyServerUpgradeRespons
|
|||
upgradeResponse.getHeaders().add(name, value);
|
||||
}
|
||||
|
||||
public void copyHeaders()
|
||||
{
|
||||
headers.forEach((key, values) -> upgradeResponse.getHeaders().put(key, values));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeader(String name, String value)
|
||||
{
|
||||
|
@ -100,9 +87,6 @@ public class DelegatedServerUpgradeResponse implements JettyServerUpgradeRespons
|
|||
@Override
|
||||
public Map<String, List<String>> getHeaders()
|
||||
{
|
||||
if (isUpgraded)
|
||||
return Collections.unmodifiableMap(HttpFields.asMap(upgradeResponse.getHeaders()));
|
||||
else
|
||||
return HttpFields.asMap(upgradeResponse.getHeaders());
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ public class JettyServerFrameHandlerFactory extends JettyWebSocketFrameHandlerFa
|
|||
{
|
||||
JettyWebSocketFrameHandler frameHandler = super.newJettyFrameHandler(websocketPojo);
|
||||
frameHandler.setUpgradeRequest(new DelegatedServerUpgradeRequest(upgradeRequest));
|
||||
frameHandler.setUpgradeResponse(new DelegatedServerUpgradeResponse(upgradeResponse, true));
|
||||
frameHandler.setUpgradeResponse(new DelegatedServerUpgradeResponse(upgradeResponse));
|
||||
return frameHandler;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue