diff --git a/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientFilter.java b/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientFilter.java index 04761ccd..d1e892e7 100644 --- a/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientFilter.java +++ b/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -30,6 +30,7 @@ import org.glassfish.tyrus.core.CloseReasons; import org.glassfish.tyrus.core.TyrusUpgradeResponse; import org.glassfish.tyrus.core.Utils; +import org.glassfish.tyrus.core.uri.internal.UriComponent; import org.glassfish.tyrus.spi.ClientEngine; import org.glassfish.tyrus.spi.ReadHandler; import org.glassfish.tyrus.spi.UpgradeRequest; @@ -415,7 +416,7 @@ private HttpContent getHttpContent(UpgradeRequest request) { sb.append(uri.getPath()); final String query = uri.getQuery(); if (query != null) { - sb.append('?').append(query); + sb.append('?').append(isDoubleEncodedQuery(query) ? query : uri.getRawQuery()); } if (sb.length() == 0) { sb.append('/'); @@ -438,6 +439,19 @@ private HttpContent getHttpContent(UpgradeRequest request) { return HttpContent.builder(builder.build()).build(); } + private static boolean isDoubleEncodedQuery(String query) { + final int size = query.length(); + int index = -1; + while ((index = query.indexOf('%', index + 1)) != -1 && index < size - 2) { + char c1 = query.charAt(index + 1); + char c2 = query.charAt(index + 2); + if (UriComponent.isHexCharacter(c1) && UriComponent.isHexCharacter(c2)) { + return true; + } + } + return false; + } + private class ProcessTask extends TaskProcessor.Task { private final ByteBuffer buffer; private final ReadHandler readHandler; diff --git a/tests/e2e/standard-config/src/test/java/org/glassfish/tyrus/test/standard_config/QueryParamTest.java b/tests/e2e/standard-config/src/test/java/org/glassfish/tyrus/test/standard_config/QueryParamTest.java new file mode 100644 index 00000000..d7ab0db6 --- /dev/null +++ b/tests/e2e/standard-config/src/test/java/org/glassfish/tyrus/test/standard_config/QueryParamTest.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.tyrus.test.standard_config; + +import org.glassfish.tyrus.client.ClientManager; +import org.glassfish.tyrus.server.Server; +import org.glassfish.tyrus.test.tools.TestContainer; +import org.junit.Test; + +import jakarta.websocket.ClientEndpointConfig; +import jakarta.websocket.DeploymentException; +import jakarta.websocket.Endpoint; +import jakarta.websocket.EndpointConfig; +import jakarta.websocket.MessageHandler; +import jakarta.websocket.OnMessage; +import jakarta.websocket.Session; +import jakarta.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.net.URI; +import java.net.URLEncoder; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertEquals; + +public class QueryParamTest extends TestContainer { + + public static final String QUERY_NAME = "NAME"; + public static final String QUERY_VALUE = "value10%&value2%"; + + @ServerEndpoint(value = "/query") + public static class QueryEndpoint { + + @OnMessage + public void onMessage(Session session, String message) throws IOException { + session.getBasicRemote().sendText(session.getRequestParameterMap().get(QUERY_NAME).get(0)); + } + } + + @Test + public void testQueryClient() throws DeploymentException { + Server server = startServer(QueryEndpoint.class); + try { + testQueryClient(false); + testQueryClient(true); + } finally { + stopServer(server); + } + } + + public void testQueryClient(boolean fromString) throws DeploymentException { + AtomicReference response = new AtomicReference<>(); + + try { + final CountDownLatch messageLatch = new CountDownLatch(1); + + ClientManager client = createClient(); + client.connectToServer(new Endpoint() { + @Override + public void onOpen(Session session, EndpointConfig config) { + try { + session.addMessageHandler(new MessageHandler.Whole() { + @Override + public void onMessage(String message) { + response.set(message); + messageLatch.countDown(); + } + }); + + session.getBasicRemote().sendText("Hello"); + + } catch (IOException e) { + // do nothing. + } + } + }, ClientEndpointConfig.Builder.create().build(), getURI(QueryEndpoint.class, "ws", fromString)); + + messageLatch.await(1, TimeUnit.SECONDS); + assertEquals(0, messageLatch.getCount()); + assertEquals(QUERY_VALUE, response.get()); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e.getMessage(), e); + } + } + + protected URI getURI(Class serverClass, String scheme, boolean fromString) { + URI uri = super.getURI(serverClass, scheme); + URI withQuery = null; + try { + if (fromString) { + withQuery = new URI(uri.toASCIIString() + "?" + QUERY_NAME + "=" + URLEncoder.encode(QUERY_VALUE, "UTF-8")); + } else { + withQuery = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath(), + QUERY_NAME + "=" + URLEncoder.encode(QUERY_VALUE, "UTF-8"), uri.getFragment()); + } + } catch (Exception e) { + e.printStackTrace(); + } + return withQuery; + } +}