From 5ad705fb86693223d8e06dd55aba1b11c0ee0a2d Mon Sep 17 00:00:00 2001 From: jansupol Date: Wed, 13 Dec 2023 14:11:54 +0100 Subject: [PATCH] Better Support deployment handshake statuses != 101 Introduce TyrusClientEndpointConfigurator class making UpgradeRequest available before the handshake, not only after it Signed-off-by: jansupol --- .../glassfish/tyrus/client/ClientManager.java | 72 ++------ .../tyrus/client/TyrusClientEngine.java | 18 +- .../DeploymentHandshakeException.java | 44 +++++ .../tyrus/client/exception/Exceptions.java | 43 +++++ .../tyrus/client/exception/package-info.java | 20 +++ .../grizzly/client/GrizzlyClientSocket.java | 13 +- .../jdk/client/JdkClientContainer.java | 7 +- .../tyrus/core/collection/SupplierWithEx.java | 22 +++ .../spi/TyrusClientEndpointConfigurator.java | 42 +++++ .../test/standard_config/HandshakeTest.java | 159 ++++++++++++++++++ 10 files changed, 369 insertions(+), 71 deletions(-) create mode 100644 client/src/main/java/org/glassfish/tyrus/client/exception/DeploymentHandshakeException.java create mode 100644 client/src/main/java/org/glassfish/tyrus/client/exception/Exceptions.java create mode 100644 client/src/main/java/org/glassfish/tyrus/client/exception/package-info.java create mode 100644 core/src/main/java/org/glassfish/tyrus/core/collection/SupplierWithEx.java create mode 100644 spi/src/main/java/org/glassfish/tyrus/spi/TyrusClientEndpointConfigurator.java diff --git a/client/src/main/java/org/glassfish/tyrus/client/ClientManager.java b/client/src/main/java/org/glassfish/tyrus/client/ClientManager.java index 4201a0db..6469f742 100755 --- a/client/src/main/java/org/glassfish/tyrus/client/ClientManager.java +++ b/client/src/main/java/org/glassfish/tyrus/client/ClientManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023 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 @@ -42,6 +42,7 @@ import javax.websocket.Session; import javax.websocket.WebSocketContainer; +import org.glassfish.tyrus.client.exception.Exceptions; import org.glassfish.tyrus.core.AnnotatedEndpoint; import org.glassfish.tyrus.core.BaseContainer; import org.glassfish.tyrus.core.ComponentProviderService; @@ -52,6 +53,7 @@ import org.glassfish.tyrus.core.TyrusFuture; import org.glassfish.tyrus.core.TyrusSession; import org.glassfish.tyrus.core.Utils; +import org.glassfish.tyrus.core.collection.SupplierWithEx; import org.glassfish.tyrus.core.monitoring.EndpointEventListener; import org.glassfish.tyrus.spi.ClientContainer; import org.glassfish.tyrus.spi.ClientEngine; @@ -295,74 +297,38 @@ public Session connectToServer(Class annotatedEndpointClass, URI path) throws De "Class argument in connectToServer(Class, URI) is to be annotated endpoint class. Class " + "%s does not have @ClientEndpoint", annotatedEndpointClass.getName())); } - try { - return connectToServer(annotatedEndpointClass, null, path.toString(), true).get(); - } catch (InterruptedException e) { - throw new DeploymentException(e.getMessage(), e); - } catch (ExecutionException e) { - final Throwable cause = e.getCause(); - if (cause instanceof DeploymentException) { - throw (DeploymentException) cause; - } else if (cause instanceof IOException) { - throw (IOException) cause; - } else { - throw new DeploymentException(cause.getMessage(), cause); - } - } + return tryCatchInterruptedExecutionEx(() -> connectToServer(annotatedEndpointClass, null, path.toString(), true)); } @Override public Session connectToServer(Class endpointClass, ClientEndpointConfig cec, URI path) throws DeploymentException, IOException { - try { - return connectToServer(endpointClass, cec, path.toString(), true).get(); - } catch (InterruptedException e) { - throw new DeploymentException(e.getMessage(), e); - } catch (ExecutionException e) { - final Throwable cause = e.getCause(); - if (cause instanceof DeploymentException) { - throw (DeploymentException) cause; - } else if (cause instanceof IOException) { - throw (IOException) cause; - } else { - throw new DeploymentException(cause.getMessage(), cause); - } - } + return tryCatchInterruptedExecutionEx(() -> connectToServer(endpointClass, cec, path.toString(), true)); } @Override public Session connectToServer(Endpoint endpointInstance, ClientEndpointConfig cec, URI path) throws DeploymentException, IOException { - try { - return connectToServer(endpointInstance, cec, path.toString(), true).get(); - } catch (InterruptedException e) { - throw new DeploymentException(e.getMessage(), e); - } catch (ExecutionException e) { - final Throwable cause = e.getCause(); - if (cause instanceof DeploymentException) { - throw (DeploymentException) cause; - } else if (cause instanceof IOException) { - throw (IOException) cause; - } else { - throw new DeploymentException(cause.getMessage(), cause); - } - } + return tryCatchInterruptedExecutionEx(() -> connectToServer(endpointInstance, cec, path.toString(), true)); } @Override public Session connectToServer(Object obj, URI path) throws DeploymentException, IOException { + return tryCatchInterruptedExecutionEx(() -> connectToServer(obj, null, path.toString(), true)); + } + + private Session tryCatchInterruptedExecutionEx(SupplierWithEx, DeploymentException> supplier) + throws DeploymentException, IOException { try { - return connectToServer(obj, null, path.toString(), true).get(); + return supplier.get().get(); } catch (InterruptedException e) { throw new DeploymentException(e.getMessage(), e); } catch (ExecutionException e) { final Throwable cause = e.getCause(); - if (cause instanceof DeploymentException) { - throw (DeploymentException) cause; - } else if (cause instanceof IOException) { + if (cause instanceof IOException) { throw (IOException) cause; } else { - throw new DeploymentException(cause.getMessage(), cause); + throw Exceptions.deploymentException(cause.getMessage(), cause); } } } @@ -652,11 +618,7 @@ public void onClose(TyrusSession session, if (countedDown) { final Throwable exception = listener.getThrowable(); if (exception != null) { - if (exception instanceof DeploymentException) { - throw (DeploymentException) exception; - } else { - throw new DeploymentException("Handshake error.", exception); - } + throw Exceptions.deploymentException("Handshake error.", exception); } future.setResult(listener.getSession()); @@ -669,10 +631,8 @@ public void onClose(TyrusSession session, timeoutHandler.handleTimeout(); } } - } catch (DeploymentException e) { - throw e; } catch (Exception e) { - throw new DeploymentException("Handshake response not received.", e); + throw Exceptions.deploymentException("Handshake response not received.", e); } throw new DeploymentException("Handshake response not received."); diff --git a/client/src/main/java/org/glassfish/tyrus/client/TyrusClientEngine.java b/client/src/main/java/org/glassfish/tyrus/client/TyrusClientEngine.java index 1625a535..3312cecc 100644 --- a/client/src/main/java/org/glassfish/tyrus/client/TyrusClientEngine.java +++ b/client/src/main/java/org/glassfish/tyrus/client/TyrusClientEngine.java @@ -64,6 +64,7 @@ import org.glassfish.tyrus.spi.ClientEngine; import org.glassfish.tyrus.spi.Connection; import org.glassfish.tyrus.spi.ReadHandler; +import org.glassfish.tyrus.spi.TyrusClientEndpointConfigurator; import org.glassfish.tyrus.spi.UpgradeRequest; import org.glassfish.tyrus.spi.UpgradeResponse; import org.glassfish.tyrus.spi.Writer; @@ -168,6 +169,9 @@ public UpgradeRequest createUpgradeRequest(TimeoutHandler timeoutHandler) { clientHandShake.prepareRequest(); UpgradeRequest upgradeRequest = clientHandShake.getRequest(); + if (TyrusClientEndpointConfigurator.class.isInstance(config.getConfigurator())) { + ((TyrusClientEndpointConfigurator) config.getConfigurator()).beforeRequest(upgradeRequest); + } config.getConfigurator().beforeRequest(upgradeRequest.getHeaders()); clientEngineState = TyrusClientEngineState.UPGRADE_REQUEST_CREATED; @@ -250,6 +254,7 @@ public ClientUpgradeInfo processResponse(final UpgradeResponse upgradeResponse, throw new IllegalArgumentException(LocalizationMessages.ARGUMENT_NOT_NULL("upgradeResponse")); } + final ClientEngine.ClientUpgradeInfo upgradeInfo; switch (upgradeResponse.getStatus()) { case 101: return handleSwitchProtocol(upgradeResponse, writer, closeListener); @@ -261,7 +266,8 @@ public ClientUpgradeInfo processResponse(final UpgradeResponse upgradeResponse, case 308: return handleRedirect(upgradeResponse); case 401: - return handleAuth(upgradeResponse); + upgradeInfo = handleAuth(upgradeResponse); + break; case 503: // get Retry-After header @@ -292,19 +298,21 @@ public ClientUpgradeInfo processResponse(final UpgradeResponse upgradeResponse, listener.onError(new RetryAfterException( LocalizationMessages.HANDSHAKE_HTTP_RETRY_AFTER_MESSAGE(), delay)); - return UPGRADE_INFO_FAILED; + upgradeInfo = UPGRADE_INFO_FAILED; + break; default: - ((ClientEndpointConfig) endpointWrapper.getEndpointConfig()).getConfigurator().afterResponse(upgradeResponse); - clientEngineState = TyrusClientEngineState.FAILED; HandshakeException e = new HandshakeException( upgradeResponse.getStatus(), LocalizationMessages.INVALID_RESPONSE_CODE(101, upgradeResponse.getStatus())); listener.onError(e); redirectUriHistory.clear(); - return UPGRADE_INFO_FAILED; + upgradeInfo = UPGRADE_INFO_FAILED; + break; } + ((ClientEndpointConfig) endpointWrapper.getEndpointConfig()).getConfigurator().afterResponse(upgradeResponse); + return upgradeInfo; } redirectUriHistory.clear(); diff --git a/client/src/main/java/org/glassfish/tyrus/client/exception/DeploymentHandshakeException.java b/client/src/main/java/org/glassfish/tyrus/client/exception/DeploymentHandshakeException.java new file mode 100644 index 00000000..d684f968 --- /dev/null +++ b/client/src/main/java/org/glassfish/tyrus/client/exception/DeploymentHandshakeException.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 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.client.exception; + +import org.glassfish.tyrus.core.HandshakeException; + +import javax.websocket.DeploymentException; + +/** + * The {@link DeploymentException} wrapping the {@link HandshakeException} and makes the HTTP status code accessible directly. + */ +public class DeploymentHandshakeException extends DeploymentException { + + public DeploymentHandshakeException(String message) { + super(message); + } + + public DeploymentHandshakeException(String message, HandshakeException cause) { + super(message, cause); + } + + /** + * Get the error code. + * + * @return the error code. + */ + public int getHttpStatusCode() { + return ((HandshakeException) getCause()).getHttpStatusCode(); + } +} diff --git a/client/src/main/java/org/glassfish/tyrus/client/exception/Exceptions.java b/client/src/main/java/org/glassfish/tyrus/client/exception/Exceptions.java new file mode 100644 index 00000000..9c50d491 --- /dev/null +++ b/client/src/main/java/org/glassfish/tyrus/client/exception/Exceptions.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 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.client.exception; + +import org.glassfish.tyrus.core.HandshakeException; + +import javax.websocket.DeploymentException; + +/** + * Converts the exceptions into more specific ones. + */ +public class Exceptions { + + /** + * Get the Deployment Exception, or return the exception if of the type. + * @param message The Exception message + * @param cause The Cause Exception + * @return a Deployment exception. + */ + public static DeploymentException deploymentException(String message, Throwable cause) { + if (DeploymentException.class.isInstance(cause)) { + return (DeploymentException) cause; + } else if (HandshakeException.class.isInstance(cause)) { + return new DeploymentHandshakeException(message, (HandshakeException) cause); + } else { + return new DeploymentException(message, cause); + } + } +} diff --git a/client/src/main/java/org/glassfish/tyrus/client/exception/package-info.java b/client/src/main/java/org/glassfish/tyrus/client/exception/package-info.java new file mode 100644 index 00000000..5f27d2d0 --- /dev/null +++ b/client/src/main/java/org/glassfish/tyrus/client/exception/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 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 + */ + +/** + * Common Client Exceptions + */ +package org.glassfish.tyrus.client.exception; diff --git a/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientSocket.java b/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientSocket.java index d615d118..25031a63 100644 --- a/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientSocket.java +++ b/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientSocket.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023 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 @@ -48,6 +48,7 @@ import org.glassfish.tyrus.client.ClientManager; import org.glassfish.tyrus.client.ClientProperties; import org.glassfish.tyrus.client.SslEngineConfigurator; +import org.glassfish.tyrus.client.exception.Exceptions; import org.glassfish.tyrus.core.TyrusFuture; import org.glassfish.tyrus.core.Utils; import org.glassfish.tyrus.spi.ClientEngine; @@ -202,7 +203,7 @@ public class GrizzlyClientSocket { (sharedTransport && sharedTransportTimeoutProperty != null) ? sharedTransportTimeoutProperty : 30; this.clientEngine = clientEngine; } catch (RuntimeException e) { - throw new DeploymentException(e.getMessage(), e); + throw Exceptions.deploymentException(e.getMessage(), e); } grizzlyConnector = new Callable() { @@ -224,12 +225,10 @@ public Void call() throws Exception { public void connect() throws DeploymentException, IOException { try { grizzlyConnector.call(); - } catch (DeploymentException e) { - throw e; } catch (IOException e) { throw e; } catch (Exception e) { - throw new DeploymentException(e.getMessage(), e); + throw Exceptions.deploymentException(e.getMessage(), e); } } @@ -380,7 +379,7 @@ public void handleTimeout() { throw new DeploymentException("SSL handshake has failed", e.getCause()); } catch (Exception e) { closeTransport(privateTransport); - throw new DeploymentException(String.format("Connection to '%s' failed.", requestURI), + throw Exceptions.deploymentException(String.format("Connection to '%s' failed.", requestURI), e.getCause()); } } @@ -410,7 +409,7 @@ public void handleTimeout() { } } - throw new DeploymentException("Connection failed.", exception); + throw Exceptions.deploymentException("Connection failed.", exception); } private static TCPNIOTransport createTransport(ThreadPoolConfig workerThreadPoolConfig, diff --git a/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/JdkClientContainer.java b/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/JdkClientContainer.java index a784ad95..717bbe8b 100644 --- a/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/JdkClientContainer.java +++ b/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/JdkClientContainer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023 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 @@ -43,6 +43,7 @@ import org.glassfish.tyrus.client.SslContextConfigurator; import org.glassfish.tyrus.client.SslEngineConfigurator; import org.glassfish.tyrus.client.ThreadPoolConfig; +import org.glassfish.tyrus.client.exception.Exceptions; import org.glassfish.tyrus.core.ReflectionHelper; import org.glassfish.tyrus.core.Utils; import org.glassfish.tyrus.spi.ClientContainer; @@ -184,7 +185,7 @@ public void handleTimeout() { } } - throw new DeploymentException("Connection failed.", exception); + throw Exceptions.deploymentException("Connection failed.", exception); } }; @@ -199,7 +200,7 @@ public void handleTimeout() { throw (IOException) e; } - throw new DeploymentException(e.getMessage(), e); + throw Exceptions.deploymentException(e.getMessage(), e); } } diff --git a/core/src/main/java/org/glassfish/tyrus/core/collection/SupplierWithEx.java b/core/src/main/java/org/glassfish/tyrus/core/collection/SupplierWithEx.java new file mode 100644 index 00000000..2c56fbd4 --- /dev/null +++ b/core/src/main/java/org/glassfish/tyrus/core/collection/SupplierWithEx.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 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.core.collection; + +@FunctionalInterface +public interface SupplierWithEx { + T get() throws EX; +} diff --git a/spi/src/main/java/org/glassfish/tyrus/spi/TyrusClientEndpointConfigurator.java b/spi/src/main/java/org/glassfish/tyrus/spi/TyrusClientEndpointConfigurator.java new file mode 100644 index 00000000..a685d16f --- /dev/null +++ b/spi/src/main/java/org/glassfish/tyrus/spi/TyrusClientEndpointConfigurator.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 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.spi; + +import javax.websocket.ClientEndpointConfig; +import java.util.Map; + +/** + * Extended Configurator that can be used for subclassing the user provided configurator. + * If done so, the additional methods are invoked as described by the methods. + */ +public class TyrusClientEndpointConfigurator extends ClientEndpointConfig.Configurator { + + /** + * This method is called by the implementation after it has formulated the handshake request that will be used + * to initiate the connection to the server, but before {@link #beforeRequest(Map)} is invoked. This allows the + * developer to inspect the handshake request itself prior to the start of the handshake interaction. + *

+ * For modifying the HandshakeRequestHeaders, use {@link #beforeRequest(Map)}. + *

+ * + * @param upgradeRequest the read-only handshake request the implementation is about to send to start the + * handshake interaction. + */ + public void beforeRequest(UpgradeRequest upgradeRequest) { + + } +} diff --git a/tests/e2e/standard-config/src/test/java/org/glassfish/tyrus/test/standard_config/HandshakeTest.java b/tests/e2e/standard-config/src/test/java/org/glassfish/tyrus/test/standard_config/HandshakeTest.java index f7f4bc33..30c49cc5 100755 --- a/tests/e2e/standard-config/src/test/java/org/glassfish/tyrus/test/standard_config/HandshakeTest.java +++ b/tests/e2e/standard-config/src/test/java/org/glassfish/tyrus/test/standard_config/HandshakeTest.java @@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import javax.websocket.ClientEndpoint; import javax.websocket.ClientEndpointConfig; import javax.websocket.DeploymentException; import javax.websocket.EndpointConfig; @@ -36,7 +37,10 @@ import javax.websocket.server.ServerEndpointConfig; import org.glassfish.tyrus.client.ClientManager; +import org.glassfish.tyrus.client.exception.DeploymentHandshakeException; import org.glassfish.tyrus.server.Server; +import org.glassfish.tyrus.spi.TyrusClientEndpointConfigurator; +import org.glassfish.tyrus.spi.UpgradeRequest; import org.glassfish.tyrus.spi.UpgradeResponse; import org.glassfish.tyrus.test.standard_config.bean.TestEndpoint; import org.glassfish.tyrus.test.tools.TestContainer; @@ -192,4 +196,159 @@ public EndpointConfig getEndpointConfig() { server.stop(); } } + + public static class Status401SetterConfiguration extends ServerEndpointConfig.Configurator { + @Override + public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) { + ((UpgradeResponse) response).setStatus(401); + response.getHeaders().put(HEADER, Collections.singletonList(HEADER)); + } + } + + @Test + public void test401InConfigurer() throws DeploymentException, IOException { + @ServerEndpoint(value = "/status", configurator = Status401SetterConfiguration.class) + class Status401SetterEndpoint { + @OnMessage + public void onMessage(String message) { + throw new IllegalStateException("ON MESSAGE"); + } + } + + final AtomicReference status = new AtomicReference<>(); + final AtomicReference header = new AtomicReference<>(); + + Server server = startServer(Status401SetterEndpoint.class); + + ClientEndpointConfig.Configurator cecc = new ClientEndpointConfig.Configurator() { + @Override + public void afterResponse(HandshakeResponse hr) { + status.set(((UpgradeResponse) hr).getStatus()); + header.set(((UpgradeResponse) hr).getFirstHeaderValue(HEADER)); + } + }; + + ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().configurator(cecc).build(); + + try { + ClientManager client = createClient(); + Session session = client.connectToServer(new TestEndpointAdapter() { + @Override + public void onMessage(String message) { + } + + @Override + public void onOpen(Session session) { + try { + session.getBasicRemote().sendText("This should never be sent"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public EndpointConfig getEndpointConfig() { + return cec; + } + }, cec, getURI(Status401SetterEndpoint.class)); + + throw new IllegalStateException("DeploymentException was not thrown"); + } catch (DeploymentHandshakeException de) { + Assert.assertEquals(401, status.get().intValue()); + Assert.assertEquals(status.get().intValue(), de.getHttpStatusCode()); + Assert.assertEquals(HEADER, header.get()); + } finally { + server.stop(); + } + } + + @Test + public void beforeRequestUpgradeRequest() throws DeploymentException, IOException { + @ServerEndpoint(value = "/status", configurator = StatusSetterConfiguration.class) + class StatusSetterEndpoint { + @OnMessage + public void onMessage(String message) { + throw new IllegalStateException("ON MESSAGE"); + } + } + + final AtomicReference requestAtomicReference = new AtomicReference<>(); + + Server server = startServer(StatusSetterEndpoint.class); + + ClientEndpointConfig.Configurator cecc = new TyrusClientEndpointConfigurator() { + @Override + public void beforeRequest(UpgradeRequest upgradeRequest) { + requestAtomicReference.set(upgradeRequest); + } + }; + + ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().configurator(cecc).build(); + + try { + ClientManager client = createClient(); + Session session = client.connectToServer(new TestEndpointAdapter() { + @Override + public void onMessage(String message) { + } + + @Override + public void onOpen(Session session) { + try { + session.getBasicRemote().sendText("This should never be sent"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public EndpointConfig getEndpointConfig() { + return cec; + } + }, cec, getURI(StatusSetterEndpoint.class)); + + throw new IllegalStateException("DeploymentException was not thrown"); + } catch (DeploymentException de) { + Assert.assertEquals(getURI(StatusSetterEndpoint.class), requestAtomicReference.get().getRequestURI()); + } finally { + server.stop(); + } + } + + public static final AtomicReference REQUEST_ATOMIC_REFERENCE = new AtomicReference<>(); + + public static class AnnotatedBeforeRequestTestClientConfigurator extends TyrusClientEndpointConfigurator { + @Override + public void beforeRequest(UpgradeRequest upgradeRequest) { + REQUEST_ATOMIC_REFERENCE.set(upgradeRequest); + } + } + + @ClientEndpoint(configurator = AnnotatedBeforeRequestTestClientConfigurator.class) + public static class AnnotatedBeforeRequestTestClient { + + } + + @Test + public void beforeRequestUpgradeRequestOnAnnotatedClient() throws DeploymentException, IOException { + @ServerEndpoint(value = "/status", configurator = StatusSetterConfiguration.class) + class StatusSetterEndpoint { + @OnMessage + public void onMessage(String message) { + throw new IllegalStateException("ON MESSAGE"); + } + } + + Server server = startServer(StatusSetterEndpoint.class); + + try { + ClientManager client = createClient(); + Session session = client.connectToServer(AnnotatedBeforeRequestTestClient.class, getURI(StatusSetterEndpoint.class)); + throw new IllegalStateException("DeploymentException was not thrown"); + } catch (DeploymentException de) { + Assert.assertEquals(getURI(StatusSetterEndpoint.class), REQUEST_ATOMIC_REFERENCE.get().getRequestURI()); + } finally { + server.stop(); + } + } }