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

[UNDERTOW-2347] [UNDERTOW-1926] [UNDERTOW-2351] Backport fixes to 2.2.x branch #1582

Merged
merged 3 commits into from
Apr 19, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 19 additions & 7 deletions core/src/main/java/io/undertow/client/http/HttpClientProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import org.xnio.ssl.SslConnection;
import org.xnio.ssl.XnioSsl;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.security.AccessController;
Expand All @@ -52,6 +51,8 @@
*/
public class HttpClientProvider implements ClientProvider {

private static final String HTTP_1_1 = "http/1.1";

public static final String DISABLE_HTTPS_ENDPOINT_IDENTIFICATION_PROPERTY = "io.undertow.client.https.disableEndpointIdentification";
public static final boolean DISABLE_HTTPS_ENDPOINT_IDENTIFICATION;

Expand Down Expand Up @@ -154,6 +155,14 @@ public void handleEvent(StreamConnection connection) {
};
}

public static ALPNClientSelector.ALPNProtocol alpnProtocol(final ClientCallback<ClientConnection> listener, URI uri, ByteBufferPool bufferPool, OptionMap options) {
return new ALPNClientSelector.ALPNProtocol(new ChannelListener<SslConnection>() {
@Override
public void handleEvent(SslConnection connection) {
listener.completed(new HttpClientConnection(connection, options, bufferPool));
}
}, HTTP_1_1);
}

