From 8c7949e61e27902e9a07825484aac5ffe6bd1b56 Mon Sep 17 00:00:00 2001 From: Santiago Pericasgeertsen Date: Thu, 28 Jul 2022 08:55:23 -0400 Subject: [PATCH] Updated TyrusSupport to correctly propagate query params from webserver (#4624) * Updated TyrusSupport to correctly propagate query params from webserver to Tyrus. Moved UriComponent over to common/http for sharing purposes. Updated tests to verify changes. * Fixed checkstyle problems. Signed-off-by: Santiago Pericasgeertsen --- .../io/helidon/common/http}/UriComponent.java | 16 ++++--------- .../common/http}/UriComponentTest.java | 6 ++--- webserver/tyrus/pom.xml | 4 ++++ .../helidon/webserver/tyrus/TyrusSupport.java | 13 +++++++++-- .../tyrus/src/main/java/module-info.java | 3 ++- .../helidon/webserver/tyrus/EchoEndpoint.java | 23 ++++++++++++++++++- .../webserver/tyrus/EchoServiceTest.java | 6 ++--- .../webserver/tyrus/HttpClientTest.java | 2 +- .../helidon/webserver/tyrus/RoutingTest.java | 6 ++--- .../java/io/helidon/webserver/Request.java | 3 ++- 10 files changed, 55 insertions(+), 27 deletions(-) rename {webserver/webserver/src/main/java/io/helidon/webserver => common/http/src/main/java/io/helidon/common/http}/UriComponent.java (88%) rename {webserver/webserver/src/test/java/io/helidon/webserver => common/http/src/test/java/io/helidon/common/http}/UriComponentTest.java (94%) diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/UriComponent.java b/common/http/src/main/java/io/helidon/common/http/UriComponent.java similarity index 88% rename from webserver/webserver/src/main/java/io/helidon/webserver/UriComponent.java rename to common/http/src/main/java/io/helidon/common/http/UriComponent.java index f8be1df7c40..1f64e728ae4 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/UriComponent.java +++ b/common/http/src/main/java/io/helidon/common/http/UriComponent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021 Oracle and/or its affiliates. + * Copyright (c) 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,24 +14,18 @@ * limitations under the License. */ -package io.helidon.webserver; +package io.helidon.common.http; import java.net.URLDecoder; -import io.helidon.common.http.HashParameters; -import io.helidon.common.http.Parameters; - import static java.nio.charset.StandardCharsets.UTF_8; /** * Extracted from Jersey *

* Utility class for validating, encoding and decoding components of a URI. - * - * @author Paul Sandoz - * @author Marek Potociar (marek.potociar at oracle.com) */ -final class UriComponent { +public final class UriComponent { private UriComponent() { } @@ -47,7 +41,7 @@ private UriComponent() { * should be in decoded form. * @return the multivalued map of query parameters. */ - static Parameters decodeQuery(String query, boolean decode) { + public static Parameters decodeQuery(String query, boolean decode) { return decodeQuery(query, true, decode); } @@ -64,7 +58,7 @@ static Parameters decodeQuery(String query, boolean decode) { * should be in decoded form. * @return the multivalued map of query parameters. */ - static Parameters decodeQuery(String query, boolean decodeNames, boolean decodeValues) { + public static Parameters decodeQuery(String query, boolean decodeNames, boolean decodeValues) { Parameters queryParameters = HashParameters.create(); if (query == null || query.isEmpty()) { diff --git a/webserver/webserver/src/test/java/io/helidon/webserver/UriComponentTest.java b/common/http/src/test/java/io/helidon/common/http/UriComponentTest.java similarity index 94% rename from webserver/webserver/src/test/java/io/helidon/webserver/UriComponentTest.java rename to common/http/src/test/java/io/helidon/common/http/UriComponentTest.java index db26ac03e2a..0d90ee4cd3a 100644 --- a/webserver/webserver/src/test/java/io/helidon/webserver/UriComponentTest.java +++ b/common/http/src/test/java/io/helidon/common/http/UriComponentTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021 Oracle and/or its affiliates. + * Copyright (c) 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +14,12 @@ * limitations under the License. */ -package io.helidon.webserver; +package io.helidon.common.http; import java.net.URI; import java.net.URLEncoder; import java.util.Optional; -import io.helidon.common.http.Parameters; - import org.junit.jupiter.api.Test; import static java.nio.charset.StandardCharsets.US_ASCII; diff --git a/webserver/tyrus/pom.xml b/webserver/tyrus/pom.xml index 924a7a8ae62..e9b34f28c76 100644 --- a/webserver/tyrus/pom.xml +++ b/webserver/tyrus/pom.xml @@ -43,6 +43,10 @@ io.helidon.common helidon-common-reactive + + io.helidon.common + helidon-common-http + jakarta.websocket jakarta.websocket-api diff --git a/webserver/tyrus/src/main/java/io/helidon/webserver/tyrus/TyrusSupport.java b/webserver/tyrus/src/main/java/io/helidon/webserver/tyrus/TyrusSupport.java index d4a19db2566..149eedf74a8 100644 --- a/webserver/tyrus/src/main/java/io/helidon/webserver/tyrus/TyrusSupport.java +++ b/webserver/tyrus/src/main/java/io/helidon/webserver/tyrus/TyrusSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. + * Copyright (c) 2020, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,9 @@ import java.net.URI; import java.nio.ByteBuffer; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -31,6 +33,8 @@ import javax.websocket.server.HandshakeRequest; import javax.websocket.server.ServerEndpointConfig; +import io.helidon.common.http.Parameters; +import io.helidon.common.http.UriComponent; import io.helidon.webserver.Handler; import io.helidon.webserver.Routing; import io.helidon.webserver.ServerRequest; @@ -265,9 +269,14 @@ public void accept(ServerRequest req, ServerResponse res) { LOGGER.fine("Initiating WebSocket handshake ..."); - // Create Tyrus request context and copy request headers + // Create Tyrus request context, copying headers and query params + Map paramsMap = new HashMap<>(); + Parameters params = UriComponent.decodeQuery(req.uri().getRawQuery(), true); + params.toMap().forEach((key, value) -> paramsMap.put(key, value.toArray(new String[0]))); RequestContext requestContext = RequestContext.Builder.create() .requestURI(URI.create(req.path().toString())) // excludes context path + .queryString(req.query()) + .parameterMap(paramsMap) .build(); req.headers().toMap().forEach((key, value) -> requestContext.getHeaders().put(key, value)); diff --git a/webserver/tyrus/src/main/java/module-info.java b/webserver/tyrus/src/main/java/module-info.java index 09a0e97fc8a..c34df61495c 100644 --- a/webserver/tyrus/src/main/java/module-info.java +++ b/webserver/tyrus/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ requires io.helidon.common.context; requires io.helidon.common.mapper; requires io.helidon.common.reactive; + requires io.helidon.common.http; requires tyrus.core; requires tyrus.server; diff --git a/webserver/tyrus/src/test/java/io/helidon/webserver/tyrus/EchoEndpoint.java b/webserver/tyrus/src/test/java/io/helidon/webserver/tyrus/EchoEndpoint.java index 8e06b799ae3..3cbbb7c7416 100644 --- a/webserver/tyrus/src/test/java/io/helidon/webserver/tyrus/EchoEndpoint.java +++ b/webserver/tyrus/src/test/java/io/helidon/webserver/tyrus/EchoEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -62,6 +62,24 @@ private static void verifyRunningThread(Session session, Logger logger) throws I } } + /** + * Verify session includes expected query params. + * + * @param session Websocket session. + * @param logger A logger. + * @throws IOException Exception during close. + */ + private static void verifyQueryParams(Session session, Logger logger) throws IOException { + if (!"user=Helidon".equals(session.getQueryString())) { + logger.warning("Websocket session does not include required query params"); + session.close(); + } + if (!session.getRequestParameterMap().get("user").get(0).equals("Helidon")) { + logger.warning("Websocket session does not include required query parameter map"); + session.close(); + } + } + public static class ServerConfigurator extends ServerEndpointConfig.Configurator { @Override @@ -76,6 +94,7 @@ public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, public void onOpen(Session session) throws IOException { LOGGER.info("OnOpen called"); verifyRunningThread(session, LOGGER); + verifyQueryParams(session, LOGGER); if (!modifyHandshakeCalled.get()) { session.close(); // unexpected } @@ -85,6 +104,7 @@ public void onOpen(Session session) throws IOException { public void echo(Session session, String message) throws Exception { LOGGER.info("Endpoint OnMessage called '" + message + "'"); verifyRunningThread(session, LOGGER); + verifyQueryParams(session, LOGGER); if (!isDecoded(message)) { throw new InternalError("Message has not been decoded"); } @@ -101,6 +121,7 @@ public void onError(Throwable t) { public void onClose(Session session) throws IOException { LOGGER.info("OnClose called"); verifyRunningThread(session, LOGGER); + verifyQueryParams(session, LOGGER); modifyHandshakeCalled.set(false); } } diff --git a/webserver/tyrus/src/test/java/io/helidon/webserver/tyrus/EchoServiceTest.java b/webserver/tyrus/src/test/java/io/helidon/webserver/tyrus/EchoServiceTest.java index 1a8f3b4c465..adbf71db838 100644 --- a/webserver/tyrus/src/test/java/io/helidon/webserver/tyrus/EchoServiceTest.java +++ b/webserver/tyrus/src/test/java/io/helidon/webserver/tyrus/EchoServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ public static void startServer() throws Exception { @Test public void testEchoSingle() { try { - URI uri = URI.create("ws://localhost:" + webServer().port() + "/tyrus/echo"); + URI uri = URI.create("ws://localhost:" + webServer().port() + "/tyrus/echo?user=Helidon"); new EchoClient(uri).echo("One"); } catch (Exception e) { fail("Unexpected exception " + e); @@ -46,7 +46,7 @@ public void testEchoSingle() { @Test public void testEchoMultiple() { try { - URI uri = URI.create("ws://localhost:" + webServer().port() + "/tyrus/echo"); + URI uri = URI.create("ws://localhost:" + webServer().port() + "/tyrus/echo?user=Helidon"); new EchoClient(uri).echo("One", "Two", "Three"); } catch (Exception e) { fail("Unexpected exception " + e); diff --git a/webserver/tyrus/src/test/java/io/helidon/webserver/tyrus/HttpClientTest.java b/webserver/tyrus/src/test/java/io/helidon/webserver/tyrus/HttpClientTest.java index 0a0c797a67a..366365ce3ca 100644 --- a/webserver/tyrus/src/test/java/io/helidon/webserver/tyrus/HttpClientTest.java +++ b/webserver/tyrus/src/test/java/io/helidon/webserver/tyrus/HttpClientTest.java @@ -43,7 +43,7 @@ static void startServer() throws ExecutionException, InterruptedException, Timeo @Test void testJdkClient() throws ExecutionException, InterruptedException, TimeoutException { - URI uri = URI.create("ws://localhost:" + webServer().port() + "/tyrus/echo"); + URI uri = URI.create("ws://localhost:" + webServer().port() + "/tyrus/echo?user=Helidon"); ClientListener listener = new ClientListener(); WebSocket webSocket = HttpClient.newHttpClient() diff --git a/webserver/tyrus/src/test/java/io/helidon/webserver/tyrus/RoutingTest.java b/webserver/tyrus/src/test/java/io/helidon/webserver/tyrus/RoutingTest.java index 3b8e8d56bd4..8d37851750c 100644 --- a/webserver/tyrus/src/test/java/io/helidon/webserver/tyrus/RoutingTest.java +++ b/webserver/tyrus/src/test/java/io/helidon/webserver/tyrus/RoutingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ public static void startServer() throws Exception { @Test public void testEcho() { try { - URI uri = URI.create("ws://localhost:" + webServer().port() + "/tyrus/echo"); + URI uri = URI.create("ws://localhost:" + webServer().port() + "/tyrus/echo?user=Helidon"); new EchoClient(uri).echo("One"); } catch (Exception e) { fail("Unexpected exception " + e); @@ -46,7 +46,7 @@ public void testEcho() { @Test public void testDoubleEcho() { try { - URI uri = URI.create("ws://localhost:" + webServer().port() + "/tyrus/doubleEcho"); + URI uri = URI.create("ws://localhost:" + webServer().port() + "/tyrus/doubleEcho?user=Helidon"); new EchoClient(uri, (s1, s2) -> s2.equals(s1 + s1)).echo("One"); } catch (Exception e) { fail("Unexpected exception " + e); diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/Request.java b/webserver/webserver/src/main/java/io/helidon/webserver/Request.java index 49720984183..8bdaa3a9bcc 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/Request.java +++ b/webserver/webserver/src/main/java/io/helidon/webserver/Request.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021 Oracle and/or its affiliates. + * Copyright (c) 2017, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,6 +33,7 @@ import io.helidon.common.http.Http; import io.helidon.common.http.MediaType; import io.helidon.common.http.Parameters; +import io.helidon.common.http.UriComponent; import io.helidon.common.reactive.Single; import io.helidon.media.common.MessageBodyContext; import io.helidon.media.common.MessageBodyReadableContent;