Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #5706 - fix potential NPE from websocket-core ServerUpgradeResponse #5768

Merged
merged 2 commits into from
Dec 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand All @@ -43,31 +42,32 @@ public void addHeader(String name, String value)
{
if (HttpHeader.SEC_WEBSOCKET_SUBPROTOCOL.is(name))
{
setAcceptedSubProtocol(value); // Can only be one, so set
setAcceptedSubProtocol(value);
return;
}

if (HttpHeader.SEC_WEBSOCKET_EXTENSIONS.is(name) && getExtensions() != null)
if (HttpHeader.SEC_WEBSOCKET_EXTENSIONS.is(name))
{
// Move any extensions configs to the headers
response.addHeader(name, ExtensionConfig.toHeaderValue(getExtensions()));
setExtensions(null);
addExtensions(ExtensionConfig.parseList(value));
return;
}

response.addHeader(name, value);
}

public void setHeader(String name, String value)
{

if (HttpHeader.SEC_WEBSOCKET_SUBPROTOCOL.is(name))
{
setAcceptedSubProtocol(value);
return;
}

if (HttpHeader.SEC_WEBSOCKET_EXTENSIONS.is(name))
setExtensions(null);
{
setExtensions(ExtensionConfig.parseList(value));
return;
}

response.setHeader(name, value);
}
Expand All @@ -86,14 +86,21 @@ else if (values.size() == 1)

if (HttpHeader.SEC_WEBSOCKET_EXTENSIONS.is(name))
{
setExtensions(null);
response.setHeader(name, null);
values.forEach(value -> addHeader(name, value));
List<ExtensionConfig> extensions = Collections.emptyList();
if (values != null)
{
extensions = values.stream()
.flatMap(s -> ExtensionConfig.parseList(s).stream())
.collect(Collectors.toList());
}

setExtensions(extensions);
return;
}

response.setHeader(name, null); // clear it out first
values.forEach(value -> response.addHeader(name, value));
response.setHeader(name, null);
if (values != null)
values.forEach(value -> response.addHeader(name, value));
}

public String getAcceptedSubProtocol()
Expand All @@ -113,7 +120,7 @@ public String getHeader(String name)

public Set<String> getHeaderNames()
{
return Collections.unmodifiableSet(new HashSet<>(response.getHeaderNames()));
return Set.copyOf(response.getHeaderNames());
}

public Map<String, List<String>> getHeadersMap()
Expand All @@ -125,7 +132,7 @@ public Map<String, List<String>> getHeadersMap()

public List<String> getHeaders(String name)
{
return Collections.unmodifiableList(new ArrayList<>(response.getHeaders(name)));
return List.copyOf(response.getHeaders(name));
}

public int getStatusCode()
Expand Down Expand Up @@ -154,6 +161,14 @@ public void setAcceptedSubProtocol(String protocol)
negotiation.setSubprotocol(protocol);
}

public void addExtensions(List<ExtensionConfig> configs)
{
ArrayList<ExtensionConfig> combinedConfig = new ArrayList<>();
combinedConfig.addAll(getExtensions());
combinedConfig.addAll(configs);
setExtensions(combinedConfig);
}

public void setExtensions(List<ExtensionConfig> configs)
{
// This validation is also done later in RFC6455Handshaker but it is better to fail earlier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,13 @@
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer;
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class JettyWebSocketNegotiationTest
Expand Down Expand Up @@ -112,7 +111,6 @@ public void testServerError() throws Exception
}
}

@Disabled("Work in progress; ServerUpgradeResponse in websocket-core throws NPEs")
@Test
public void testManualNegotiationInCreator() throws Exception
{
Expand All @@ -123,7 +121,7 @@ public void testManualNegotiationInCreator() throws Exception
.filter(ec -> "permessage-deflate".equals(ec.getName()))
.filter(ec -> ec.getParameters().containsKey("client_no_context_takeover"))
.count();
assertThat(matchedExts, Matchers.is(1L));
assertThat(matchedExts, is(1L));

// Manually drop the param so it is not negotiated in the extension stack.
resp.setHeader("Sec-WebSocket-Extensions", "permessage-deflate");
Expand All @@ -146,6 +144,7 @@ public void onHandshakeResponse(HttpRequest request, HttpResponse response)

client.connect(socket, uri, upgradeRequest, upgradeListener).get(5, TimeUnit.SECONDS);
HttpResponse httpResponse = responseReference.get();
System.err.println(httpResponse.getHeaders());
String extensions = httpResponse.getHeaders().get("Sec-WebSocket-Extensions");
assertThat(extensions, is("permessage-deflate"));
}
}