433793 - WebSocket / empty protocol list in ServerEndpointConfig.Configurator when using non-exact header name

+ Marking header (and parameter) maps as case-insensitive.
This commit is contained in:
Joakim Erdfelt 2014-05-05 13:46:58 -07:00
parent 8e957b5a23
commit 5635f02235
2 changed files with 130 additions and 2 deletions

View File

@ -25,6 +25,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.websocket.Extension; import javax.websocket.Extension;
import javax.websocket.HandshakeResponse; import javax.websocket.HandshakeResponse;
@ -130,6 +131,38 @@ public class ConfiguratorTest
} }
} }
public static class ProtocolsConfigurator extends ServerEndpointConfig.Configurator
{
public static AtomicReference<String> seenProtocols = new AtomicReference<>();
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response)
{
super.modifyHandshake(sec,request,response);
}
@Override
public String getNegotiatedSubprotocol(List<String> supported, List<String> requested)
{
LOG.warn(new Throwable());
String seen = QuoteUtil.join(requested,",");
seenProtocols.compareAndSet(null,seen);
return super.getNegotiatedSubprotocol(supported,requested);
}
}
@ServerEndpoint(value = "/protocols", configurator = ProtocolsConfigurator.class)
public static class ProtocolsSocket
{
@OnMessage
public String onMessage(Session session, String msg)
{
StringBuilder response = new StringBuilder();
response.append("Requested Protocols: [").append(ProtocolsConfigurator.seenProtocols.get()).append("]");
return response.toString();
}
}
private static Server server; private static Server server;
private static URI baseServerUri; private static URI baseServerUri;
@ -149,6 +182,7 @@ public class ConfiguratorTest
container.addEndpoint(CaptureHeadersSocket.class); container.addEndpoint(CaptureHeadersSocket.class);
container.addEndpoint(EmptySocket.class); container.addEndpoint(EmptySocket.class);
container.addEndpoint(NoExtensionsSocket.class); container.addEndpoint(NoExtensionsSocket.class);
container.addEndpoint(ProtocolsSocket.class);
server.start(); server.start();
String host = connector.getHost(); String host = connector.getHost();
@ -215,4 +249,96 @@ public class ConfiguratorTest
Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("Request Header [X-Dummy]: \"Bogus\"")); Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("Request Header [X-Dummy]: \"Bogus\""));
} }
} }
/**
* Test of Sec-WebSocket-Protocol, as seen in RFC-6455, 1 protocol
*/
@Test
public void testProtocol_Single() throws Exception
{
URI uri = baseServerUri.resolve("/protocols");
ProtocolsConfigurator.seenProtocols.set(null);
try (BlockheadClient client = new BlockheadClient(uri))
{
client.addHeader("Sec-WebSocket-Protocol: echo\r\n");
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
client.write(new TextFrame().setPayload("getProtocols"));
EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
WebSocketFrame frame = frames.poll();
Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("Requested Protocols: [\"echo\"]"));
}
}
/**
* Test of Sec-WebSocket-Protocol, as seen in RFC-6455, 3 protocols
*/
@Test
public void testProtocol_Triple() throws Exception
{
URI uri = baseServerUri.resolve("/protocols");
ProtocolsConfigurator.seenProtocols.set(null);
try (BlockheadClient client = new BlockheadClient(uri))
{
client.addHeader("Sec-WebSocket-Protocol: echo, chat, status\r\n");
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
client.write(new TextFrame().setPayload("getProtocols"));
EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
WebSocketFrame frame = frames.poll();
Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("Requested Protocols: [\"echo\",\"chat\",\"status\"]"));
}
}
/**
* Test of Sec-WebSocket-Protocol, using all lowercase header
*/
@Test
public void testProtocol_LowercaseHeader() throws Exception
{
URI uri = baseServerUri.resolve("/protocols");
ProtocolsConfigurator.seenProtocols.set(null);
try (BlockheadClient client = new BlockheadClient(uri))
{
client.addHeader("sec-websocket-protocol: echo, chat, status\r\n");
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
client.write(new TextFrame().setPayload("getProtocols"));
EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
WebSocketFrame frame = frames.poll();
Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("Requested Protocols: [\"echo\",\"chat\",\"status\"]"));
}
}
/**
* Test of Sec-WebSocket-Protocol, using non-spec case header
*/
@Test
public void testProtocol_AltHeaderCase() throws Exception
{
URI uri = baseServerUri.resolve("/protocols");
ProtocolsConfigurator.seenProtocols.set(null);
try (BlockheadClient client = new BlockheadClient(uri))
{
client.addHeader("Sec-Websocket-Protocol: echo, chat, status\r\n");
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
client.write(new TextFrame().setPayload("getProtocols"));
EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
WebSocketFrame frame = frames.poll();
Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("Requested Protocols: [\"echo\",\"chat\",\"status\"]"));
}
}
} }

View File

@ -31,6 +31,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.TreeMap;
import javax.servlet.AsyncContext; import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType; import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher; import javax.servlet.RequestDispatcher;
@ -68,8 +70,8 @@ public class UpgradeHttpServletRequest implements HttpServletRequest
private final String remoteUser; private final String remoteUser;
private final Principal principal; private final Principal principal;
private final Map<String, List<String>> headers = new HashMap<>(8); private final Map<String, List<String>> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
private final Map<String, String[]> parameters = new HashMap<>(2); private final Map<String, String[]> parameters = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
private final Map<String, Object> attributes = new HashMap<>(2); private final Map<String, Object> attributes = new HashMap<>(2);
private final List<Locale> locales = new ArrayList<>(2); private final List<Locale> locales = new ArrayList<>(2);