diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConnectTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConnectTest.java index 597af68b6f2..2f9988dcaae 100644 --- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConnectTest.java +++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConnectTest.java @@ -26,6 +26,8 @@ import java.net.SocketTimeoutException; import java.net.URI; import java.time.Duration; import java.util.EnumSet; +import java.util.List; +import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -182,6 +184,27 @@ public class ClientConnectTest } } + @Test + public void testUpgradeRequest_PercentEncodedQuery() throws Exception + { + CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint(); + client.setIdleTimeout(Duration.ofSeconds(10)); + + URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/echo?name=%25foo")); + ClientUpgradeRequest request = new ClientUpgradeRequest(); + request.setSubProtocols("echo"); + Future future = client.connect(cliSock, wsUri); + + try (Session sess = future.get(30, TimeUnit.SECONDS)) + { + assertThat("Connect.UpgradeRequest", sess.getUpgradeRequest(), notNullValue()); + Map> paramMap = sess.getUpgradeRequest().getParameterMap(); + List values = paramMap.get("name"); + assertThat("Params[name]", values.get(0), is("%foo")); + assertThat("Connect.UpgradeResponse", sess.getUpgradeResponse(), notNullValue()); + } + } + @Test public void testUpgradeWithAuthorizationHeader() throws Exception { diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/Negotiated.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/Negotiated.java index 269fdb8f1a1..17cdaf294a4 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/Negotiated.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/Negotiated.java @@ -28,6 +28,7 @@ import java.util.Objects; import java.util.stream.Collectors; import org.eclipse.jetty.util.MultiMap; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.UrlEncoded; import org.eclipse.jetty.websocket.core.ExtensionConfig; import org.eclipse.jetty.websocket.core.WebSocketConstants; @@ -50,14 +51,17 @@ public class Negotiated this.extensions = extensions; this.protocolVersion = protocolVersion; + String rawQuery = requestURI.getRawQuery(); Map> map; - if (requestURI.getQuery() == null) + if (StringUtil.isBlank(rawQuery)) + { map = Collections.emptyMap(); + } else { map = new HashMap<>(); MultiMap params = new MultiMap<>(); - UrlEncoded.decodeUtf8To(requestURI.getQuery(), params); + UrlEncoded.decodeUtf8To(rawQuery, params); for (String p : params.keySet()) { map.put(p, Collections.unmodifiableList(params.getValues(p)));