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));
});
}
}