package io.dropwizard.jetty;
import com.codahale.metrics.MetricRegistry;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.Resources;
import io.dropwizard.configuration.YamlConfigurationFactory;
import io.dropwizard.jackson.DiscoverableSubtypeResolver;
import io.dropwizard.jackson.Jackson;
import io.dropwizard.logging.ConsoleAppenderFactory;
import io.dropwizard.logging.FileAppenderFactory;
import io.dropwizard.logging.SyslogAppenderFactory;
import io.dropwizard.util.Duration;
import io.dropwizard.util.Size;
import io.dropwizard.validation.BaseValidator;
import org.eclipse.jetty.http.HttpCompliance;
import org.eclipse.jetty.io.ArrayByteBufferPool;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.server.ForwardedRequestCustomizer;
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.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
import org.eclipse.jetty.util.thread.ThreadPool;
import org.junit.Before;
import org.junit.Test;
import javax.validation.Validator;
import java.io.File;
import java.util.Optional;
import static org.apache.commons.lang3.reflect.FieldUtils.getField;
import static org.assertj.core.api.Assertions.assertThat;
public class HttpConnectorFactoryTest {
private final ObjectMapper objectMapper = Jackson.newObjectMapper();
private final Validator validator = BaseValidator.newValidator();
@Before
public void setUp() throws Exception {
objectMapper.getSubtypeResolver().registerSubtypes(ConsoleAppenderFactory.class,
FileAppenderFactory.class, SyslogAppenderFactory.class, HttpConnectorFactory.class);
}
@Test
public void isDiscoverable() throws Exception {
assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes())
.contains(HttpConnectorFactory.class);
}
@Test
public void testParseMinimalConfiguration() throws Exception {
HttpConnectorFactory http =
new YamlConfigurationFactory<>(HttpConnectorFactory.class, validator, objectMapper, "dw")
.build(new File(Resources.getResource("yaml/http-connector-minimal.yml").toURI()));
assertThat(http.getPort()).isEqualTo(8080);
assertThat(http.getBindHost()).isNull();
assertThat(http.isInheritChannel()).isEqualTo(false);
assertThat(http.getHeaderCacheSize()).isEqualTo(Size.bytes(512));
assertThat(http.getOutputBufferSize()).isEqualTo(Size.kilobytes(32));
assertThat(http.getMaxRequestHeaderSize()).isEqualTo(Size.kilobytes(8));
assertThat(http.getMaxResponseHeaderSize()).isEqualTo(Size.kilobytes(8));
assertThat(http.getInputBufferSize()).isEqualTo(Size.kilobytes(8));
assertThat(http.getIdleTimeout()).isEqualTo(Duration.seconds(30));
assertThat(http.getMinBufferPoolSize()).isEqualTo(Size.bytes(64));
assertThat(http.getBufferPoolIncrement()).isEqualTo(Size.bytes(1024));
assertThat(http.getMaxBufferPoolSize()).isEqualTo(Size.kilobytes(64));
assertThat(http.getAcceptorThreads()).isEmpty();
assertThat(http.getSelectorThreads()).isEmpty();
assertThat(http.getAcceptQueueSize()).isNull();
assertThat(http.isReuseAddress()).isTrue();
assertThat(http.getSoLingerTime()).isNull();
assertThat(http.isUseServerHeader()).isFalse();
assertThat(http.isUseDateHeader()).isTrue();
assertThat(http.isUseForwardedHeaders()).isTrue();
assertThat(http.getBlockingTimeout()).isNull();
assertThat(http.getHttpCompliance()).isEqualTo(HttpCompliance.RFC7230);
}
@Test
public void testParseFullConfiguration() throws Exception {
HttpConnectorFactory http =
new YamlConfigurationFactory<>(HttpConnectorFactory.class, validator, objectMapper, "dw")
.build(new File(Resources.getResource("yaml/http-connector.yml").toURI()));
assertThat(http.getPort()).isEqualTo(9090);
assertThat(http.getBindHost()).isEqualTo("127.0.0.1");
assertThat(http.isInheritChannel()).isEqualTo(true);
assertThat(http.getHeaderCacheSize()).isEqualTo(Size.bytes(256));
assertThat(http.getOutputBufferSize()).isEqualTo(Size.kilobytes(128));
assertThat(http.getMaxRequestHeaderSize()).isEqualTo(Size.kilobytes(4));
assertThat(http.getMaxResponseHeaderSize()).isEqualTo(Size.kilobytes(4));
assertThat(http.getInputBufferSize()).isEqualTo(Size.kilobytes(4));
assertThat(http.getIdleTimeout()).isEqualTo(Duration.seconds(10));
assertThat(http.getMinBufferPoolSize()).isEqualTo(Size.bytes(128));
assertThat(http.getBufferPoolIncrement()).isEqualTo(Size.bytes(500));
assertThat(http.getMaxBufferPoolSize()).isEqualTo(Size.kilobytes(32));
assertThat(http.getAcceptorThreads()).contains(1);
assertThat(http.getSelectorThreads()).contains(4);
assertThat(http.getAcceptQueueSize()).isEqualTo(1024);
assertThat(http.isReuseAddress()).isFalse();
assertThat(http.getSoLingerTime()).isEqualTo(Duration.seconds(30));
assertThat(http.isUseServerHeader()).isTrue();
assertThat(http.isUseDateHeader()).isFalse();
assertThat(http.isUseForwardedHeaders()).isFalse();
assertThat(http.getBlockingTimeout()).isEqualTo(Duration.seconds(30));
assertThat(http.getHttpCompliance()).isEqualTo(HttpCompliance.RFC2616);
}
@Test
public void testBuildConnector() throws Exception {
HttpConnectorFactory http = new HttpConnectorFactory();
http.setBindHost("127.0.0.1");
http.setAcceptorThreads(Optional.of(1));
http.setSelectorThreads(Optional.of(2));
http.setAcceptQueueSize(1024);
http.setSoLingerTime(Duration.seconds(30));
http.setBlockingTimeout(Duration.minutes(1));
Server server = new Server();
MetricRegistry metrics = new MetricRegistry();
ThreadPool threadPool = new QueuedThreadPool();
ServerConnector connector = (ServerConnector) http.build(server, metrics, "test-http-connector", threadPool);
assertThat(connector.getPort()).isEqualTo(8080);
assertThat(connector.getHost()).isEqualTo("127.0.0.1");
assertThat(connector.getAcceptQueueSize()).isEqualTo(1024);
assertThat(connector.getReuseAddress()).isTrue();
assertThat(connector.getSoLingerTime()).isEqualTo(30);
assertThat(connector.getIdleTimeout()).isEqualTo(30000);
assertThat(connector.getName()).isEqualTo("test-http-connector");
assertThat(connector.getServer()).isSameAs(server);
assertThat(connector.getScheduler()).isInstanceOf(ScheduledExecutorScheduler.class);
assertThat(connector.getExecutor()).isSameAs(threadPool);
// That's gross, but unfortunately ArrayByteBufferPool doesn't have public API for configuration
ByteBufferPool byteBufferPool = connector.getByteBufferPool();
assertThat(byteBufferPool).isInstanceOf(ArrayByteBufferPool.class);
assertThat(getField(ArrayByteBufferPool.class, "_min", true).get(byteBufferPool)).isEqualTo(64);
assertThat(getField(ArrayByteBufferPool.class, "_inc", true).get(byteBufferPool)).isEqualTo(1024);
assertThat(((Object[]) getField(ArrayByteBufferPool.class, "_direct", true)
.get(byteBufferPool)).length).isEqualTo(64);
assertThat(connector.getAcceptors()).isEqualTo(1);
assertThat(connector.getSelectorManager().getSelectorCount()).isEqualTo(2);
Jetty93InstrumentedConnectionFactory connectionFactory =
(Jetty93InstrumentedConnectionFactory) connector.getConnectionFactory("http/1.1");
assertThat(connectionFactory).isInstanceOf(Jetty93InstrumentedConnectionFactory.class);
assertThat(connectionFactory.getTimer())
.isSameAs(metrics.timer("org.eclipse.jetty.server.HttpConnectionFactory.127.0.0.1.8080.connections"));
HttpConnectionFactory httpConnectionFactory = (HttpConnectionFactory) connectionFactory.getConnectionFactory();
assertThat(httpConnectionFactory.getInputBufferSize()).isEqualTo(8192);
assertThat(httpConnectionFactory.getHttpCompliance()).isEqualByComparingTo(HttpCompliance.RFC7230);
HttpConfiguration httpConfiguration = httpConnectionFactory.getHttpConfiguration();
assertThat(httpConfiguration.getHeaderCacheSize()).isEqualTo(512);
assertThat(httpConfiguration.getOutputBufferSize()).isEqualTo(32768);
assertThat(httpConfiguration.getRequestHeaderSize()).isEqualTo(8192);
assertThat(httpConfiguration.getResponseHeaderSize()).isEqualTo(8192);
assertThat(httpConfiguration.getSendDateHeader()).isTrue();
assertThat(httpConfiguration.getSendServerVersion()).isFalse();
assertThat(httpConfiguration.getCustomizers()).hasAtLeastOneElementOfType(ForwardedRequestCustomizer.class);
assertThat(httpConfiguration.getBlockingTimeout()).isEqualTo(60000L);
connector.stop();
server.stop();
}
@Test
public void testDefaultAcceptQueueSize() throws Exception {
HttpConnectorFactory http = new HttpConnectorFactory();
http.setBindHost("127.0.0.1");
http.setAcceptorThreads(Optional.of(1));
http.setSelectorThreads(Optional.of(2));
http.setSoLingerTime(Duration.seconds(30));
Server server = new Server();
MetricRegistry metrics = new MetricRegistry();
ThreadPool threadPool = new QueuedThreadPool();
ServerConnector connector = (ServerConnector) http.build(server, metrics, "test-http-connector", threadPool);
assertThat(connector.getAcceptQueueSize()).isEqualTo(NetUtil.getTcpBacklog());
connector.stop();
}
}