private void handleConnected(final StreamConnection connection, final ClientCallback<ClientConnection> listener, final ByteBufferPool bufferPool, final OptionMap options, URI uri) {

Expand All @@ -163,7 +172,7 @@ private void handleConnected(final StreamConnection connection, final ClientCall
if(h2) {
protocolList.add(Http2ClientProvider.alpnProtocol(listener, uri, bufferPool, options));
}

protocolList.add(alpnProtocol(listener, uri, bufferPool, options));
ALPNClientSelector.runAlpn((SslConnection) connection, new ChannelListener<SslConnection>() {
@Override
public void handleEvent(SslConnection connection) {
Expand All @@ -172,11 +181,14 @@ public void handleEvent(SslConnection connection) {
}, listener, protocolList.toArray(new ALPNClientSelector.ALPNProtocol[protocolList.size()]));
} else {
if(connection instanceof SslConnection) {
try {
((SslConnection) connection).startHandshake();
} catch (Throwable t) {
listener.failed((t instanceof IOException) ? (IOException) t : new IOException(t));
}
List<ALPNClientSelector.ALPNProtocol> protocolList = new ArrayList<>();
protocolList.add(alpnProtocol(listener, uri, bufferPool, options));
ALPNClientSelector.runAlpn((SslConnection) connection, new ChannelListener<SslConnection>() {
@Override
public void handleEvent(SslConnection connection) {
listener.completed(new HttpClientConnection(connection, options, bufferPool));
}
}, listener, protocolList.toArray(new ALPNClientSelector.ALPNProtocol[protocolList.size()]));
}
listener.completed(new HttpClientConnection(connection, options, bufferPool));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@
public class Http2ClientProvider implements ClientProvider {

private static final String HTTP2 = "h2";
private static final String HTTP_1_1 = "http/1.1";

private static final ChannelListener<SslConnection> FAILED = new ChannelListener<SslConnection>() {
@Override
public void handleEvent(SslConnection connection) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import io.undertow.server.HttpServerExchange;
import io.undertow.testutils.DefaultServer;
import io.undertow.testutils.HttpClientUtils;
import io.undertow.testutils.ProxyIgnore;
import io.undertow.testutils.TestHttpClient;
import io.undertow.util.Headers;
import io.undertow.util.StatusCodes;
Expand Down Expand Up @@ -187,7 +186,6 @@ public void testAsyncSender() throws IOException {
}

@Test
@ProxyIgnore("UNDERTOW-1926 fails with proxy http2 sporadically") // FIXME
public void testAsyncTransfer() throws Exception {
StringBuilder sb = new StringBuilder(TXS);
for (int i = 0; i < TXS; ++i) {
Expand All @@ -213,7 +211,6 @@ public void testAsyncTransfer() throws Exception {
}

@Test
@ProxyIgnore("UNDERTOW-1926 fails with proxy http2 sporadically") // FIXME
public void testSyncTransfer() throws Exception {
StringBuilder sb = new StringBuilder(TXS);
for (int i = 0; i < TXS; ++i) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -612,26 +612,28 @@ public HttpHandler call(HttpServerExchange exchange, Object ignore) throws Servl

@Override
public void stop() throws ServletException {
try {
deployment.createThreadSetupAction(new ThreadSetupHandler.Action<Void, Object>() {
@Override
public Void call(HttpServerExchange exchange, Object ignore) throws ServletException {
for (Lifecycle object : deployment.getLifecycleObjects()) {
try {
object.stop();
} catch (Throwable t) {
UndertowServletLogger.ROOT_LOGGER.failedToDestroy(object, t);
if(deployment.getDeploymentState() == State.STARTED) {
try {
deployment.createThreadSetupAction(new ThreadSetupHandler.Action<Void, Object>() {
@Override
public Void call(HttpServerExchange exchange, Object ignore) throws ServletException {
for (Lifecycle object : deployment.getLifecycleObjects()) {
try {
object.stop();
} catch (Throwable t) {
UndertowServletLogger.ROOT_LOGGER.failedToDestroy(object, t);
}
}
deployment.getSessionManager().stop();
state = State.DEPLOYED;
return null;
}
deployment.getSessionManager().stop();
state = State.DEPLOYED;
return null;
}
}).call(null, null);
} catch (ServletException|RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}).call(null, null);
} catch (ServletException|RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

Expand Down Expand Up @@ -669,27 +671,29 @@ public void handleDeploymentSessionConfig(DeploymentInfo deploymentInfo, Servlet

@Override
public void undeploy() {
try {
deployment.createThreadSetupAction(new ThreadSetupHandler.Action<Void, Object>() {
@Override
public Void call(HttpServerExchange exchange, Object ignore) throws ServletException {
for(ServletContextListener listener : deployment.getDeploymentInfo().getDeploymentCompleteListeners()) {
try {
listener.contextDestroyed(new ServletContextEvent(deployment.getServletContext()));
} catch (Throwable t) {
UndertowServletLogger.REQUEST_LOGGER.failedToDestroy(listener, t);
if(deployment.getDeploymentState() == State.DEPLOYED) {
//NOTE: this can happen if deployment isnt full and attempt is made to roll it back.
try {
deployment.createThreadSetupAction(new ThreadSetupHandler.Action<Void, Object>() {
@Override
public Void call(HttpServerExchange exchange, Object ignore) throws ServletException {
for(ServletContextListener listener : deployment.getDeploymentInfo().getDeploymentCompleteListeners()) {
try {
listener.contextDestroyed(new ServletContextEvent(deployment.getServletContext()));
} catch (Throwable t) {
UndertowServletLogger.REQUEST_LOGGER.failedToDestroy(listener, t);
}
}
deployment.destroy();
deployment = null;
state = State.UNDEPLOYED;
return null;
}
deployment.destroy();
deployment = null;
state = State.UNDEPLOYED;
return null;
}
}).call(null, null);
} catch (Exception e) {
throw new RuntimeException(e);
}).call(null, null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2024 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.undertow.websockets.jsr.test.annotated;

import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.DeploymentManager;
import io.undertow.servlet.api.ServletContainer;
import io.undertow.servlet.test.util.TestClassIntrospector;
import io.undertow.servlet.test.util.TestResourceLoader;
import io.undertow.testutils.DefaultServer;
import io.undertow.testutils.HttpOneOnly;
import io.undertow.websockets.jsr.ServerWebSocketContainer;
import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;

import javax.servlet.ServletException;
import javax.websocket.DeploymentException;
import javax.websocket.server.ServerEndpointConfig;

import static org.hamcrest.core.IsInstanceOf.instanceOf;
/**
* @author baranowb
*/
@RunWith(DefaultServer.class)
@HttpOneOnly
public class BadAnnotatedEndpointTestCase {

private static ServerWebSocketContainer deployment;
private static DeploymentManager deploymentManager;

@BeforeClass
public static void setup() throws Exception {

final ServletContainer container = ServletContainer.Factory.newInstance();

DeploymentInfo builder = new DeploymentInfo()
.setClassLoader(BadAnnotatedEndpointTestCase.class.getClassLoader()).setContextPath("/ws")
.setResourceManager(new TestResourceLoader(BadAnnotatedEndpointTestCase.class))
.setClassIntrospecter(TestClassIntrospector.INSTANCE)
.addServletContextAttribute(WebSocketDeploymentInfo.ATTRIBUTE_NAME,
new WebSocketDeploymentInfo()
.setBuffers(DefaultServer.getBufferPool())
.setWorker(DefaultServer.getWorkerSupplier())
.addEndpoint(BadOnMessageEndpoint.class)
.addListener(readyContainer -> deployment = readyContainer)
.addEndpoint(ServerEndpointConfig.Builder.create(
AnnotatedAddedProgrammaticallyEndpoint.class,
AnnotatedAddedProgrammaticallyEndpoint.PATH)
.build())
)
.setDeploymentName("servletContext.war");


deploymentManager = container.addDeployment(builder);

}

@AfterClass
public static void after() throws ServletException {
if (deployment != null) {
deployment.close();
deployment = null;
}
if (deploymentManager != null) {
deploymentManager.stop();
deploymentManager.undeploy();
}
}

@Rule
public final ExpectedException thrown = ExpectedException.none();

@Test
public void testStringOnMessage() throws Exception {
thrown.expect(RuntimeException.class);
thrown.expectMessage("javax.websocket.DeploymentException: UT003012: Method public int io.undertow.websockets.jsr.test.annotated.BadOnMessageEndpoint.handleMessage(int,javax.websocket.EndpointConfig) has invalid parameters at locations [1]");
thrown.expectCause(instanceOf(DeploymentException.class));
deploymentManager.deploy();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2024 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.undertow.websockets.jsr.test.annotated;

import javax.websocket.EndpointConfig;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

/**
* @author baranowb
*/
@ServerEndpoint("/increment/{increment}")
public class BadOnMessageEndpoint {

int increment;

@OnOpen
public void open(final Session session, final EndpointConfig config, @PathParam("increment") int increment) {
this.increment = increment;
}

@OnMessage
public int handleMessage(final int message, EndpointConfig config) {
System.err.println("---> "+config);
return message + increment;
}

}