/* * JBoss, Home of Professional Open Source. * Copyright (c) 2011, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.test.integration.web.reverseproxy; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.OperateOnDeployment; import org.jboss.arquillian.container.test.api.RunAsClient; import org.jboss.arquillian.junit.Arquillian; import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.as.arquillian.api.ContainerResource; import org.jboss.as.arquillian.container.ManagementClient; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.test.integration.management.ManagementOperations; import org.jboss.dmr.ModelNode; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.net.URL; import java.util.HashSet; import java.util.Set; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ALLOW_RESOURCE_SERVICE_RESTART; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OPERATION_HEADERS; import static org.jboss.as.test.integration.management.util.ModelUtil.createOpNode; /** */ @RunWith(Arquillian.class) @RunAsClient public class ReverseProxyTestCase { @ContainerResource private ManagementClient managementClient; private static ManagementClient mc; @Before public void setup() throws Exception { if (mc == null) { mc = managementClient; //add the reverse proxy ModelNode op = new ModelNode(); ModelNode addr = new ModelNode(); addr.add(ModelDescriptionConstants.SUBSYSTEM, "undertow"); addr.add("configuration", "handler"); addr.add("reverse-proxy", "myproxy"); op.get(ModelDescriptionConstants.OP_ADDR).set(addr); op.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD); ManagementOperations.executeOperation(managementClient.getControllerClient(), op); //add the hosts ModelNode addSocketBindingOp = getOutboundSocketBinding(managementClient.getWebUri().getHost(), managementClient.getWebUri().getPort()); ManagementOperations.executeOperation(managementClient.getControllerClient(),addSocketBindingOp); op = new ModelNode(); addr = new ModelNode(); addr.add(ModelDescriptionConstants.SUBSYSTEM, "undertow"); addr.add("configuration", "handler"); addr.add("reverse-proxy", "myproxy"); addr.add("host", "server1"); op.get(ModelDescriptionConstants.OP_ADDR).set(addr); op.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD); op.get("outbound-socket-binding").set("proxy-host"); op.get("path").set("/server1"); ManagementOperations.executeOperation(managementClient.getControllerClient(), op); op = new ModelNode(); addr = new ModelNode(); addr.add(ModelDescriptionConstants.SUBSYSTEM, "undertow"); addr.add("configuration", "handler"); addr.add("reverse-proxy", "myproxy"); addr.add("host", "server2"); op.get(ModelDescriptionConstants.OP_ADDR).set(addr); op.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD); op.get("outbound-socket-binding").set("proxy-host"); op.get("path").set("/server2"); ManagementOperations.executeOperation(managementClient.getControllerClient(), op); op = new ModelNode(); addr = new ModelNode(); addr.add(ModelDescriptionConstants.SUBSYSTEM, "undertow"); addr.add("server", "default-server"); addr.add("host", "default-host"); addr.add("location", "/proxy"); op.get(ModelDescriptionConstants.OP_ADDR).set(addr); op.get("handler").set("myproxy"); op.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD); ManagementOperations.executeOperation(managementClient.getControllerClient(), op); } } private static ModelNode getOutboundSocketBinding(String address, int port) { ModelNode op = createOpNode("socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=proxy-host", "add"); op.get("host").set(address); op.get("port").set(port); return op; } @AfterClass public static void tearDown() throws Exception { ModelNode op = new ModelNode(); ModelNode addr = new ModelNode(); addr.add(ModelDescriptionConstants.SUBSYSTEM, "undertow"); addr.add("configuration", "handler"); addr.add("reverse-proxy", "myproxy"); addr.add("host", "server2"); op.get(ModelDescriptionConstants.OP_ADDR).set(addr); op.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.REMOVE); op.get(OPERATION_HEADERS, ALLOW_RESOURCE_SERVICE_RESTART).set(true); ManagementOperations.executeOperation(mc.getControllerClient(), op); op = new ModelNode(); addr = new ModelNode(); addr.add(ModelDescriptionConstants.SUBSYSTEM, "undertow"); addr.add("configuration", "handler"); addr.add("reverse-proxy", "myproxy"); addr.add("host", "server1"); op.get(ModelDescriptionConstants.OP_ADDR).set(addr); op.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.REMOVE); op.get(OPERATION_HEADERS, ALLOW_RESOURCE_SERVICE_RESTART).set(true); ManagementOperations.executeOperation(mc.getControllerClient(), op); op = new ModelNode(); addr = new ModelNode(); addr.add(ModelDescriptionConstants.SUBSYSTEM, "undertow"); addr.add("server", "default-server"); addr.add("host", "default-host"); addr.add("location", "/proxy"); op.get(ModelDescriptionConstants.OP_ADDR).set(addr); op.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.REMOVE); op.get(OPERATION_HEADERS, ALLOW_RESOURCE_SERVICE_RESTART).set(true); ManagementOperations.executeOperation(mc.getControllerClient(), op); op = new ModelNode(); addr = new ModelNode(); addr.add(ModelDescriptionConstants.SUBSYSTEM, "undertow"); addr.add("configuration", "handler"); addr.add("reverse-proxy", "myproxy"); op.get(ModelDescriptionConstants.OP_ADDR).set(addr); op.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.REMOVE); op.get(OPERATION_HEADERS, ALLOW_RESOURCE_SERVICE_RESTART).set(true); ManagementOperations.executeOperation(mc.getControllerClient(), op); op = createOpNode("socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=proxy-host", "remove"); ManagementOperations.executeOperation(mc.getControllerClient(), op); } @ArquillianResource @OperateOnDeployment("server1") private URL url; @Deployment(name = "server1", testable = false) public static WebArchive server1() { WebArchive war = ShrinkWrap.create(WebArchive.class, "server1.war"); war.addClasses(ServerNameServlet.class, CookieListener.class); war.addAsWebInfResource(ReverseProxyTestCase.class.getPackage(), "web-server1.xml", "web.xml"); return war; } @Deployment(name = "server2", testable = false) public static WebArchive server2() { WebArchive war = ShrinkWrap.create(WebArchive.class, "server2.war"); war.addClasses(ServerNameServlet.class, CookieListener.class); war.addAsWebInfResource(ReverseProxyTestCase.class.getPackage(), "web-server2.xml", "web.xml"); return war; } private String performCall(CloseableHttpClient httpclient, String urlPattern) throws Exception { HttpResponse res = httpclient.execute(new HttpGet("http://" + url.getHost() + ":" + url.getPort() + "/proxy/" + urlPattern)); Assert.assertEquals(200, res.getStatusLine().getStatusCode()); return EntityUtils.toString(res.getEntity()); } @Test public void testReverseProxy() throws Exception { try (CloseableHttpClient httpclient = HttpClients.createDefault()){ final Set<String> results = new HashSet<>(); for (int i = 0; i < 10; ++i) { results.add(performCall(httpclient,"name")); } Assert.assertEquals(2, results.size()); Assert.assertTrue(results.contains("server1")); Assert.assertTrue(results.contains("server2")); //TODO: re-add JVM route based sticky session testing //String session = performCall("name?session=true"); //sticky sessions should stick it to this node //for (int i = 0; i < 10; ++i) { // Assert.assertEquals(session, performCall("name")); //} } } }