| // |
| // ======================================================================== |
| // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| package org.eclipse.jetty.fcgi.server.proxy; |
| |
| import java.io.IOException; |
| import java.util.Random; |
| import java.util.concurrent.TimeUnit; |
| |
| import javax.servlet.ServletException; |
| import javax.servlet.http.HttpServlet; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.eclipse.jetty.client.HttpClient; |
| import org.eclipse.jetty.client.api.ContentResponse; |
| import org.eclipse.jetty.client.api.Request; |
| import org.eclipse.jetty.client.util.FutureResponseListener; |
| import org.eclipse.jetty.fcgi.FCGI; |
| import org.eclipse.jetty.fcgi.server.ServerFCGIConnectionFactory; |
| import org.eclipse.jetty.http.HttpStatus; |
| import org.eclipse.jetty.server.HttpConfiguration; |
| import org.eclipse.jetty.server.Server; |
| import org.eclipse.jetty.server.ServerConnector; |
| import org.eclipse.jetty.server.handler.HandlerWrapper; |
| import org.eclipse.jetty.servlet.ServletContextHandler; |
| import org.eclipse.jetty.servlet.ServletHolder; |
| import org.eclipse.jetty.util.thread.QueuedThreadPool; |
| import org.hamcrest.Matchers; |
| import org.junit.After; |
| import org.junit.Assert; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| |
| @RunWith(Parameterized.class) |
| public class FastCGIProxyServletTest |
| { |
| @Parameterized.Parameters |
| public static Object[] parameters() |
| { |
| return new Object[]{true, false}; |
| } |
| |
| private final boolean sendStatus200; |
| private Server server; |
| private ServerConnector httpConnector; |
| private ServerConnector fcgiConnector; |
| private ServletContextHandler context; |
| private HttpClient client; |
| |
| public FastCGIProxyServletTest(boolean sendStatus200) |
| { |
| this.sendStatus200 = sendStatus200; |
| } |
| |
| public void prepare(HttpServlet servlet) throws Exception |
| { |
| QueuedThreadPool serverThreads = new QueuedThreadPool(); |
| serverThreads.setName("server"); |
| server = new Server(serverThreads); |
| httpConnector = new ServerConnector(server); |
| server.addConnector(httpConnector); |
| |
| fcgiConnector = new ServerConnector(server, new ServerFCGIConnectionFactory(new HttpConfiguration(), sendStatus200)); |
| server.addConnector(fcgiConnector); |
| |
| final String contextPath = "/"; |
| context = new ServletContextHandler(server, contextPath); |
| |
| final String servletPath = "/script"; |
| FastCGIProxyServlet fcgiServlet = new FastCGIProxyServlet() |
| { |
| @Override |
| protected String rewriteTarget(HttpServletRequest request) |
| { |
| return "http://localhost:" + fcgiConnector.getLocalPort() + servletPath + request.getServletPath(); |
| } |
| }; |
| ServletHolder fcgiServletHolder = new ServletHolder(fcgiServlet); |
| fcgiServletHolder.setName("fcgi"); |
| fcgiServletHolder.setInitParameter(FastCGIProxyServlet.SCRIPT_ROOT_INIT_PARAM, "/scriptRoot"); |
| fcgiServletHolder.setInitParameter("proxyTo", "http://localhost"); |
| fcgiServletHolder.setInitParameter(FastCGIProxyServlet.SCRIPT_PATTERN_INIT_PARAM, "(.+?\\.php)"); |
| context.addServlet(fcgiServletHolder, "*.php"); |
| |
| context.addServlet(new ServletHolder(servlet), servletPath + "/*"); |
| |
| QueuedThreadPool clientThreads = new QueuedThreadPool(); |
| clientThreads.setName("client"); |
| client = new HttpClient(); |
| client.setExecutor(clientThreads); |
| server.addBean(client); |
| |
| server.start(); |
| } |
| |
| @After |
| public void dispose() throws Exception |
| { |
| server.stop(); |
| } |
| |
| @Test |
| public void testGETWithSmallResponseContent() throws Exception |
| { |
| testGETWithResponseContent(1024, 0); |
| } |
| |
| @Test |
| public void testGETWithLargeResponseContent() throws Exception |
| { |
| testGETWithResponseContent(16 * 1024 * 1024, 0); |
| } |
| |
| @Test |
| public void testGETWithLargeResponseContentWithSlowClient() throws Exception |
| { |
| testGETWithResponseContent(16 * 1024 * 1024, 1); |
| } |
| |
| private void testGETWithResponseContent(int length, final long delay) throws Exception |
| { |
| final byte[] data = new byte[length]; |
| new Random().nextBytes(data); |
| |
| final String path = "/foo/index.php"; |
| prepare(new HttpServlet() |
| { |
| @Override |
| protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException |
| { |
| Assert.assertTrue(request.getRequestURI().endsWith(path)); |
| response.setContentLength(data.length); |
| response.getOutputStream().write(data); |
| } |
| }); |
| |
| Request request = client.newRequest("localhost", httpConnector.getLocalPort()) |
| .onResponseContentAsync((response, content, callback) -> |
| { |
| try |
| { |
| if (delay > 0) |
| TimeUnit.MILLISECONDS.sleep(delay); |
| callback.succeeded(); |
| } |
| catch (InterruptedException x) |
| { |
| callback.failed(x); |
| } |
| }) |
| .path(path); |
| FutureResponseListener listener = new FutureResponseListener(request, length); |
| request.send(listener); |
| |
| ContentResponse response = listener.get(30, TimeUnit.SECONDS); |
| |
| Assert.assertEquals(200, response.getStatus()); |
| Assert.assertArrayEquals(data, response.getContent()); |
| } |
| |
| @Test |
| public void testURIRewrite() throws Exception |
| { |
| String originalPath = "/original/index.php"; |
| String originalQuery = "foo=bar"; |
| String remotePath = "/remote/index.php"; |
| prepare(new HttpServlet() |
| { |
| @Override |
| protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException |
| { |
| Assert.assertThat((String)request.getAttribute(FCGI.Headers.REQUEST_URI), Matchers.startsWith(originalPath)); |
| Assert.assertEquals(originalQuery, request.getAttribute(FCGI.Headers.QUERY_STRING)); |
| Assert.assertThat(request.getRequestURI(), Matchers.endsWith(remotePath)); |
| } |
| }); |
| context.stop(); |
| String pathAttribute = "_path_attribute"; |
| String queryAttribute = "_query_attribute"; |
| ServletHolder fcgi = context.getServletHandler().getServlet("fcgi"); |
| fcgi.setInitParameter(FastCGIProxyServlet.ORIGINAL_URI_ATTRIBUTE_INIT_PARAM, pathAttribute); |
| fcgi.setInitParameter(FastCGIProxyServlet.ORIGINAL_QUERY_ATTRIBUTE_INIT_PARAM, queryAttribute); |
| context.insertHandler(new HandlerWrapper() |
| { |
| @Override |
| public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException |
| { |
| if (target.startsWith("/remote/")) |
| { |
| request.setAttribute(pathAttribute, originalPath); |
| request.setAttribute(queryAttribute, originalQuery); |
| } |
| super.handle(target, baseRequest, request, response); |
| } |
| }); |
| context.start(); |
| |
| ContentResponse response = client.newRequest("localhost", httpConnector.getLocalPort()) |
| .path(remotePath) |
| .send(); |
| |
| Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); |
| } |
| } |