package org.jooby.internal.jetty; import static org.easymock.EasyMock.eq; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.isA; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.util.Map; import javax.inject.Provider; import javax.servlet.ServletContext; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.DecoratedObjectFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.ThreadPool; import org.eclipse.jetty.websocket.api.WebSocketBehavior; import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.server.WebSocketServerFactory; import org.eclipse.jetty.websocket.servlet.WebSocketCreator; import org.jooby.spi.HttpHandler; import org.jooby.test.MockUnit; import org.jooby.test.MockUnit.Block; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import com.google.common.collect.ImmutableMap; import com.typesafe.config.Config; import com.typesafe.config.ConfigException; import com.typesafe.config.ConfigFactory; import com.typesafe.config.ConfigValueFactory; @RunWith(PowerMockRunner.class) @PrepareForTest({JettyServer.class, Server.class, QueuedThreadPool.class, ServerConnector.class, HttpConfiguration.class, HttpConnectionFactory.class, WebSocketPolicy.class, WebSocketServerFactory.class }) public class JettyServerTest { Map<String, Object> httpConfig = ImmutableMap.<String, Object> builder() .put("HeaderCacheSize", "8k") .put("RequestHeaderSize", "8k") .put("ResponseHeaderSize", "8k") .put("FileSizeThreshold", "16k") .put("SendServerVersion", false) .put("SendXPoweredBy", false) .put("SendDateHeader", false) .put("OutputBufferSize", "32k") .put("BadOption", "bad") .put("connector", ImmutableMap.<String, Object> builder() .put("AcceptQueueSize", 0) .put("SoLingerTime", -1) .put("StopTimeout", "3s") .put("IdleTimeout", "3s") .build()) .build(); Map<String, Object> ws = ImmutableMap.<String, Object> builder() .put("MaxTextMessageSize", "64k") .put("MaxTextMessageBufferSize", "32k") .put("MaxBinaryMessageSize", "64k") .put("MaxBinaryMessageBufferSize", "32kB") .put("AsyncWriteTimeout", 60000) .put("IdleTimeout", "5minutes") .put("InputBufferSize", "4k") .build(); Config config = ConfigFactory.empty() .withValue("jetty.threads.MinThreads", ConfigValueFactory.fromAnyRef("1")) .withValue("jetty.threads.MaxThreads", ConfigValueFactory.fromAnyRef("10")) .withValue("jetty.threads.IdleTimeout", ConfigValueFactory.fromAnyRef("3s")) .withValue("jetty.threads.Name", ConfigValueFactory.fromAnyRef("jetty task")) .withValue("jetty.FileSizeThreshold", ConfigValueFactory.fromAnyRef(1024)) .withValue("jetty.url.charset", ConfigValueFactory.fromAnyRef("UTF-8")) .withValue("jetty.http", ConfigValueFactory.fromAnyRef(httpConfig)) .withValue("jetty.ws", ConfigValueFactory.fromAnyRef(ws)) .withValue("server.http.MaxRequestSize", ConfigValueFactory.fromAnyRef("200k")) .withValue("server.http2.enabled", ConfigValueFactory.fromAnyRef(false)) .withValue("application.port", ConfigValueFactory.fromAnyRef(6789)) .withValue("application.host", ConfigValueFactory.fromAnyRef("0.0.0.0")) .withValue("application.tmpdir", ConfigValueFactory.fromAnyRef("target")); private MockUnit.Block pool = unit -> { QueuedThreadPool pool = unit.mockConstructor(QueuedThreadPool.class); unit.registerMock(QueuedThreadPool.class, pool); pool.setMaxThreads(10); pool.setMinThreads(1); pool.setIdleTimeout(3000); pool.setName("jetty task"); }; private MockUnit.Block server = unit -> { Server server = unit.constructor(Server.class) .args(ThreadPool.class) .build(unit.get(QueuedThreadPool.class)); ContextHandler ctx = unit.constructor(ContextHandler.class) .build(); ctx.setContextPath("/"); ctx.setHandler(isA(JettyHandler.class)); ctx.setAttribute(eq(DecoratedObjectFactory.ATTR), isA(DecoratedObjectFactory.class)); expect(ctx.getServletContext()).andReturn(unit.get(ContextHandler.Context.class)); server.setStopAtShutdown(false); server.setHandler(ctx); server.start(); server.join(); server.stop(); unit.registerMock(Server.class, server); expect(server.getThreadPool()).andReturn(unit.get(QueuedThreadPool.class)).anyTimes(); }; private MockUnit.Block httpConf = unit -> { HttpConfiguration conf = unit.mockConstructor(HttpConfiguration.class); conf.setOutputBufferSize(32768); conf.setRequestHeaderSize(8192); conf.setSendXPoweredBy(false); conf.setHeaderCacheSize(8192); conf.setSendServerVersion(false); conf.setSendDateHeader(false); conf.setResponseHeaderSize(8192); unit.registerMock(HttpConfiguration.class, conf); }; private MockUnit.Block httpFactory = unit -> { HttpConnectionFactory factory = unit.constructor(HttpConnectionFactory.class) .args(HttpConfiguration.class) .build(unit.get(HttpConfiguration.class)); unit.registerMock(HttpConnectionFactory.class, factory); }; private MockUnit.Block connector = unit -> { ServerConnector connector = unit.constructor(ServerConnector.class) .args(Server.class, ConnectionFactory[].class) .build(unit.get(HttpConnectionFactory.class)); connector.setSoLingerTime(-1); connector.setIdleTimeout(3000); connector.setStopTimeout(3000); connector.setAcceptQueueSize(0); connector.setPort(6789); connector.setHost("0.0.0.0"); unit.registerMock(ServerConnector.class, connector); Server server = unit.get(Server.class); server.addConnector(connector); }; private Block wsPolicy = unit -> { WebSocketPolicy policy = unit.constructor(WebSocketPolicy.class) .args(WebSocketBehavior.class) .build(WebSocketBehavior.SERVER); policy.setAsyncWriteTimeout(60000L); policy.setMaxBinaryMessageSize(65536); policy.setMaxBinaryMessageBufferSize(32000); policy.setIdleTimeout(300000L); policy.setMaxTextMessageSize(65536); policy.setMaxTextMessageBufferSize(32768); policy.setInputBufferSize(4096); unit.registerMock(WebSocketPolicy.class, policy); }; private Block wsFactory = unit -> { WebSocketServerFactory factory = unit.constructor(WebSocketServerFactory.class) .args(ServletContext.class, WebSocketPolicy.class) .build(unit.get(ContextHandler.Context.class), unit.get(WebSocketPolicy.class)); factory.setCreator(isA(WebSocketCreator.class)); factory.setStopTimeout(30000L); unit.registerMock(WebSocketServerFactory.class, factory); }; @SuppressWarnings("unchecked") @Test public void startStopServer() throws Exception { new MockUnit(HttpHandler.class, Provider.class, ContextHandler.Context.class) .expect(pool) .expect(server) .expect(httpConf) .expect(httpFactory) .expect(connector) .expect(wsPolicy) .expect(wsFactory) .run(unit -> { JettyServer server = new JettyServer(unit.get(HttpHandler.class), config, unit.get(Provider.class)); assertNotNull(server.executor()); server.start(); assertTrue(server.executor().isPresent()); server.join(); server.stop(); }); } @SuppressWarnings("unchecked") @Test(expected = IllegalArgumentException.class) public void badOption() throws Exception { new MockUnit(HttpHandler.class, Provider.class) .expect(unit -> { QueuedThreadPool pool = unit.mockConstructor(QueuedThreadPool.class); unit.registerMock(QueuedThreadPool.class, pool); pool.setMaxThreads(10); expectLastCall().andThrow(new IllegalArgumentException("10")); }) .run(unit -> { new JettyServer(unit.get(HttpHandler.class), config, unit.get(Provider.class)); }); } @SuppressWarnings("unchecked") @Test(expected = ConfigException.BadValue.class) public void badConfOption() throws Exception { new MockUnit(HttpHandler.class, Provider.class) .run(unit -> { new JettyServer(unit.get(HttpHandler.class), config.withValue("jetty.threads.MinThreads", ConfigValueFactory.fromAnyRef("x")), unit.get(Provider.class)); }); } }