/* * Copyright 2010 Proofpoint, Inc. * * 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.airlift.http.server.testing; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.net.HttpHeaders; import com.google.common.net.MediaType; import com.google.inject.Binder; import com.google.inject.Injector; import com.google.inject.Module; import com.google.inject.TypeLiteral; import io.airlift.bootstrap.Bootstrap; import io.airlift.bootstrap.LifeCycleManager; import io.airlift.http.client.HttpClient; import io.airlift.http.client.HttpClientConfig; import io.airlift.http.client.HttpStatus; import io.airlift.http.client.HttpUriBuilder; import io.airlift.http.client.StatusResponseHandler.StatusResponse; import io.airlift.http.client.StringResponseHandler; import io.airlift.http.client.jetty.JettyHttpClient; import io.airlift.http.server.HttpServerConfig; import io.airlift.http.server.HttpServerInfo; import io.airlift.http.server.TheServlet; import io.airlift.log.Logging; import io.airlift.node.NodeInfo; import io.airlift.node.testing.TestingNodeModule; import io.airlift.units.Duration; import org.testng.annotations.BeforeSuite; import org.testng.annotations.Test; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URI; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8; import static com.google.inject.multibindings.Multibinder.newSetBinder; import static io.airlift.http.client.HttpUriBuilder.uriBuilderFrom; import static io.airlift.http.client.Request.Builder.prepareGet; import static io.airlift.http.client.StatusResponseHandler.createStatusResponseHandler; import static io.airlift.http.client.StringResponseHandler.createStringResponseHandler; import static io.airlift.http.server.HttpServerBinder.HttpResourceBinding; import static io.airlift.http.server.HttpServerBinder.httpServerBinder; import static io.airlift.testing.Assertions.assertGreaterThan; import static java.util.concurrent.TimeUnit.SECONDS; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; public class TestTestingHttpServer { @BeforeSuite public void setupSuite() { Logging.initialize(); } @Test public void testInitialization() throws Exception { DummyServlet servlet = new DummyServlet(); Map<String, String> params = ImmutableMap.of("sampleInitParameter", "the value"); TestingHttpServer server = createTestingHttpServer(servlet, params); try { server.start(); assertEquals(servlet.getSampleInitParam(), "the value"); assertGreaterThan(server.getPort(), 0); } finally { server.stop(); } } @Test public void testRequest() throws Exception { DummyServlet servlet = new DummyServlet(); TestingHttpServer server = createTestingHttpServer(servlet, ImmutableMap.<String, String>of()); try { server.start(); try (HttpClient client = new JettyHttpClient(new HttpClientConfig().setConnectTimeout(new Duration(1, SECONDS)))) { StatusResponse response = client.execute(prepareGet().setUri(server.getBaseUrl()).build(), createStatusResponseHandler()); assertEquals(response.getStatusCode(), HttpServletResponse.SC_OK); assertEquals(servlet.getCallCount(), 1); } } finally { server.stop(); } } @Test public void testFilteredRequest() throws Exception { DummyServlet servlet = new DummyServlet(); DummyFilter filter = new DummyFilter(); TestingHttpServer server = createTestingHttpServerWithFilter(servlet, ImmutableMap.<String, String>of(), filter); try { server.start(); try (HttpClient client = new JettyHttpClient(new HttpClientConfig().setConnectTimeout(new Duration(1, SECONDS)))) { StatusResponse response = client.execute(prepareGet().setUri(server.getBaseUrl()).build(), createStatusResponseHandler()); assertEquals(response.getStatusCode(), HttpServletResponse.SC_OK); assertEquals(servlet.getCallCount(), 1); assertEquals(filter.getCallCount(), 1); } } finally { server.stop(); } } @Test public void testGuiceInjectionWithoutFilters() throws Exception { final DummyServlet servlet = new DummyServlet(); Bootstrap app = new Bootstrap( new TestingNodeModule(), new TestingHttpServerModule(), new Module() { @Override public void configure(Binder binder) { binder.bind(Servlet.class).annotatedWith(TheServlet.class).toInstance(servlet); binder.bind(new TypeLiteral<Map<String, String>>() {}).annotatedWith(TheServlet.class).toInstance(ImmutableMap.<String, String>of()); } }); Injector injector = app .strictConfig() .doNotInitializeLogging() .initialize(); LifeCycleManager lifeCycleManager = injector.getInstance(LifeCycleManager.class); TestingHttpServer server = injector.getInstance(TestingHttpServer.class); try (HttpClient client = new JettyHttpClient(new HttpClientConfig().setConnectTimeout(new Duration(1, SECONDS)))) { StatusResponse response = client.execute(prepareGet().setUri(server.getBaseUrl()).build(), createStatusResponseHandler()); assertEquals(response.getStatusCode(), HttpServletResponse.SC_OK); assertEquals(servlet.getCallCount(), 1); } finally { lifeCycleManager.stop(); } } @Test public void testGuiceInjectionWithFilters() throws Exception { final DummyServlet servlet = new DummyServlet(); final DummyFilter filter = new DummyFilter(); Bootstrap app = new Bootstrap( new TestingNodeModule(), new TestingHttpServerModule(), new Module() { @Override public void configure(Binder binder) { binder.bind(Servlet.class).annotatedWith(TheServlet.class).toInstance(servlet); binder.bind(new TypeLiteral<Map<String, String>>() {}).annotatedWith(TheServlet.class).toInstance(ImmutableMap.<String, String>of()); newSetBinder(binder, Filter.class, TheServlet.class).addBinding().toInstance(filter); } }); Injector injector = app .strictConfig() .doNotInitializeLogging() .initialize(); LifeCycleManager lifeCycleManager = injector.getInstance(LifeCycleManager.class); TestingHttpServer server = injector.getInstance(TestingHttpServer.class); try (HttpClient client = new JettyHttpClient(new HttpClientConfig().setConnectTimeout(new Duration(1, SECONDS)))) { StatusResponse response = client.execute(prepareGet().setUri(server.getBaseUrl()).build(), createStatusResponseHandler()); assertEquals(response.getStatusCode(), HttpServletResponse.SC_OK); assertEquals(servlet.getCallCount(), 1); assertEquals(filter.getCallCount(), 1); } finally { lifeCycleManager.stop(); } } @Test public void testGuiceInjectionWithResources() throws Exception { final DummyServlet servlet = new DummyServlet(); Bootstrap app = new Bootstrap( new TestingNodeModule(), new TestingHttpServerModule(), new Module() { @Override public void configure(Binder binder) { binder.bind(Servlet.class).annotatedWith(TheServlet.class).toInstance(servlet); binder.bind(new TypeLiteral<Map<String, String>>() {}).annotatedWith(TheServlet.class).toInstance(ImmutableMap.<String, String>of()); httpServerBinder(binder).bindResource("/", "webapp/user").withWelcomeFile("user-welcome.txt"); httpServerBinder(binder).bindResource("/", "webapp/user2"); httpServerBinder(binder).bindResource("path", "webapp/user").withWelcomeFile("user-welcome.txt"); httpServerBinder(binder).bindResource("path", "webapp/user2"); } }); Injector injector = app .strictConfig() .doNotInitializeLogging() .initialize(); LifeCycleManager lifeCycleManager = injector.getInstance(LifeCycleManager.class); TestingHttpServer server = injector.getInstance(TestingHttpServer.class); try (HttpClient client = new JettyHttpClient(new HttpClientConfig().setConnectTimeout(new Duration(1, SECONDS)))) { // test http resources URI uri = server.getBaseUrl(); assertResource(uri, client, "", "welcome user!"); assertResource(uri, client, "user-welcome.txt", "welcome user!"); assertResource(uri, client, "user.txt", "user"); assertResource(uri, client, "user2.txt", "user2"); assertResource(uri, client, "path", "welcome user!"); assertResource(uri, client, "path/", "welcome user!"); assertResource(uri, client, "path/user-welcome.txt", "welcome user!"); assertResource(uri, client, "path/user.txt", "user"); assertResource(uri, client, "path/user2.txt", "user2"); // verify that servlet did not receive resource requests assertEquals(servlet.getCallCount(), 0); } finally { lifeCycleManager.stop(); } } private static void assertResource(URI baseUri, HttpClient client, String path, String contents) { HttpUriBuilder uriBuilder = uriBuilderFrom(baseUri); StringResponseHandler.StringResponse data = client.execute(prepareGet().setUri(uriBuilder.appendPath(path).build()).build(), createStringResponseHandler()); assertEquals(data.getStatusCode(), HttpStatus.OK.code()); MediaType contentType = MediaType.parse(data.getHeader(HttpHeaders.CONTENT_TYPE)); assertTrue(PLAIN_TEXT_UTF_8.is(contentType), "Expected text/plain but got " + contentType); assertEquals(data.getBody().trim(), contents); } private TestingHttpServer createTestingHttpServer(DummyServlet servlet, Map<String, String> params) throws IOException { NodeInfo nodeInfo = new NodeInfo("test"); HttpServerConfig config = new HttpServerConfig().setHttpPort(0); HttpServerInfo httpServerInfo = new HttpServerInfo(config, nodeInfo); return new TestingHttpServer(httpServerInfo, nodeInfo, config, servlet, params); } private TestingHttpServer createTestingHttpServerWithFilter(DummyServlet servlet, Map<String, String> params, DummyFilter filter) throws IOException { NodeInfo nodeInfo = new NodeInfo("test"); HttpServerConfig config = new HttpServerConfig().setHttpPort(0); HttpServerInfo httpServerInfo = new HttpServerInfo(config, nodeInfo); return new TestingHttpServer(httpServerInfo, nodeInfo, config, servlet, params, ImmutableSet.<Filter>of(filter), ImmutableSet.<HttpResourceBinding>of()); } static class DummyServlet extends HttpServlet { private String sampleInitParam; private int callCount; @Override public synchronized void init(ServletConfig config) throws ServletException { sampleInitParam = config.getInitParameter("sampleInitParameter"); } public synchronized String getSampleInitParam() { return sampleInitParam; } public synchronized int getCallCount() { return callCount; } @Override protected synchronized void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ++callCount; resp.setStatus(HttpServletResponse.SC_OK); } } static class DummyFilter implements Filter { private final AtomicInteger callCount = new AtomicInteger(); public int getCallCount() { return callCount.get(); } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { callCount.incrementAndGet(); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } } }