SslHandler factory, switch to JUnit 5

This commit is contained in:
Jörg Prante 2019-04-30 16:35:45 +02:00
parent ad460a5111
commit ef0c59d3f5
76 changed files with 2041 additions and 678 deletions

View file

@ -37,6 +37,9 @@ subprojects {
} }
dependencies { dependencies {
testImplementation "org.junit.jupiter:junit-jupiter-api:${project.property('junit.version')}"
testImplementation "org.junit.jupiter:junit-jupiter-params:${project.property('junit.version')}"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${project.property('junit.version')}"
alpnagent "org.mortbay.jetty.alpn:jetty-alpn-agent:${project.property('alpnagent.version')}" alpnagent "org.mortbay.jetty.alpn:jetty-alpn-agent:${project.property('alpnagent.version')}"
asciidoclet "org.asciidoctor:asciidoclet:${project.property('asciidoclet.version')}" asciidoclet "org.asciidoctor:asciidoclet:${project.property('asciidoclet.version')}"
wagon "org.apache.maven.wagon:wagon-ssh:${project.property('wagon.version')}" wagon "org.apache.maven.wagon:wagon-ssh:${project.property('wagon.version')}"
@ -52,9 +55,11 @@ subprojects {
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8
} }
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:all,-serial" options.compilerArgs << "-Xlint:all,-fallthrough"
if (!options.compilerArgs.contains("-processor")) {
options.compilerArgs << '-proc:none'
}
} }
jar { jar {
@ -64,10 +69,20 @@ subprojects {
} }
test { test {
useJUnitPlatform()
systemProperty 'java.util.logging.config.file', 'src/test/resources/logging.properties' systemProperty 'java.util.logging.config.file', 'src/test/resources/logging.properties'
failFast = false
testLogging { testLogging {
showStandardStreams = false events 'PASSED', 'FAILED', 'SKIPPED'
exceptionFormat = 'full' }
afterSuite { desc, result ->
if (!desc.parent) {
println "\nTest result: ${result.resultType}"
println "Test summary: ${result.testCount} tests, " +
"${result.successfulTestCount} succeeded, " +
"${result.failedTestCount} failed, " +
"${result.skippedTestCount} skipped"
}
} }
if (JavaVersion.current() == JavaVersion.VERSION_1_8) { if (JavaVersion.current() == JavaVersion.VERSION_1_8) {
jvmArgs "-javaagent:" + configurations.alpnagent.asPath jvmArgs "-javaagent:" + configurations.alpnagent.asPath
@ -105,13 +120,13 @@ subprojects {
task javadocJar(type: Jar, dependsOn: classes) { task javadocJar(type: Jar, dependsOn: classes) {
from javadoc from javadoc
into "build/tmp" into "build/tmp"
classifier 'javadoc' archiveClassifier.set('javadoc')
} }
task sourcesJar(type: Jar, dependsOn: classes) { task sourcesJar(type: Jar, dependsOn: classes) {
from sourceSets.main.allSource from sourceSets.main.allSource
into "build/tmp" into "build/tmp"
classifier 'sources' archiveClassifier.set('sources')
} }
artifacts { artifacts {
@ -202,7 +217,7 @@ subprojects {
} }
spotbugs { spotbugs {
toolVersion = '3.1.3' toolVersion = '3.1.12'
sourceSets = [sourceSets.main] sourceSets = [sourceSets.main]
ignoreFailures = true ignoreFailures = true
effort = "max" effort = "max"

View file

@ -1,16 +1,24 @@
group = org.xbib group = org.xbib
name = netty-http name = netty-http
version = 4.1.35.0 version = 4.1.35.1
# main packages # main packages
netty.version = 4.1.35.Final netty.version = 4.1.35.Final
tcnative.version = 2.0.22.Final tcnative.version = 2.0.22.Final
bouncycastle.version = 1.61
alpnagent.version = 2.0.9 alpnagent.version = 2.0.9
# common
xbib-net-url.version = 1.2.2 xbib-net-url.version = 1.2.2
# server
bouncycastle.version = 1.61
# server-rest
xbib-guice.version = 4.0.4
# test packages # test packages
junit.version = 4.12 junit.version = 5.4.2
conscrypt.version = 2.0.0 conscrypt.version = 2.0.0
jackson.version = 2.8.11.1 jackson.version = 2.8.11.1
wagon.version = 3.0.0 wagon.version = 3.0.0

View file

View file

View file

@ -1,11 +1,12 @@
dependencies { dependencies {
compile project(":netty-http-common") implementation project(":netty-http-common")
compile "io.netty:netty-handler-proxy:${project.property('netty.version')}" implementation "io.netty:netty-handler-proxy:${project.property('netty.version')}"
compile "io.netty:netty-transport-native-epoll:${project.property('netty.version')}" implementation "io.netty:netty-transport-native-epoll:${project.property('netty.version')}"
implementation "io.netty:netty-codec-http2:${project.property('netty.version')}"
implementation "org.xbib:net-url:${project.property('xbib-net-url.version')}"
testCompile "io.netty:netty-tcnative-boringssl-static:${project.property('tcnative.version')}" testImplementation "io.netty:netty-tcnative-boringssl-static:${project.property('tcnative.version')}"
testCompile "org.conscrypt:conscrypt-openjdk-uber:${project.property('conscrypt.version')}" testImplementation "org.conscrypt:conscrypt-openjdk-uber:${project.property('conscrypt.version')}"
testCompile "junit:junit:${project.property('junit.version')}" testImplementation "com.fasterxml.jackson.core:jackson-databind:${project.property('jackson.version')}"
testCompile "com.fasterxml.jackson.core:jackson-databind:${project.property('jackson.version')}"
} }

View file

@ -199,13 +199,13 @@ public final class Client {
Channel channel; Channel channel;
if (httpAddress != null) { if (httpAddress != null) {
HttpVersion httpVersion = httpAddress.getVersion(); HttpVersion httpVersion = httpAddress.getVersion();
SslHandlerFactory sslHandlerFactory = new SslHandlerFactory(clientConfig, httpAddress, byteBufAllocator);
ChannelInitializer<Channel> initializer; ChannelInitializer<Channel> initializer;
SslHandler sslHandler = newSslHandler(clientConfig, byteBufAllocator, httpAddress);
if (httpVersion.majorVersion() == 1) { if (httpVersion.majorVersion() == 1) {
initializer = new HttpChannelInitializer(clientConfig, httpAddress, sslHandler, initializer = new HttpChannelInitializer(clientConfig, httpAddress, sslHandlerFactory,
new Http2ChannelInitializer(clientConfig, httpAddress, sslHandler)); new Http2ChannelInitializer(clientConfig, httpAddress, sslHandlerFactory));
} else { } else {
initializer = new Http2ChannelInitializer(clientConfig, httpAddress, sslHandler); initializer = new Http2ChannelInitializer(clientConfig, httpAddress, sslHandlerFactory);
} }
try { try {
channel = bootstrap.handler(initializer) channel = bootstrap.handler(initializer)
@ -412,15 +412,34 @@ public final class Client {
public void channelCreated(Channel channel) { public void channelCreated(Channel channel) {
HttpAddress httpAddress = channel.attr(pool.getAttributeKey()).get(); HttpAddress httpAddress = channel.attr(pool.getAttributeKey()).get();
HttpVersion httpVersion = httpAddress.getVersion(); HttpVersion httpVersion = httpAddress.getVersion();
SslHandler sslHandler = newSslHandler(clientConfig, byteBufAllocator, httpAddress); SslHandlerFactory sslHandlerFactory = new SslHandlerFactory(clientConfig, httpAddress, byteBufAllocator);
Http2ChannelInitializer http2ChannelInitializer = new Http2ChannelInitializer(clientConfig, httpAddress, sslHandlerFactory);
if (httpVersion.majorVersion() == 1) { if (httpVersion.majorVersion() == 1) {
HttpChannelInitializer initializer = new HttpChannelInitializer(clientConfig, httpAddress, sslHandler, HttpChannelInitializer initializer = new HttpChannelInitializer(clientConfig, httpAddress, sslHandlerFactory,
new Http2ChannelInitializer(clientConfig, httpAddress, sslHandler)); http2ChannelInitializer);
initializer.initChannel(channel); initializer.initChannel(channel);
} else { } else {
Http2ChannelInitializer initializer = new Http2ChannelInitializer(clientConfig, httpAddress, sslHandler); http2ChannelInitializer.initChannel(channel);
initializer.initChannel(channel);
} }
} }
} }
public class SslHandlerFactory {
private final ClientConfig clientConfig;
private final HttpAddress httpAddress;
private final ByteBufAllocator allocator;
SslHandlerFactory(ClientConfig clientConfig, HttpAddress httpAddress, ByteBufAllocator allocator) {
this.clientConfig = clientConfig;
this.httpAddress = httpAddress;
this.allocator = allocator;
}
public SslHandler create() {
return newSslHandler(clientConfig, allocator, httpAddress);
}
}
} }

View file

@ -10,7 +10,7 @@ import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LogLevel;
import io.netty.handler.ssl.ApplicationProtocolNames; import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler; import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
import io.netty.handler.ssl.SslHandler; import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.ClientConfig; import org.xbib.netty.http.client.ClientConfig;
import org.xbib.netty.http.client.handler.http2.Http2ChannelInitializer; import org.xbib.netty.http.client.handler.http2.Http2ChannelInitializer;
import org.xbib.netty.http.common.HttpAddress; import org.xbib.netty.http.common.HttpAddress;
@ -26,7 +26,7 @@ public class HttpChannelInitializer extends ChannelInitializer<Channel> {
private final HttpAddress httpAddress; private final HttpAddress httpAddress;
private final SslHandler sslHandler; private final Client.SslHandlerFactory sslHandlerFactory;
private final HttpResponseHandler httpResponseHandler; private final HttpResponseHandler httpResponseHandler;
@ -34,11 +34,11 @@ public class HttpChannelInitializer extends ChannelInitializer<Channel> {
public HttpChannelInitializer(ClientConfig clientConfig, public HttpChannelInitializer(ClientConfig clientConfig,
HttpAddress httpAddress, HttpAddress httpAddress,
SslHandler sslHandler, Client.SslHandlerFactory sslHandlerFactory,
Http2ChannelInitializer http2ChannelInitializer) { Http2ChannelInitializer http2ChannelInitializer) {
this.clientConfig = clientConfig; this.clientConfig = clientConfig;
this.httpAddress = httpAddress; this.httpAddress = httpAddress;
this.sslHandler = sslHandler; this.sslHandlerFactory = sslHandlerFactory;
this.http2ChannelInitializer = http2ChannelInitializer; this.http2ChannelInitializer = http2ChannelInitializer;
this.httpResponseHandler = new HttpResponseHandler(); this.httpResponseHandler = new HttpResponseHandler();
} }
@ -60,7 +60,7 @@ public class HttpChannelInitializer extends ChannelInitializer<Channel> {
private void configureEncrypted(Channel channel) { private void configureEncrypted(Channel channel) {
ChannelPipeline pipeline = channel.pipeline(); ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(sslHandler); pipeline.addLast(sslHandlerFactory.create());
if (clientConfig.isEnableNegotiation()) { if (clientConfig.isEnableNegotiation()) {
ApplicationProtocolNegotiationHandler negotiationHandler = ApplicationProtocolNegotiationHandler negotiationHandler =
new ApplicationProtocolNegotiationHandler(ApplicationProtocolNames.HTTP_1_1) { new ApplicationProtocolNegotiationHandler(ApplicationProtocolNames.HTTP_1_1) {
@ -101,10 +101,6 @@ public class HttpChannelInitializer extends ChannelInitializer<Channel> {
false); false);
httpObjectAggregator.setMaxCumulationBufferComponents(clientConfig.getMaxCompositeBufferComponents()); httpObjectAggregator.setMaxCumulationBufferComponents(clientConfig.getMaxCompositeBufferComponents());
pipeline.addLast(httpObjectAggregator); pipeline.addLast(httpObjectAggregator);
/*if (clientConfig.isEnableGzip()) {
pipeline.addLast(new HttpChunkContentCompressor(6));
}
pipeline.addLast(new ChunkedWriteHandler());*/
pipeline.addLast(httpResponseHandler); pipeline.addLast(httpResponseHandler);
} }
} }

View file

@ -12,7 +12,7 @@ import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.codec.http2.Http2MultiplexCodec; import io.netty.handler.codec.http2.Http2MultiplexCodec;
import io.netty.handler.codec.http2.Http2MultiplexCodecBuilder; import io.netty.handler.codec.http2.Http2MultiplexCodecBuilder;
import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LogLevel;
import io.netty.handler.ssl.SslHandler; import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.ClientConfig; import org.xbib.netty.http.client.ClientConfig;
import org.xbib.netty.http.client.handler.http.TrafficLoggingHandler; import org.xbib.netty.http.client.handler.http.TrafficLoggingHandler;
import org.xbib.netty.http.client.transport.Transport; import org.xbib.netty.http.client.transport.Transport;
@ -29,14 +29,14 @@ public class Http2ChannelInitializer extends ChannelInitializer<Channel> {
private final HttpAddress httpAddress; private final HttpAddress httpAddress;
private final SslHandler sslHandler; private final Client.SslHandlerFactory sslHandlerFactory;
public Http2ChannelInitializer(ClientConfig clientConfig, public Http2ChannelInitializer(ClientConfig clientConfig,
HttpAddress httpAddress, HttpAddress httpAddress,
SslHandler sslHandler) { Client.SslHandlerFactory sslHandlerFactory) {
this.clientConfig = clientConfig; this.clientConfig = clientConfig;
this.httpAddress = httpAddress; this.httpAddress = httpAddress;
this.sslHandler = sslHandler; this.sslHandlerFactory = sslHandlerFactory;
} }
@Override @Override
@ -55,7 +55,7 @@ public class Http2ChannelInitializer extends ChannelInitializer<Channel> {
} }
private void configureEncrypted(Channel channel) { private void configureEncrypted(Channel channel) {
channel.pipeline().addLast(sslHandler); channel.pipeline().addLast(sslHandlerFactory.create());
configureCleartext(channel); configureCleartext(channel);
} }

View file

@ -80,12 +80,8 @@ public class HttpTransport extends BaseTransport {
logger.log(Level.WARNING, "throwable not null for response " + fullHttpResponse, throwable); logger.log(Level.WARNING, "throwable not null for response " + fullHttpResponse, throwable);
return; return;
} }
if (requests.isEmpty()) {
logger.log(Level.WARNING, "no request present, can not handle response " + fullHttpResponse);
return;
}
// streamID is expected to be null, last request on memory is expected to be current, remove request from memory // streamID is expected to be null, last request on memory is expected to be current, remove request from memory
Request request = requests.remove(requests.lastKey()); Request request = requests.remove(requests.isEmpty() ? null : requests.lastKey());
if (request != null) { if (request != null) {
for (String cookieString : fullHttpResponse.headers().getAll(HttpHeaderNames.SET_COOKIE)) { for (String cookieString : fullHttpResponse.headers().getAll(HttpHeaderNames.SET_COOKIE)) {
Cookie cookie = ClientCookieDecoder.STRICT.decode(cookieString); Cookie cookie = ClientCookieDecoder.STRICT.decode(cookieString);
@ -140,6 +136,6 @@ public class HttpTransport extends BaseTransport {
@Override @Override
protected String getRequestKey(String channelId, Integer streamId) { protected String getRequestKey(String channelId, Integer streamId) {
return requests.lastKey(); return requests.isEmpty() ? null : requests.lastKey();
} }
} }

View file

@ -1,7 +1,7 @@
package org.xbib.netty.http.client.test; package org.xbib.netty.http.client.test;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.xbib.netty.http.client.Client; import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.Request; import org.xbib.netty.http.client.Request;
@ -12,7 +12,7 @@ import java.util.function.Function;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
public class CompletableFutureTest { class CompletableFutureTest {
private static final Logger logger = Logger.getLogger(CompletableFutureTest.class.getName()); private static final Logger logger = Logger.getLogger(CompletableFutureTest.class.getName());
@ -20,7 +20,7 @@ public class CompletableFutureTest {
* Get some weird content from one URL and post it to another URL, by composing completable futures. * Get some weird content from one URL and post it to another URL, by composing completable futures.
*/ */
@Test @Test
public void testComposeCompletableFutures() throws IOException { void testComposeCompletableFutures() throws IOException {
Client client = Client.builder().build(); Client client = Client.builder().build();
try { try {
final Function<FullHttpResponse, String> httpResponseStringFunction = response -> final Function<FullHttpResponse, String> httpResponseStringFunction = response ->

View file

@ -1,8 +1,8 @@
package org.xbib.netty.http.client.test; package org.xbib.netty.http.client.test;
import org.conscrypt.Conscrypt; import org.conscrypt.Conscrypt;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.xbib.TestBase; import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.client.Client; import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.Request; import org.xbib.netty.http.client.Request;
@ -11,14 +11,14 @@ import java.nio.charset.StandardCharsets;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
public class ConscryptTest extends TestBase { @ExtendWith(NettyHttpExtension.class)
class ConscryptTest {
private static final Logger logger = Logger.getLogger(""); private static final Logger logger = Logger.getLogger(ConscryptTest.class.getName());
@Test @Test
public void testConscrypt() throws IOException { void testConscrypt() throws IOException {
Client client = Client.builder() Client client = Client.builder()
.enableDebug()
.setJdkSslProvider() .setJdkSslProvider()
.setSslContextProvider(Conscrypt.newProvider()) .setSslContextProvider(Conscrypt.newProvider())
.build(); .build();

View file

@ -1,7 +1,7 @@
package org.xbib.netty.http.client.test; package org.xbib.netty.http.client.test;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.xbib.TestBase; import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.client.Client; import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.Request; import org.xbib.netty.http.client.Request;
@ -10,9 +10,8 @@ import java.nio.charset.StandardCharsets;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** @ExtendWith(NettyHttpExtension.class)
*/ class CookieSetterHttpBinTest {
public class CookieSetterHttpBinTest extends TestBase {
private static final Logger logger = Logger.getLogger(CookieSetterHttpBinTest.class.getName()); private static final Logger logger = Logger.getLogger(CookieSetterHttpBinTest.class.getName());
@ -30,7 +29,7 @@ public class CookieSetterHttpBinTest extends TestBase {
* @throws IOException if test fails * @throws IOException if test fails
*/ */
@Test @Test
public void testHttpBinCookies() throws IOException { void testHttpBinCookies() throws IOException {
Client client = new Client(); Client client = new Client();
try { try {
Request request = Request.get() Request request = Request.get()

View file

@ -1,8 +1,8 @@
package org.xbib.netty.http.client.test; package org.xbib.netty.http.client.test;
import org.junit.Ignore; import org.junit.jupiter.api.Disabled;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.xbib.TestBase; import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.client.Client; import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.common.HttpAddress; import org.xbib.netty.http.common.HttpAddress;
import org.xbib.netty.http.client.Request; import org.xbib.netty.http.client.Request;
@ -18,17 +18,18 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
@Ignore @Disabled
public class ElasticsearchTest extends TestBase { @ExtendWith(NettyHttpExtension.class)
class ElasticsearchTest {
private static final Logger logger = Logger.getLogger(ElasticsearchTest.class.getName()); private static final Logger logger = Logger.getLogger(ElasticsearchTest.class.getName());
@Test @Test
@Ignore void testElasticsearch() throws IOException {
public void testElasticsearch() throws IOException { Client client = Client.builder()
Client client = Client.builder().enableDebug().build(); .build();
try { try {
Request request = Request.get().url("http://localhost:9200") Request request = Request.get().url("http://localhost:9200")
.build() .build()
@ -44,9 +45,9 @@ public class ElasticsearchTest extends TestBase {
} }
@Test @Test
@Ignore void testElasticsearchCreateDocument() throws IOException {
public void testElasticsearchCreateDocument() throws IOException { Client client = Client.builder()
Client client = Client.builder().enableDebug().build(); .build();
try { try {
Request request = Request.put().url("http://localhost:9200/test/test/1") Request request = Request.put().url("http://localhost:9200/test/test/1")
.json("{\"text\":\"Hello World\"}") .json("{\"text\":\"Hello World\"}")
@ -63,8 +64,7 @@ public class ElasticsearchTest extends TestBase {
} }
@Test @Test
@Ignore void testElasticsearchMatchQuery() throws IOException {
public void testElasticsearchMatchQuery() throws IOException {
Client client = new Client(); Client client = new Client();
try { try {
Request request = Request.post().url("http://localhost:9200/test/_search") Request request = Request.post().url("http://localhost:9200/test/_search")
@ -85,7 +85,7 @@ public class ElasticsearchTest extends TestBase {
* @throws IOException if test fails * @throws IOException if test fails
*/ */
@Test @Test
public void testElasticsearchPooled() throws IOException { void testElasticsearchPooled() throws IOException {
HttpAddress httpAddress = HttpAddress.http1("localhost", 9200); HttpAddress httpAddress = HttpAddress.http1("localhost", 9200);
int limit = 4; int limit = 4;
Client client = Client.builder() Client client = Client.builder()

View file

@ -1,8 +1,8 @@
package org.xbib.netty.http.client.test; package org.xbib.netty.http.client.test;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.xbib.TestBase; import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.client.Client; import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.Request; import org.xbib.netty.http.client.Request;
@ -11,13 +11,15 @@ import java.nio.charset.StandardCharsets;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
public class Http1Test extends TestBase { @ExtendWith(NettyHttpExtension.class)
class Http1Test {
private static final Logger logger = Logger.getLogger(Http1Test.class.getName()); private static final Logger logger = Logger.getLogger(Http1Test.class.getName());
@Test @Test
public void testHttp1() throws Exception { void testHttp1() throws Exception {
Client client = Client.builder().enableDebug().build(); Client client = Client.builder()
.build();
try { try {
Request request = Request.get().url("http://xbib.org").build() Request request = Request.get().url("http://xbib.org").build()
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " + .setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
@ -31,8 +33,28 @@ public class Http1Test extends TestBase {
} }
@Test @Test
public void testParallelRequests() throws IOException { void testSequentialRequests() throws Exception {
Client client = Client.builder().enableDebug().build(); Client client = Client.builder()
.build();
try {
Request request1 = Request.get().url("http://xbib.org").build()
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
msg.content().toString(StandardCharsets.UTF_8)));
client.execute(request1).get();
Request request2 = Request.get().url("http://google.com").setVersion("HTTP/1.1").build()
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
msg.content().toString(StandardCharsets.UTF_8)));
client.execute(request2).get();
} finally {
client.shutdown();
}
}
@Test
void testParallelRequests() throws IOException {
Client client = Client.builder()
.build();
try { try {
Request request1 = Request.builder(HttpMethod.GET) Request request1 = Request.builder(HttpMethod.GET)
.url("http://xbib.org").setVersion("HTTP/1.1") .url("http://xbib.org").setVersion("HTTP/1.1")
@ -58,22 +80,4 @@ public class Http1Test extends TestBase {
client.shutdownGracefully(); client.shutdownGracefully();
} }
} }
@Test
public void testSequentialRequests() throws Exception {
Client client = Client.builder().enableDebug().build();
try {
Request request1 = Request.get().url("http://xbib.org").build()
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
msg.content().toString(StandardCharsets.UTF_8)));
client.execute(request1).get();
Request request2 = Request.get().url("http://google.com").setVersion("HTTP/1.1").build()
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
msg.content().toString(StandardCharsets.UTF_8)));
client.execute(request2).get();
} finally {
client.shutdown();
}
}
} }

View file

@ -1,9 +1,8 @@
package org.xbib.netty.http.client.test; package org.xbib.netty.http.client.test;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import org.junit.Ignore; import org.junit.jupiter.api.Test;
import org.junit.Test; import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.TestBase;
import org.xbib.netty.http.client.Client; import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.Request; import org.xbib.netty.http.client.Request;
@ -12,7 +11,8 @@ import java.nio.charset.StandardCharsets;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
public class Http2Test extends TestBase { @ExtendWith(NettyHttpExtension.class)
class Http2Test {
private static final Logger logger = Logger.getLogger(Http2Test.class.getName()); private static final Logger logger = Logger.getLogger(Http2Test.class.getName());
@ -29,10 +29,8 @@ public class Http2Test extends TestBase {
* @throws IOException if test fails * @throws IOException if test fails
*/ */
@Test @Test
@Ignore void testAkamai() throws IOException {
public void testAkamai() throws IOException {
Client client = Client.builder() Client client = Client.builder()
.enableDebug()
.addServerNameForIdentification("http2.akamai.com") .addServerNameForIdentification("http2.akamai.com")
.build(); .build();
try { try {
@ -53,8 +51,9 @@ public class Http2Test extends TestBase {
} }
@Test @Test
public void testWebtide() throws Exception { void testWebtide() throws Exception {
Client client = Client.builder().enableDebug().build(); Client client = Client.builder()
.build();
client.logDiagnostics(Level.INFO); client.logDiagnostics(Level.INFO);
try { try {
Request request = Request.get().url("https://webtide.com").setVersion("HTTP/2.0").build() Request request = Request.get().url("https://webtide.com").setVersion("HTTP/2.0").build()
@ -69,10 +68,9 @@ public class Http2Test extends TestBase {
} }
@Test @Test
public void testHttp2PushIO() throws IOException { void testHttp2PushIO() throws IOException {
String url = "https://http2-push.io"; String url = "https://http2-push.io";
Client client = Client.builder() Client client = Client.builder()
.enableDebug()
.addServerNameForIdentification("http2-push.io") .addServerNameForIdentification("http2-push.io")
.build(); .build();
try { try {
@ -91,7 +89,7 @@ public class Http2Test extends TestBase {
} }
@Test @Test
public void testWebtideTwoRequestsOnSameConnection() throws IOException { void testWebtideTwoRequestsOnSameConnection() throws IOException {
Client client = new Client(); Client client = new Client();
try { try {
Request request1 = Request.builder(HttpMethod.GET) Request request1 = Request.builder(HttpMethod.GET)

View file

@ -1,4 +1,7 @@
package org.xbib.netty.http.server.test; package org.xbib.netty.http.client.test;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import java.util.logging.ConsoleHandler; import java.util.logging.ConsoleHandler;
import java.util.logging.Handler; import java.util.logging.Handler;
@ -7,10 +10,10 @@ import java.util.logging.LogManager;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.logging.SimpleFormatter; import java.util.logging.SimpleFormatter;
public class TestBase { public class NettyHttpExtension implements BeforeAllCallback {
static {
@Override
public void beforeAll(ExtensionContext context) throws Exception {
System.setProperty("io.netty.noUnsafe", Boolean.toString(true)); System.setProperty("io.netty.noUnsafe", Boolean.toString(true));
System.setProperty("io.netty.noKeySetOptimization", Boolean.toString(true)); System.setProperty("io.netty.noKeySetOptimization", Boolean.toString(true));
//System.setProperty("io.netty.recycler.maxCapacity", Integer.toString(0)); //System.setProperty("io.netty.recycler.maxCapacity", Integer.toString(0));

View file

@ -1,18 +1,18 @@
package org.xbib.netty.http.client.test; package org.xbib.netty.http.client.test;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.xbib.netty.http.client.Request; import org.xbib.netty.http.client.Request;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
public class RequestBuilderTest { class RequestBuilderTest {
private static final Logger logger = Logger.getLogger(RequestBuilderTest.class.getName()); private static final Logger logger = Logger.getLogger(RequestBuilderTest.class.getName());
@Test @Test
public void testSimpleRequest() { void testSimpleRequest() {
Request request = Request.builder(HttpMethod.GET).content("Hello", "text/plain").build(); Request request = Request.builder(HttpMethod.GET).content("Hello", "text/plain").build();
logger.log(Level.INFO, request.toString()); logger.log(Level.INFO, request.toString());
} }

View file

@ -1,9 +1,8 @@
package org.xbib.netty.http.client.test; package org.xbib.netty.http.client.test;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import org.junit.Ignore; import org.junit.jupiter.api.Test;
import org.junit.Test; import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.TestBase;
import org.xbib.netty.http.client.Client; import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.Request; import org.xbib.netty.http.client.Request;
@ -12,13 +11,15 @@ import java.nio.charset.StandardCharsets;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
public class SecureHttp1Test extends TestBase { @ExtendWith(NettyHttpExtension.class)
class SecureHttpTest {
private static final Logger logger = Logger.getLogger(SecureHttp1Test.class.getName()); private static final Logger logger = Logger.getLogger(SecureHttpTest.class.getName());
@Test @Test
public void testHttp1() throws Exception { void testHttp1() throws Exception {
Client client = Client.builder().enableDebug().build(); Client client = Client.builder()
.build();
try { try {
Request request = Request.get().url("https://www.google.com/").build() Request request = Request.get().url("https://www.google.com/").build()
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " + .setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
@ -32,39 +33,9 @@ public class SecureHttp1Test extends TestBase {
} }
@Test @Test
@Ignore void testSequentialRequests() throws Exception {
public void testParallelRequests() throws IOException { Client client = Client.builder()
Client client = Client.builder().enableDebug().build(); .build();
try {
Request request1 = Request.builder(HttpMethod.GET)
.url("https://google.com").setVersion("HTTP/1.1")
.build()
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
msg.headers().entries() +
//msg.content().toString(StandardCharsets.UTF_8) +
" status=" + msg.status().code()));
Request request2 = Request.builder(HttpMethod.GET)
.url("https://google.com").setVersion("HTTP/1.1")
.build()
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
msg.headers().entries() +
//msg.content().toString(StandardCharsets.UTF_8) +
" status=" + msg.status().code()));
for (int i = 0; i < 10; i++) {
client.execute(request1);
client.execute(request2);
}
} finally {
client.shutdownGracefully();
}
}
@Test
@Ignore
public void testSequentialRequests() throws Exception {
Client client = Client.builder().enableDebug().build();
try { try {
Request request1 = Request.get().url("https://google.com").build() Request request1 = Request.get().url("https://google.com").build()
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " + .setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
@ -79,4 +50,33 @@ public class SecureHttp1Test extends TestBase {
client.shutdown(); client.shutdown();
} }
} }
@Test
void testParallelRequests() throws IOException {
Client client = Client.builder()
.build();
try {
Request request1 = Request.builder(HttpMethod.GET)
.url("https://google.com").setVersion("HTTP/1.1")
.build()
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
msg.headers().entries() +
" status=" + msg.status().code()));
Request request2 = Request.builder(HttpMethod.GET)
.url("https://google.com").setVersion("HTTP/1.1")
.build()
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
msg.headers().entries() +
" status=" + msg.status().code()));
for (int i = 0; i < 10; i++) {
client.execute(request1);
client.execute(request2);
}
} finally {
client.shutdownGracefully();
}
}
} }

View file

@ -1,8 +1,9 @@
package org.xbib.netty.http.client.test; package org.xbib.netty.http.client.test;
import org.junit.After; import org.junit.jupiter.api.AfterAll;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.xbib.TestBase; import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.client.Client; import org.xbib.netty.http.client.Client;
import java.io.IOException; import java.io.IOException;
@ -10,18 +11,20 @@ import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
public class ThreadLeakTest extends TestBase { @ExtendWith(NettyHttpExtension.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class ThreadLeakTest {
private static final Logger logger = Logger.getLogger(ThreadLeakTest.class.getName()); private static final Logger logger = Logger.getLogger(ThreadLeakTest.class.getName());
@Test @Test
public void testForLeaks() throws IOException { void testForLeaks() throws IOException {
Client client = new Client(); Client client = new Client();
client.shutdownGracefully(); client.shutdownGracefully();
} }
@After @AfterAll
public void checkThreads() { void checkThreads() {
Set<Thread> threadSet = Thread.getAllStackTraces().keySet(); Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
logger.log(Level.INFO, "threads = " + threadSet.size() ); logger.log(Level.INFO, "threads = " + threadSet.size() );
threadSet.forEach( thread -> logger.log(Level.INFO, thread.toString())); threadSet.forEach( thread -> logger.log(Level.INFO, thread.toString()));

View file

@ -1,19 +1,17 @@
package org.xbib.netty.http.client.test; package org.xbib.netty.http.client.test;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.xbib.netty.http.client.Request; import org.xbib.netty.http.client.Request;
import org.xbib.netty.http.client.RequestBuilder; import org.xbib.netty.http.client.RequestBuilder;
import java.net.URI; import java.net.URI;
import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
/** class URITest {
*/
public class URITest {
@Test @Test
public void testURIResolve() { void testURIResolve() {
URI uri = URI.create("http://localhost"); URI uri = URI.create("http://localhost");
URI uri2 = uri.resolve("/path"); URI uri2 = uri.resolve("/path");
assertEquals("http://localhost/path", uri2.toString()); assertEquals("http://localhost/path", uri2.toString());
@ -23,7 +21,7 @@ public class URITest {
} }
@Test @Test
public void testRequestURIs() { void testRequestURIs() {
RequestBuilder httpRequestBuilder = Request.get(); RequestBuilder httpRequestBuilder = Request.get();
httpRequestBuilder.url("https://localhost").uri("/path"); httpRequestBuilder.url("https://localhost").uri("/path");
assertEquals("/path", httpRequestBuilder.build().relativeUri()); assertEquals("/path", httpRequestBuilder.build().relativeUri());

View file

@ -2,8 +2,7 @@ package org.xbib.netty.http.client.test;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.proxy.HttpProxyHandler; import io.netty.handler.proxy.HttpProxyHandler;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.xbib.TestBase;
import org.xbib.netty.http.client.Client; import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.Request; import org.xbib.netty.http.client.Request;
@ -15,12 +14,12 @@ import java.util.function.Function;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
public class XbibTest extends TestBase { class XbibTest {
private static final Logger logger = Logger.getLogger(""); private static final Logger logger = Logger.getLogger(XbibTest.class.getName());
@Test @Test
public void testXbibOrgWithDefaults() throws IOException { void testXbibOrgWithDefaults() throws IOException {
Client client = new Client(); Client client = new Client();
try { try {
Request request = Request.get().url("http://xbib.org") Request request = Request.get().url("http://xbib.org")
@ -36,7 +35,7 @@ public class XbibTest extends TestBase {
} }
@Test @Test
public void testXbibOrgWithCompletableFuture() throws IOException { void testXbibOrgWithCompletableFuture() throws IOException {
Client httpClient = Client.builder() Client httpClient = Client.builder()
.setTcpNodelay(true) .setTcpNodelay(true)
.build(); .build();
@ -66,7 +65,7 @@ public class XbibTest extends TestBase {
} }
@Test @Test
public void testXbibOrgWithProxy() throws IOException { void testXbibOrgWithProxy() throws IOException {
Client httpClient = Client.builder() Client httpClient = Client.builder()
.setHttpProxyHandler(new HttpProxyHandler(new InetSocketAddress("80.241.223.251", 8080))) .setHttpProxyHandler(new HttpProxyHandler(new InetSocketAddress("80.241.223.251", 8080)))
.setConnectTimeoutMillis(30000) .setConnectTimeoutMillis(30000)
@ -87,7 +86,7 @@ public class XbibTest extends TestBase {
} }
@Test @Test
public void testXbibOrgWithVeryShortReadTimeout() throws IOException { void testXbibOrgWithVeryShortReadTimeout() throws IOException {
Client httpClient = Client.builder() Client httpClient = Client.builder()
.build(); .build();
try { try {
@ -106,7 +105,7 @@ public class XbibTest extends TestBase {
} }
@Test @Test
public void testXbibTwoSequentialRequests() throws IOException { void testXbibTwoSequentialRequests() throws IOException {
Client httpClient = new Client(); Client httpClient = new Client();
try { try {
httpClient.execute(Request.get() httpClient.execute(Request.get()

View file

@ -1,4 +1,4 @@
package org.xbib.netty.http.hacks; package org.xbib.netty.http.client.test.hacks;
import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
@ -26,9 +26,7 @@ import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.SslProvider; import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.SupportedCipherSuiteFilter; import io.netty.handler.ssl.SupportedCipherSuiteFilter;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import org.junit.Ignore; import org.junit.jupiter.api.Test;
import org.junit.Test;
import org.xbib.TestBase;
import javax.net.ssl.SNIHostName; import javax.net.ssl.SNIHostName;
import javax.net.ssl.SNIServerName; import javax.net.ssl.SNIServerName;
@ -40,13 +38,12 @@ import java.util.concurrent.CompletableFuture;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@Ignore class Http2FramesTest {
public class Http2FramesTest extends TestBase {
private static final Logger logger = Logger.getLogger(Http2FramesTest.class.getName()); private static final Logger logger = Logger.getLogger(Http2FramesTest.class.getName());
@Test @Test
public void testHttp2Frames() throws Exception { void testHttp2Frames() throws Exception {
final InetSocketAddress inetSocketAddress = new InetSocketAddress("webtide.com", 443); final InetSocketAddress inetSocketAddress = new InetSocketAddress("webtide.com", 443);
CompletableFuture<Boolean> completableFuture = new CompletableFuture<>(); CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); EventLoopGroup eventLoopGroup = new NioEventLoopGroup();

View file

@ -1,4 +1,4 @@
package org.xbib.netty.http.hacks; package org.xbib.netty.http.client.test.hacks;
import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel; import io.netty.channel.Channel;
@ -19,10 +19,9 @@ import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import org.junit.After; import org.junit.jupiter.api.AfterAll;
import org.junit.Ignore; import org.junit.jupiter.api.Test;
import org.junit.Test; import org.junit.jupiter.api.TestInstance;
import org.xbib.TestBase;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
@ -40,8 +39,8 @@ import java.util.logging.LogManager;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.logging.SimpleFormatter; import java.util.logging.SimpleFormatter;
@Ignore @TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class SimpleHttp1Test extends TestBase { public class SimpleHttp1Test {
private static final Logger logger = Logger.getLogger(SimpleHttp1Test.class.getName()); private static final Logger logger = Logger.getLogger(SimpleHttp1Test.class.getName());
@ -62,7 +61,7 @@ public class SimpleHttp1Test extends TestBase {
} }
} }
@After @AfterAll
public void checkThreads() { public void checkThreads() {
Set<Thread> threadSet = Thread.getAllStackTraces().keySet(); Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
logger.log(Level.INFO, "threads = " + threadSet.size() ); logger.log(Level.INFO, "threads = " + threadSet.size() );
@ -74,7 +73,7 @@ public class SimpleHttp1Test extends TestBase {
} }
@Test @Test
public void testHttp1() throws Exception { void testHttp1() throws Exception {
Client client = new Client(); Client client = new Client();
try { try {
HttpTransport transport = client.newTransport("xbib.org", 80); HttpTransport transport = client.newTransport("xbib.org", 80);

View file

@ -1,4 +1,4 @@
package org.xbib.netty.http.hacks; package org.xbib.netty.http.client.test.hacks;
import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel; import io.netty.channel.Channel;
@ -35,9 +35,7 @@ import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.SupportedCipherSuiteFilter; import io.netty.handler.ssl.SupportedCipherSuiteFilter;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import org.junit.Ignore; import org.junit.jupiter.api.Test;
import org.junit.Test;
import org.xbib.TestBase;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import java.io.IOException; import java.io.IOException;
@ -53,13 +51,12 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@Ignore class SimpleHttp2Test {
public class SimpleHttp2Test extends TestBase {
private static final Logger logger = Logger.getLogger(SimpleHttp2Test.class.getName()); private static final Logger logger = Logger.getLogger(SimpleHttp2Test.class.getName());
@Test @Test
public void testHttp2WithUpgrade() throws Exception { void testHttp2WithUpgrade() throws Exception {
Client client = new Client(); Client client = new Client();
try { try {
Http2Transport transport = client.newTransport("webtide.com", 443); Http2Transport transport = client.newTransport("webtide.com", 443);

View file

@ -0,0 +1,17 @@
package org.xbib.netty.http.client.test.pool;
import io.netty.channel.epoll.Epoll;
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
public class DisableTestCondition implements ExecutionCondition {
@Override
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
if (Epoll.isAvailable()) {
return ConditionEvaluationResult.enabled("Test enabled");
} else {
return ConditionEvaluationResult.disabled("Test disabled");
}
}
}

View file

@ -0,0 +1,14 @@
package org.xbib.netty.http.client.test.pool;
import org.junit.jupiter.api.extension.ExtendWith;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(DisableTestCondition.class)
public @interface DisabledIfEpolllNotAvailable {
}

View file

@ -17,17 +17,14 @@ import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import org.junit.After; import org.junit.jupiter.api.AfterAll;
import org.junit.Before; import org.junit.jupiter.api.BeforeAll;
import org.junit.Ignore; import org.junit.jupiter.api.Test;
import org.junit.Test; import org.junit.jupiter.api.TestInstance;
import org.xbib.netty.http.common.HttpAddress; import org.xbib.netty.http.common.HttpAddress;
import org.xbib.netty.http.client.pool.Pool; import org.xbib.netty.http.client.pool.Pool;
import org.xbib.netty.http.client.pool.BoundedChannelPool; import org.xbib.netty.http.client.pool.BoundedChannelPool;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.Closeable; import java.io.Closeable;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -40,8 +37,12 @@ import java.util.concurrent.atomic.LongAdder;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@Ignore import static org.junit.jupiter.api.Assertions.assertEquals;
public class EpollTest { import static org.junit.jupiter.api.Assertions.assertTrue;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@DisabledIfEpolllNotAvailable
class EpollTest {
private static final Logger logger = Logger.getLogger(EpollTest.class.getName()); private static final Logger logger = Logger.getLogger(EpollTest.class.getName());
@ -64,8 +65,8 @@ public class EpollTest {
private EventLoopGroup eventLoopGroup; private EventLoopGroup eventLoopGroup;
@Before @BeforeAll
public void setUp() throws Exception { void setUp() throws Exception {
mockEpollServer = new MockEpollServer(12345, FAIL_EVERY_ATTEMPT); mockEpollServer = new MockEpollServer(12345, FAIL_EVERY_ATTEMPT);
Semaphore semaphore = new Semaphore(CONCURRENCY); Semaphore semaphore = new Semaphore(CONCURRENCY);
eventLoopGroup = new EpollEventLoopGroup(); eventLoopGroup = new EpollEventLoopGroup();
@ -86,15 +87,15 @@ public class EpollTest {
channelPool.prepare(CONCURRENCY); channelPool.prepare(CONCURRENCY);
} }
@After @AfterAll
public void tearDown() throws Exception { void tearDown() throws Exception {
channelPool.close(); channelPool.close();
eventLoopGroup.shutdownGracefully(); eventLoopGroup.shutdownGracefully();
mockEpollServer.close(); mockEpollServer.close();
} }
@Test @Test
public void testPoolEpoll() throws Exception { void testPoolEpoll() throws Exception {
LongAdder longAdder = new LongAdder(); LongAdder longAdder = new LongAdder();
ExecutorService executor = Executors.newFixedThreadPool(CONCURRENCY); ExecutorService executor = Executors.newFixedThreadPool(CONCURRENCY);
for(int i = 0; i < CONCURRENCY; i ++) { for(int i = 0; i < CONCURRENCY; i ++) {
@ -146,7 +147,7 @@ public class EpollTest {
private final AtomicLong reqCounter; private final AtomicLong reqCounter;
public MockEpollServer(int port, int dropEveryRequest) throws InterruptedException { MockEpollServer(int port, int dropEveryRequest) throws InterruptedException {
dispatchGroup = new EpollEventLoopGroup(); dispatchGroup = new EpollEventLoopGroup();
workerGroup = new EpollEventLoopGroup(); workerGroup = new EpollEventLoopGroup();
reqCounter = new AtomicLong(0); reqCounter = new AtomicLong(0);

View file

@ -16,9 +16,10 @@ import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import org.junit.After; import org.junit.jupiter.api.AfterAll;
import org.junit.Before; import org.junit.jupiter.api.BeforeAll;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.xbib.netty.http.common.HttpAddress; import org.xbib.netty.http.common.HttpAddress;
import org.xbib.netty.http.client.pool.Pool; import org.xbib.netty.http.client.pool.Pool;
import org.xbib.netty.http.client.pool.BoundedChannelPool; import org.xbib.netty.http.client.pool.BoundedChannelPool;
@ -35,10 +36,11 @@ import java.util.concurrent.atomic.LongAdder;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
public class NioTest { @TestInstance(TestInstance.Lifecycle.PER_CLASS)
class NioTest {
private static final Logger logger = Logger.getLogger(NioTest.class.getName()); private static final Logger logger = Logger.getLogger(NioTest.class.getName());
@ -61,8 +63,8 @@ public class NioTest {
private EventLoopGroup eventLoopGroup; private EventLoopGroup eventLoopGroup;
@Before @BeforeAll
public void setUp() throws Exception { void setUp() throws Exception {
mockNioServer = new MockNioServer(12345, FAIL_EVERY_ATTEMPT); mockNioServer = new MockNioServer(12345, FAIL_EVERY_ATTEMPT);
Semaphore semaphore = new Semaphore(CONCURRENCY); Semaphore semaphore = new Semaphore(CONCURRENCY);
eventLoopGroup = new NioEventLoopGroup(); eventLoopGroup = new NioEventLoopGroup();
@ -83,15 +85,15 @@ public class NioTest {
channelPool.prepare(CONCURRENCY); channelPool.prepare(CONCURRENCY);
} }
@After @AfterAll
public void tearDown() throws Exception { void tearDown() throws Exception {
channelPool.close(); channelPool.close();
eventLoopGroup.shutdownGracefully(); eventLoopGroup.shutdownGracefully();
mockNioServer.close(); mockNioServer.close();
} }
@Test @Test
public void testPoolNio() throws Exception { void testPoolNio() throws Exception {
LongAdder longAdder = new LongAdder(); LongAdder longAdder = new LongAdder();
ExecutorService executor = Executors.newFixedThreadPool(CONCURRENCY); ExecutorService executor = Executors.newFixedThreadPool(CONCURRENCY);
for(int i = 0; i < CONCURRENCY; i ++) { for(int i = 0; i < CONCURRENCY; i ++) {
@ -142,7 +144,7 @@ public class NioTest {
private final AtomicLong reqCounter; private final AtomicLong reqCounter;
public MockNioServer(int port, int dropEveryRequest) throws InterruptedException { MockNioServer(int port, int dropEveryRequest) throws InterruptedException {
dispatchGroup = new NioEventLoopGroup(); dispatchGroup = new NioEventLoopGroup();
workerGroup = new NioEventLoopGroup(); workerGroup = new NioEventLoopGroup();
reqCounter = new AtomicLong(0); reqCounter = new AtomicLong(0);

View file

@ -9,16 +9,13 @@ import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import org.junit.Test; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.runner.RunWith; import org.junit.jupiter.params.provider.ValueSource;
import org.junit.runners.Parameterized;
import org.xbib.netty.http.common.HttpAddress; import org.xbib.netty.http.common.HttpAddress;
import org.xbib.netty.http.client.pool.BoundedChannelPool; import org.xbib.netty.http.client.pool.BoundedChannelPool;
import org.xbib.netty.http.client.pool.Pool; import org.xbib.netty.http.client.pool.Pool;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@ -30,11 +27,10 @@ import java.util.concurrent.atomic.LongAdder;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
@RunWith(Parameterized.class) class PoolTest {
public class PoolTest {
private static final Logger logger = Logger.getLogger(PoolTest.class.getName()); private static final Logger logger = Logger.getLogger(PoolTest.class.getName());
@ -42,30 +38,11 @@ public class PoolTest {
private static final int BATCH_SIZE = 0x1000; private static final int BATCH_SIZE = 0x1000;
private int nodeCount; @ParameterizedTest
@ValueSource(ints = {1,10,100})
private ConcurrentMap<HttpAddress, LongAdder> nodeFreq = new ConcurrentHashMap<>(); void testPool(int concurrencyLevel) throws InterruptedException {
ConcurrentMap<HttpAddress, LongAdder> nodeFreq = new ConcurrentHashMap<>();
@Parameterized.Parameters int nodecount = 2;
public static Collection<Object[]> generateData() {
return Arrays.asList(new Object[][] {
{1, 1},
{10, 1},
{10, 2},
//{10, 5},
//{10, 10},
{100, 1},
{100, 2},
//{100, 5},
//{100, 10},
//{1000, 1},
//{1000, 2},
//{1000, 5},
//{1000, 10}
});
}
public PoolTest(int concurrencyLevel, int nodeCount) throws InterruptedException {
ServerBootstrap serverBootstrap = new ServerBootstrap() ServerBootstrap serverBootstrap = new ServerBootstrap()
.group(new NioEventLoopGroup()) .group(new NioEventLoopGroup())
@ -77,9 +54,8 @@ public class PoolTest {
}); });
Channel serverChannel = serverBootstrap.bind("localhost", 8008).sync().channel(); Channel serverChannel = serverBootstrap.bind("localhost", 8008).sync().channel();
this.nodeCount = nodeCount;
List<HttpAddress> nodes = new ArrayList<>(); List<HttpAddress> nodes = new ArrayList<>();
for (int i = 0; i < nodeCount; i ++) { for (int i = 0; i < nodecount; i ++) {
nodes.add(HttpAddress.http1("localhost", 8008)); nodes.add(HttpAddress.http1("localhost", 8008));
} }
try (Pool<Channel> pool = new BoundedChannelPool<>(new Semaphore(concurrencyLevel), HttpVersion.HTTP_1_1, try (Pool<Channel> pool = new BoundedChannelPool<>(new Semaphore(concurrencyLevel), HttpVersion.HTTP_1_1,
@ -127,25 +103,16 @@ public class PoolTest {
} finally { } finally {
serverChannel.close(); serverChannel.close();
long connCountSum = nodeFreq.values().stream().mapToLong(LongAdder::sum).sum(); long connCountSum = nodeFreq.values().stream().mapToLong(LongAdder::sum).sum();
logger.log(Level.INFO, "concurrency = " + concurrencyLevel + ", nodes = " + nodeCount + " -> rate: " + logger.log(Level.INFO, "concurrency = " + concurrencyLevel + ", nodes = " + nodecount + " -> rate: " +
connCountSum / TEST_STEP_TIME_SECONDS); connCountSum / TEST_STEP_TIME_SECONDS);
} long avgConnCountPerNode = connCountSum / 2;
}
@Test
public void testNodeFrequency() {
if (nodeCount > 1) {
long connCountSum = nodeFreq.values().stream().mapToLong(LongAdder::sum).sum();
long avgConnCountPerNode = connCountSum / nodeCount;
for (HttpAddress nodeAddr: nodeFreq.keySet()) { for (HttpAddress nodeAddr: nodeFreq.keySet()) {
assertTrue(nodeFreq.get(nodeAddr).sum() > 0); assertTrue(nodeFreq.get(nodeAddr).sum() > 0);
assertEquals("Node count: " + nodeCount + ", node: " + nodeAddr assertEquals(/*"Node count: " + nodeCount + ", node: " + nodeAddr
+ ", expected connection count: " + avgConnCountPerNode + ", actual: " + ", expected connection count: " + avgConnCountPerNode + ", actual: "
+ nodeFreq.get(nodeAddr).sum(), + nodeFreq.get(nodeAddr).sum(),*/
avgConnCountPerNode, nodeFreq.get(nodeAddr).sum(), 1.5 * avgConnCountPerNode); avgConnCountPerNode, nodeFreq.get(nodeAddr).sum(), 1.5 * avgConnCountPerNode);
} }
} else {
assertTrue(true);
} }
} }
} }

View file

@ -1,11 +1,12 @@
package org.xbib.netty.http.client.test.pool; package org.xbib.netty.http.client.test.pool;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.net.URL; import org.xbib.net.URL;
import org.xbib.netty.http.client.Client; import org.xbib.netty.http.client.Client;
import org.xbib.TestBase;
import org.xbib.netty.http.client.listener.ResponseListener; import org.xbib.netty.http.client.listener.ResponseListener;
import org.xbib.netty.http.client.test.NettyHttpExtension;
import org.xbib.netty.http.common.HttpAddress; import org.xbib.netty.http.common.HttpAddress;
import org.xbib.netty.http.client.Request; import org.xbib.netty.http.client.Request;
@ -18,12 +19,13 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
public class PooledClientTest extends TestBase { @ExtendWith(NettyHttpExtension.class)
class PooledClientTest {
private static final Logger logger = Logger.getLogger(""); private static final Logger logger = Logger.getLogger(PooledClientTest.class.getName());
@Test @Test
public void testPooledClientWithSingleNode() throws IOException { void testPooledClientWithSingleNode() throws IOException {
int loop = 10; int loop = 10;
int threads = Runtime.getRuntime().availableProcessors(); int threads = Runtime.getRuntime().availableProcessors();
URL url = URL.from("https://fl-test.hbz-nrw.de/app/fl"); URL url = URL.from("https://fl-test.hbz-nrw.de/app/fl");

View file

@ -1,17 +1,17 @@
package org.xbib.netty.http.client.test.rest; package org.xbib.netty.http.client.test.rest;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.xbib.netty.http.client.rest.RestClient; import org.xbib.netty.http.client.rest.RestClient;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Logger; import java.util.logging.Logger;
public class RestClientTest { class RestClientTest {
private static final Logger logger = Logger.getLogger(RestClientTest.class.getName()); private static final Logger logger = Logger.getLogger(RestClientTest.class.getName());
@Test @Test
public void testSimpleGet() throws IOException { void testSimpleGet() throws IOException {
String result = RestClient.get("http://xbib.org").asString(); String result = RestClient.get("http://xbib.org").asString();
logger.info(result); logger.info(result);
} }

View file

@ -1,19 +1,20 @@
package org.xbib.netty.http.client.test.retry; package org.xbib.netty.http.client.test.retry;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.xbib.netty.http.client.retry.BackOff; import org.xbib.netty.http.client.retry.BackOff;
import org.xbib.netty.http.client.retry.ExponentialBackOff; import org.xbib.netty.http.client.retry.ExponentialBackOff;
import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
/** /**
* Tests {@link ExponentialBackOff}. * Tests {@link ExponentialBackOff}.
*/ */
public class ExponentialBackOffTest { class ExponentialBackOffTest {
@Test @Test
public void testConstructor() { void testConstructor() {
ExponentialBackOff backOffPolicy = new ExponentialBackOff(); ExponentialBackOff backOffPolicy = new ExponentialBackOff();
assertEquals(ExponentialBackOff.DEFAULT_INITIAL_INTERVAL_MILLIS, assertEquals(ExponentialBackOff.DEFAULT_INITIAL_INTERVAL_MILLIS,
backOffPolicy.getInitialIntervalMillis()); backOffPolicy.getInitialIntervalMillis());
@ -29,7 +30,7 @@ public class ExponentialBackOffTest {
} }
@Test @Test
public void testBuilder() { void testBuilder() {
ExponentialBackOff backOffPolicy = new ExponentialBackOff.Builder().build(); ExponentialBackOff backOffPolicy = new ExponentialBackOff.Builder().build();
assertEquals(ExponentialBackOff.DEFAULT_INITIAL_INTERVAL_MILLIS, assertEquals(ExponentialBackOff.DEFAULT_INITIAL_INTERVAL_MILLIS,
backOffPolicy.getInitialIntervalMillis()); backOffPolicy.getInitialIntervalMillis());
@ -64,7 +65,7 @@ public class ExponentialBackOffTest {
} }
@Test @Test
public void testBackOff() { void testBackOff() {
int testInitialInterval = 500; int testInitialInterval = 500;
double testRandomizationFactor = 0.1; double testRandomizationFactor = 0.1;
double testMultiplier = 2.0; double testMultiplier = 2.0;
@ -90,7 +91,7 @@ public class ExponentialBackOffTest {
} }
@Test @Test
public void testGetRandomizedInterval() { void testGetRandomizedInterval() {
// 33% chance of being 1. // 33% chance of being 1.
assertEquals(1, ExponentialBackOff.getRandomValueFromInterval(0.5, 0, 2)); assertEquals(1, ExponentialBackOff.getRandomValueFromInterval(0.5, 0, 2));
assertEquals(1, ExponentialBackOff.getRandomValueFromInterval(0.5, 0.33, 2)); assertEquals(1, ExponentialBackOff.getRandomValueFromInterval(0.5, 0.33, 2));
@ -103,14 +104,14 @@ public class ExponentialBackOffTest {
} }
@Test @Test
public void testGetElapsedTimeMillis() { void testGetElapsedTimeMillis() {
ExponentialBackOff backOffPolicy = new ExponentialBackOff.Builder().setNanoClock(new MyNanoClock()).build(); ExponentialBackOff backOffPolicy = new ExponentialBackOff.Builder().setNanoClock(new MyNanoClock()).build();
long elapsedTimeMillis = backOffPolicy.getElapsedTimeMillis(); long elapsedTimeMillis = backOffPolicy.getElapsedTimeMillis();
assertEquals("elapsedTimeMillis=" + elapsedTimeMillis, 1000, elapsedTimeMillis); assertEquals(1000, elapsedTimeMillis);
} }
@Test @Test
public void testMaxElapsedTime() { void testMaxElapsedTime() {
ExponentialBackOff backOffPolicy = ExponentialBackOff backOffPolicy =
new ExponentialBackOff.Builder().setNanoClock(new MyNanoClock(10000)).build(); new ExponentialBackOff.Builder().setNanoClock(new MyNanoClock(10000)).build();
assertTrue(backOffPolicy.nextBackOffMillis() != BackOff.STOP); assertTrue(backOffPolicy.nextBackOffMillis() != BackOff.STOP);
@ -121,7 +122,7 @@ public class ExponentialBackOffTest {
} }
@Test @Test
public void testBackOffOverflow() { void testBackOffOverflow() {
int testInitialInterval = Integer.MAX_VALUE / 2; int testInitialInterval = Integer.MAX_VALUE / 2;
double testMultiplier = 2.1; double testMultiplier = 2.1;
int testMaxInterval = Integer.MAX_VALUE; int testMaxInterval = Integer.MAX_VALUE;

View file

@ -1,19 +1,19 @@
package org.xbib.netty.http.client.test.retry; package org.xbib.netty.http.client.test.retry;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.xbib.netty.http.client.retry.BackOff; import org.xbib.netty.http.client.retry.BackOff;
import java.io.IOException; import java.io.IOException;
import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
/** /**
* Tests {@link MockBackOff}. * Tests {@link MockBackOff}.
*/ */
public class MockBackOffTest { class MockBackOffTest {
@Test @Test
public void testNextBackOffMillis() throws IOException { void testNextBackOffMillis() throws IOException {
subtestNextBackOffMillis(0, new MockBackOff()); subtestNextBackOffMillis(0, new MockBackOff());
subtestNextBackOffMillis(BackOff.STOP, new MockBackOff().setBackOffMillis(BackOff.STOP)); subtestNextBackOffMillis(BackOff.STOP, new MockBackOff().setBackOffMillis(BackOff.STOP));
subtestNextBackOffMillis(42, new MockBackOff().setBackOffMillis(42)); subtestNextBackOffMillis(42, new MockBackOff().setBackOffMillis(42));

View file

@ -1,5 +1,5 @@
dependencies { dependencies {
compile "org.xbib:net-url:${project.property('xbib-net-url.version')}" implementation "org.xbib:net-url:${project.property('xbib-net-url.version')}"
compile "io.netty:netty-codec-http2:${project.property('netty.version')}" implementation "io.netty:netty-codec-http2:${project.property('netty.version')}"
} }

View file

@ -0,0 +1,6 @@
dependencies {
implementation project(":netty-http-server")
implementation "io.netty:netty-codec-http2:${project.property('netty.version')}"
implementation "org.xbib:net-url:${project.property('xbib-net-url.version')}"
implementation "org.xbib:guice:${project.property('xbib-guice.version')}"
}

View file

@ -0,0 +1,4 @@
package org.xbib.netty.http.server.rest;
public class Rest {
}

View file

@ -0,0 +1,4 @@
package org.xbib.netty.http.server.rest;
public class RestBuilder {
}

View file

@ -0,0 +1,4 @@
package org.xbib.netty.http.server.rest;
public class RestConfig {
}

View file

@ -0,0 +1,44 @@
package org.xbib.netty.http.server.rest;
import io.netty.bootstrap.Bootstrap;
import java.util.Optional;
/**
* Server name.
*/
public final class RestName {
/**
* The default value for {@code Server} header.
*/
private static final String SERVER_NAME = String.format("RestServer/%s (Java/%s/%s) (Netty/%s)",
httpServerVersion(), javaVendor(), javaVersion(), nettyVersion());
private RestName() {
}
public static String getServerName() {
return SERVER_NAME;
}
private static String httpServerVersion() {
return Optional.ofNullable(Rest.class.getPackage().getImplementationVersion())
.orElse("unknown");
}
private static String javaVendor() {
return Optional.ofNullable(System.getProperty("java.vendor"))
.orElse("unknown");
}
private static String javaVersion() {
return Optional.ofNullable(System.getProperty("java.version"))
.orElse("unknown");
}
private static String nettyVersion() {
return Optional.ofNullable(Bootstrap.class.getPackage().getImplementationVersion())
.orElse("unknown");
}
}

View file

@ -1,10 +1,10 @@
dependencies { dependencies {
compile project(":netty-http-common") implementation project(":netty-http-common")
compile "io.netty:netty-handler:${project.property('netty.version')}" implementation "io.netty:netty-handler:${project.property('netty.version')}"
compile "io.netty:netty-transport-native-epoll:${project.property('netty.version')}" implementation "io.netty:netty-transport-native-epoll:${project.property('netty.version')}"
compile "io.netty:netty-tcnative-boringssl-static:${project.property('tcnative.version')}" implementation "io.netty:netty-tcnative-boringssl-static:${project.property('tcnative.version')}"
compile "org.bouncycastle:bcpkix-jdk15on:${project.property('bouncycastle.version')}" implementation "io.netty:netty-codec-http2:${project.property('netty.version')}"
implementation "org.xbib:net-url:${project.property('xbib-net-url.version')}"
testCompile project(":netty-http-client") implementation "org.bouncycastle:bcpkix-jdk15on:${project.property('bouncycastle.version')}"
testCompile "junit:junit:${project.property('junit.version')}" testImplementation project(":netty-http-client")
} }

View file

@ -21,7 +21,7 @@ import io.netty.handler.ssl.SslContextBuilder;
import io.netty.util.DomainNameMapping; import io.netty.util.DomainNameMapping;
import io.netty.util.DomainNameMappingBuilder; import io.netty.util.DomainNameMappingBuilder;
import org.xbib.netty.http.common.HttpAddress; import org.xbib.netty.http.common.HttpAddress;
import org.xbib.netty.http.server.context.VirtualServer; import org.xbib.netty.http.server.endpoint.NamedServer;
import org.xbib.netty.http.server.handler.http.HttpChannelInitializer; import org.xbib.netty.http.server.handler.http.HttpChannelInitializer;
import org.xbib.netty.http.server.handler.http2.Http2ChannelInitializer; import org.xbib.netty.http.server.handler.http2.Http2ChannelInitializer;
import org.xbib.netty.http.server.transport.HttpServerTransport; import org.xbib.netty.http.server.transport.HttpServerTransport;
@ -73,7 +73,7 @@ public final class Server {
private final ServerBootstrap bootstrap; private final ServerBootstrap bootstrap;
private final Map<String, VirtualServer> virtualServerMap; private final Map<String, NamedServer> virtualServerMap;
private ChannelFuture channelFuture; private ChannelFuture channelFuture;
@ -118,11 +118,11 @@ public final class Server {
bootstrap.handler(new LoggingHandler("bootstrap-server", serverConfig.getDebugLogLevel())); bootstrap.handler(new LoggingHandler("bootstrap-server", serverConfig.getDebugLogLevel()));
} }
this.virtualServerMap = new HashMap<>(); this.virtualServerMap = new HashMap<>();
for (VirtualServer virtualServer : serverConfig.getVirtualServers()) { for (NamedServer namedServer : serverConfig.getNamedServers()) {
String name = virtualServer.getName(); String name = namedServer.getName();
virtualServerMap.put(name, virtualServer); virtualServerMap.put(name, namedServer);
for (String alias : virtualServer.getAliases()) { for (String alias : namedServer.getAliases()) {
virtualServerMap.put(alias, virtualServer); virtualServerMap.put(alias, namedServer);
} }
} }
DomainNameMapping<SslContext> domainNameMapping = null; DomainNameMapping<SslContext> domainNameMapping = null;
@ -136,8 +136,8 @@ public final class Server {
} }
SslContext sslContext = sslContextBuilder.build(); SslContext sslContext = sslContextBuilder.build();
DomainNameMappingBuilder<SslContext> mappingBuilder = new DomainNameMappingBuilder<>(sslContext); DomainNameMappingBuilder<SslContext> mappingBuilder = new DomainNameMappingBuilder<>(sslContext);
for (VirtualServer virtualServer : serverConfig.getVirtualServers()) { for (NamedServer namedServer : serverConfig.getNamedServers()) {
String name = virtualServer.getName(); String name = namedServer.getName();
mappingBuilder.add(name == null ? "*" : name, sslContext); mappingBuilder.add(name == null ? "*" : name, sslContext);
} }
domainNameMapping = mappingBuilder.build(); domainNameMapping = mappingBuilder.build();
@ -169,11 +169,11 @@ public final class Server {
* the default virtual host * the default virtual host
* @return the virtual host with the given name, or null if it doesn't exist * @return the virtual host with the given name, or null if it doesn't exist
*/ */
public VirtualServer getVirtualServer(String name) { public NamedServer getVirtualServer(String name) {
return virtualServerMap.get(name); return virtualServerMap.get(name);
} }
public VirtualServer getDefaultVirtualServer() { public NamedServer getDefaultVirtualServer() {
return virtualServerMap.get(null); return virtualServerMap.get(null);
} }

View file

@ -8,7 +8,8 @@ import io.netty.handler.ssl.CipherSuiteFilter;
import io.netty.handler.ssl.SslProvider; import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import org.xbib.netty.http.common.HttpAddress; import org.xbib.netty.http.common.HttpAddress;
import org.xbib.netty.http.server.context.VirtualServer; import org.xbib.netty.http.server.endpoint.Handler;
import org.xbib.netty.http.server.endpoint.NamedServer;
import org.xbib.netty.http.server.security.tls.SelfSignedCertificate; import org.xbib.netty.http.server.security.tls.SelfSignedCertificate;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
@ -224,8 +225,16 @@ public class ServerBuilder {
return this; return this;
} }
public ServerBuilder addVirtualServer(VirtualServer virtualServer) { public ServerBuilder addServer(NamedServer namedServer) {
this.serverConfig.addVirtualServer(virtualServer); this.serverConfig.add(namedServer);
return this;
}
public ServerBuilder addHandler(String path, Handler handler, String... methods) {
if (serverConfig.getNamedServers().isEmpty()) {
serverConfig.add(new NamedServer());
}
serverConfig.getNamedServers().getLast().addHandler(path, handler, methods);
return this; return this;
} }

View file

@ -10,12 +10,14 @@ import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.SslProvider; import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.SupportedCipherSuiteFilter; import io.netty.handler.ssl.SupportedCipherSuiteFilter;
import org.xbib.netty.http.common.HttpAddress; import org.xbib.netty.http.common.HttpAddress;
import org.xbib.netty.http.server.context.VirtualServer; import org.xbib.netty.http.server.endpoint.NamedServer;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import java.io.InputStream; import java.io.InputStream;
import java.security.KeyStore; import java.security.KeyStore;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List; import java.util.List;
public class ServerConfig { public class ServerConfig {
@ -117,7 +119,7 @@ public class ServerConfig {
/** /**
* This is Netty's default. * This is Netty's default.
* See {@link io.netty.handler.codec.MessageAggregator#DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS}. * See {@link io.netty.handler.codec.MessageAggregator#maxCumulationBufferComponents()}.
*/ */
int MAX_COMPOSITE_BUFFER_COMPONENTS = 1024; int MAX_COMPOSITE_BUFFER_COMPONENTS = 1024;
@ -225,15 +227,15 @@ public class ServerConfig {
private String keyPassword; private String keyPassword;
private List<VirtualServer> virtualServers; private Deque<NamedServer> namedServers;
private TrustManagerFactory trustManagerFactory = TRUST_MANAGER_FACTORY; private TrustManagerFactory trustManagerFactory = TRUST_MANAGER_FACTORY;
private KeyStore trustManagerKeyStore = null; private KeyStore trustManagerKeyStore = null;
public ServerConfig() { public ServerConfig() {
this.virtualServers = new ArrayList<>(); this.namedServers = new LinkedList<>();
addVirtualServer(new VirtualServer(null)); add(new NamedServer(null));
} }
public ServerConfig enableDebug() { public ServerConfig enableDebug() {
@ -512,13 +514,13 @@ public class ServerConfig {
return keyPassword; return keyPassword;
} }
public ServerConfig addVirtualServer(VirtualServer virtualServer) { public ServerConfig add(NamedServer namedServer) {
this.virtualServers.add(virtualServer); this.namedServers.add(namedServer);
return this; return this;
} }
public List<VirtualServer> getVirtualServers() { public Deque<NamedServer> getNamedServers() {
return virtualServers; return namedServers;
} }
public ServerConfig setTrustManagerFactory(TrustManagerFactory trustManagerFactory) { public ServerConfig setTrustManagerFactory(TrustManagerFactory trustManagerFactory) {

View file

@ -12,7 +12,7 @@ public final class ServerName {
/** /**
* The default value for {@code Server} header. * The default value for {@code Server} header.
*/ */
private static final String SERVER_NAME = String.format("XbibHttpServer/%s (Java/%s/%s) (Netty/%s)", private static final String SERVER_NAME = String.format("NettyHttpServer/%s (Java/%s/%s) (Netty/%s)",
httpServerVersion(), javaVendor(), javaVersion(), nettyVersion()); httpServerVersion(), javaVendor(), javaVersion(), nettyVersion());
private ServerName() { private ServerName() {

View file

@ -1,24 +0,0 @@
package org.xbib.netty.http.server.context;
import org.xbib.netty.http.server.transport.ServerRequest;
import org.xbib.netty.http.server.transport.ServerResponse;
import java.io.IOException;
/**
* A {@code ContextHandler} is capable of serving content for resources within its context.
*
* @see VirtualServer#addContext
*/
@FunctionalInterface
public interface ContextHandler {
/**
* Serves the given request using the given response.
*
* @param serverRequest the request to be served
* @param serverResponse the response to be filled
* @throws IOException if an IO error occurs
*/
void serve(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException;
}

View file

@ -1,46 +0,0 @@
package org.xbib.netty.http.server.context;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* The {@code ContextInfo} class holds a single context's information.
*/
public class ContextInfo {
private final VirtualServer virtualServer;
private final Map<String, ContextHandler> methodHandlerMap;
public ContextInfo(VirtualServer virtualServer) {
this.virtualServer = virtualServer;
this.methodHandlerMap = new LinkedHashMap<>();
}
/**
* Returns the map of supported HTTP methods and their corresponding handlers.
*
* @return the map of supported HTTP methods and their corresponding handlers
*/
public Map<String, ContextHandler> getMethodHandlerMap() {
return methodHandlerMap;
}
/**
* Adds (or replaces) a context handler for the given HTTP methods.
*
* @param handler the context handler
* @param methods the HTTP methods supported by the handler (default is "GET")
*/
public void addHandler(ContextHandler handler, String... methods) {
if (methods.length == 0) {
methodHandlerMap.put("GET", handler);
virtualServer.getMethods().add("GET");
} else {
for (String method : methods) {
methodHandlerMap.put(method, handler);
virtualServer.getMethods().add(method);
}
}
}
}

View file

@ -1,4 +1,4 @@
package org.xbib.netty.http.server.context; package org.xbib.netty.http.server.endpoint;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
@ -16,19 +16,19 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
public class ClasspathContextHandler implements ContextHandler { public class ClasspathHandler implements Handler {
private final ClassLoader classLoader; private final ClassLoader classLoader;
private final String prefix; private final String prefix;
public ClasspathContextHandler(ClassLoader classLoader, String prefix) { public ClasspathHandler(ClassLoader classLoader, String prefix) {
this.classLoader = classLoader; this.classLoader = classLoader;
this.prefix = prefix; this.prefix = prefix;
} }
@Override @Override
public void serve(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException { public void handle(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
String contextPath = serverRequest.getContextPath(); String contextPath = serverRequest.getContextPath();
URL url = classLoader.getResource(prefix + contextPath); URL url = classLoader.getResource(prefix + contextPath);
if (url != null) { if (url != null) {

View file

@ -1,4 +1,4 @@
package org.xbib.netty.http.server.context; package org.xbib.netty.http.server.endpoint;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@ -7,11 +7,11 @@ import java.lang.annotation.Target;
/** /**
* The {@code Context} annotation decorates methods which are mapped * The {@code Context} annotation decorates methods which are mapped
* to a context (path) within the server, and provide its contents. * to a context path within the server, and provide its contents.
* The annotated methods must have the same signature and contract * The annotated methods must have the same signature and contract
* as {@link ContextHandler#serve}, but can have arbitrary names. * as {@link Handler#handle}, but can have arbitrary names.
* *
* @see VirtualServer#addContexts(Object) * @see NamedServer#addHandlers(Object)
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) @Target(ElementType.METHOD)

View file

@ -1,4 +1,4 @@
package org.xbib.netty.http.server.context; package org.xbib.netty.http.server.endpoint;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufAllocator;
@ -13,19 +13,19 @@ import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
public class DirectoryContextHandler implements ContextHandler { public class DirectoryHandler implements Handler {
private Path path; private Path path;
private ByteBufAllocator allocator; private ByteBufAllocator allocator;
public DirectoryContextHandler(Path path, ByteBufAllocator allocator) { public DirectoryHandler(Path path, ByteBufAllocator allocator) {
this.path = path; this.path = path;
this.allocator = allocator; this.allocator = allocator;
} }
@Override @Override
public void serve(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException { public void handle(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
String uri = serverRequest.getRequest().uri(); String uri = serverRequest.getRequest().uri();
Path p = path.resolve(uri); Path p = path.resolve(uri);
ByteBuf byteBuf = read(allocator, p); ByteBuf byteBuf = read(allocator, p);

View file

@ -0,0 +1,46 @@
package org.xbib.netty.http.server.endpoint;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* The {@code Endpoint} class holds an endpoint information.
*/
public class Endpoint {
private final NamedServer namedServer;
private final Map<String, Handler> handlerMap;
public Endpoint(NamedServer namedServer) {
this.namedServer = namedServer;
this.handlerMap = new LinkedHashMap<>();
}
/**
* Returns the map of supported HTTP methods and their corresponding handlers.
*
* @return the map of supported HTTP methods and their corresponding handlers
*/
public Map<String, Handler> getHandlerMap() {
return handlerMap;
}
/**
* Adds (or replaces) a handler for the given HTTP methods.
*
* @param handler the handler
* @param methods the HTTP methods supported by the handler (default is "GET")
*/
public void addHandler(Handler handler, String... methods) {
if (methods.length == 0) {
handlerMap.put("GET", handler);
namedServer.getMethods().add("GET");
} else {
for (String method : methods) {
handlerMap.put(method, handler);
namedServer.getMethods().add(method);
}
}
}
}

View file

@ -0,0 +1,24 @@
package org.xbib.netty.http.server.endpoint;
import org.xbib.netty.http.server.transport.ServerRequest;
import org.xbib.netty.http.server.transport.ServerResponse;
import java.io.IOException;
/**
* A {@code Handler} is capable of serving content for resources within its context.
*
* @see NamedServer#addHandler
*/
@FunctionalInterface
public interface Handler {
/**
* Handles the given request by using the given response.
*
* @param serverRequest the request to be served
* @param serverResponse the response to be generated
* @throws IOException if an IO error occurs
*/
void handle(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException;
}

View file

@ -1,4 +1,4 @@
package org.xbib.netty.http.server.context; package org.xbib.netty.http.server.endpoint;
import org.xbib.netty.http.server.transport.ServerRequest; import org.xbib.netty.http.server.transport.ServerRequest;
import org.xbib.netty.http.server.transport.ServerResponse; import org.xbib.netty.http.server.transport.ServerResponse;
@ -8,20 +8,19 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
/** /**
* The {@code MethodContextHandler} services a context * The {@code MethodHandler} invokes g a handler method on a specified object.
* by invoking a handler method on a specified object.
* The method must have the same signature and contract as * The method must have the same signature and contract as
* {@link ContextHandler#serve}, but can have an arbitrary name. * {@link Handler#handle}, but can have an arbitrary name.
* *
* @see VirtualServer#addContexts(Object) * @see NamedServer#addHandlers(Object)
*/ */
public class MethodContextHandler implements ContextHandler { public class MethodHandler implements Handler {
private final Method m; private final Method m;
private final Object obj; private final Object obj;
public MethodContextHandler(Method m, Object obj) throws IllegalArgumentException { public MethodHandler(Method m, Object obj) throws IllegalArgumentException {
this.m = m; this.m = m;
this.obj = obj; this.obj = obj;
Class<?>[] params = m.getParameterTypes(); Class<?>[] params = m.getParameterTypes();
@ -34,7 +33,7 @@ public class MethodContextHandler implements ContextHandler {
} }
@Override @Override
public void serve(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException { public void handle(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
try { try {
m.invoke(obj, serverRequest, serverResponse); m.invoke(obj, serverRequest, serverResponse);
} catch (InvocationTargetException ite) { } catch (InvocationTargetException ite) {

View file

@ -0,0 +1,21 @@
package org.xbib.netty.http.server.endpoint;
public class NamedEndpoint {
private final String name;
private final Endpoint endpoint;
NamedEndpoint(String name, Endpoint endpoint) {
this.name = name;
this.endpoint = endpoint;
}
public String getName() {
return name;
}
public Endpoint getEndpoint() {
return endpoint;
}
}

View file

@ -1,4 +1,4 @@
package org.xbib.netty.http.server.context; package org.xbib.netty.http.server.endpoint;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collections; import java.util.Collections;
@ -10,7 +10,7 @@ import java.util.Set;
/** /**
* The {@code VirtualServer} class represents a virtual server. * The {@code VirtualServer} class represents a virtual server.
*/ */
public class VirtualServer { public class NamedServer {
private final String name; private final String name;
@ -18,28 +18,28 @@ public class VirtualServer {
private final Set<String> methods; private final Set<String> methods;
private final ContextInfo emptyContext; private final Endpoint defaultEndpoint;
private final Map<String, ContextInfo> contexts; private final Map<String, Endpoint> endpointMap;
private volatile boolean allowGeneratedIndex; private volatile boolean allowGeneratedIndex;
public VirtualServer() { public NamedServer() {
this(null); this(null);
} }
/** /**
* Constructs a VirtualServer with the given name. * Constructs a {@code NamedServer} with the given name.
* *
* @param name the name, or null if it is the default server * @param name the name, or null if it is the default server
*/ */
public VirtualServer(String name) { public NamedServer(String name) {
this.name = name; this.name = name;
this.aliases = new HashSet<>(); this.aliases = new HashSet<>();
this.methods = new HashSet<>(); this.methods = new HashSet<>();
this.contexts = new HashMap<>(); this.endpointMap = new HashMap<>();
this.emptyContext = new ContextInfo(this); this.defaultEndpoint = new Endpoint(this);
contexts.put("*", new ContextInfo(this)); // for "OPTIONS *" endpointMap.put("*", new Endpoint(this)); // for "OPTIONS *"
} }
/** /**
@ -105,34 +105,36 @@ public class VirtualServer {
* @param path the context's path (must start with '/') * @param path the context's path (must start with '/')
* @param handler the context handler for the given path * @param handler the context handler for the given path
* @param methods the HTTP methods supported by the context handler (default is "GET") * @param methods the HTTP methods supported by the context handler (default is "GET")
* @return this virtual server
* @throws IllegalArgumentException if path is malformed * @throws IllegalArgumentException if path is malformed
*/ */
public VirtualServer addContext(String path, ContextHandler handler, String... methods) { public NamedServer addHandler(String path, Handler handler, String... methods) {
if (path == null || !path.startsWith("/") && !path.equals("*")) { if (path == null || !path.startsWith("/") && !path.equals("*")) {
throw new IllegalArgumentException("invalid path: " + path); throw new IllegalArgumentException("invalid path: " + path);
} }
String s = trimRight(path, '/'); String s = trimRight(path, '/');
ContextInfo info = new ContextInfo(this); Endpoint info = new Endpoint(this);
ContextInfo existing = contexts.putIfAbsent(s, info); Endpoint existing = endpointMap.putIfAbsent(s, info);
info = existing != null ? existing : info; info = existing != null ? existing : info;
info.addHandler(handler, methods); info.addHandler(handler, methods);
return this; return this;
} }
/** /**
* Adds contexts for all methods of the given object that * Adds handler for all methods of the given object that
* are annotated with the {@link Context} annotation. * are annotated with the {@link Context} annotation.
* *
* @param o the object whose annotated methods are added * @param o the object whose annotated methods are added
* @return this virtual server
* @throws IllegalArgumentException if a Context-annotated * @throws IllegalArgumentException if a Context-annotated
* method has an {@link Context invalid signature} * method has an {@link Context invalid signature}
*/ */
public VirtualServer addContexts(Object o) throws IllegalArgumentException { public NamedServer addHandlers(Object o) throws IllegalArgumentException {
for (Class<?> c = o.getClass(); c != null; c = c.getSuperclass()) { for (Class<?> c = o.getClass(); c != null; c = c.getSuperclass()) {
for (Method m : c.getDeclaredMethods()) { for (Method m : c.getDeclaredMethods()) {
Context context = m.getAnnotation(Context.class); Context context = m.getAnnotation(Context.class);
if (context != null) { if (context != null) {
addContext(context.value(), new MethodContextHandler(m, o), context.methods()); addHandler(context.value(), new MethodHandler(m, o), context.methods());
} }
} }
} }
@ -140,24 +142,24 @@ public class VirtualServer {
} }
/** /**
* Returns the context handler for the given path. * Returns the endpoint for the given path.
* If a context is not found for the given path, the search is repeated for * If an endpoint is not found for the given path, the search is repeated for
* its parent path, and so on until a base context is found. If neither the * its parent path, and so on until a base context is found. If neither the
* given path nor any of its parents has a context, an empty context is returned. * given path nor any of its parents has a context, an empty context is returned.
* *
* @param path the context's path * @param path the context's path
* @return the context info for the given path, or an empty context if none exists * @return the context info for the given path, or an empty context if none exists
*/ */
public ContextPath getContextPath(String path) { public NamedEndpoint getNamedEndpoint(String path) {
String s = trimRight(path, '/'); String s = trimRight(path, '/');
ContextInfo info = null; Endpoint info = null;
String hook = null; String hook = null;
while (info == null && s != null) { while (info == null && s != null) {
hook = s; hook = s;
info = contexts.get(s); info = endpointMap.get(s);
s = getParentPath(s); s = getParentPath(s);
} }
return new ContextPath(hook, info != null ? info : emptyContext); return new NamedEndpoint(hook, info != null ? info : defaultEndpoint);
} }
/** /**
@ -190,24 +192,4 @@ public class VirtualServer {
return slash == -1 ? null : s.substring(0, slash); return slash == -1 ? null : s.substring(0, slash);
} }
public class ContextPath {
private final String hook;
private final ContextInfo contextInfo;
ContextPath(String hook, ContextInfo contextInfo) {
this.hook = hook;
this.contextInfo = contextInfo;
}
public String getHook() {
return hook;
}
public ContextInfo getContextInfo() {
return contextInfo;
}
}
} }

View file

@ -1,4 +1,4 @@
package org.xbib.netty.http.server.context; package org.xbib.netty.http.server.endpoint;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
@ -13,11 +13,11 @@ import java.nio.channels.FileChannel;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
public class NioContextHandler implements ContextHandler { public class NioHandler implements Handler {
private final Path prefix; private final Path prefix;
public NioContextHandler(Path prefix) { public NioHandler(Path prefix) {
this.prefix = prefix; this.prefix = prefix;
if (!Files.exists(prefix) || !Files.isDirectory(prefix)) { if (!Files.exists(prefix) || !Files.isDirectory(prefix)) {
throw new IllegalArgumentException("prefix: " + prefix + " (not a directory"); throw new IllegalArgumentException("prefix: " + prefix + " (not a directory");
@ -25,7 +25,7 @@ public class NioContextHandler implements ContextHandler {
} }
@Override @Override
public void serve(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException { public void handle(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
String requestPath = serverRequest.getRequestPath(); String requestPath = serverRequest.getRequestPath();
Path path = prefix.resolve(requestPath.substring(1)); // starts always with '/' Path path = prefix.resolve(requestPath.substring(1)); // starts always with '/'
if (Files.exists(path) && Files.isReadable(path)) { if (Files.exists(path) && Files.isReadable(path)) {

View file

@ -6,8 +6,9 @@ import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import org.xbib.netty.http.server.Server; import org.xbib.netty.http.server.Server;
import org.xbib.netty.http.server.context.ContextHandler; import org.xbib.netty.http.server.endpoint.Handler;
import org.xbib.netty.http.server.context.VirtualServer; import org.xbib.netty.http.server.endpoint.NamedEndpoint;
import org.xbib.netty.http.server.endpoint.NamedServer;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
@ -88,28 +89,28 @@ abstract class BaseServerTransport implements ServerTransport {
protected static void handle(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException { protected static void handle(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
String method = serverRequest.getRequest().method().name(); String method = serverRequest.getRequest().method().name();
String path = serverRequest.getRequest().uri(); String path = serverRequest.getRequest().uri();
VirtualServer virtualServer = serverRequest.getVirtualServer(); NamedServer namedServer = serverRequest.getNamedServer();
VirtualServer.ContextPath contextPath = virtualServer.getContextPath(path); NamedEndpoint namedEndpoint = namedServer.getNamedEndpoint(path);
serverRequest.setContextPath(contextPath.getHook()); serverRequest.setContextPath(namedEndpoint.getName());
Map<String, ContextHandler> methodHandlerMap = contextPath.getContextInfo().getMethodHandlerMap(); Map<String, Handler> methodHandlerMap = namedEndpoint.getEndpoint().getHandlerMap();
// RFC 2616#5.1.1 - GET and HEAD must be supported // RFC 2616#5.1.1 - GET and HEAD must be supported
if (method.equals("GET") || method.equals("HEAD") || methodHandlerMap.containsKey(method)) { if (method.equals("GET") || method.equals("HEAD") || methodHandlerMap.containsKey(method)) {
ContextHandler handler = methodHandlerMap.get(method); Handler handler = methodHandlerMap.get(method);
if (handler == null) { if (handler == null) {
serverResponse.writeError(HttpResponseStatus.NOT_FOUND); serverResponse.writeError(HttpResponseStatus.NOT_FOUND);
} else { } else {
handler.serve(serverRequest, serverResponse); handler.handle(serverRequest, serverResponse);
} }
} else { } else {
Set<String> methods = new LinkedHashSet<>(METHODS); Set<String> methods = new LinkedHashSet<>(METHODS);
// "*" is a special server-wide (no-context) request supported by OPTIONS // "*" is a special server-wide (no-context) request supported by OPTIONS
boolean isServerOptions = path.equals("*") && method.equals("OPTIONS"); boolean isServerOptions = path.equals("*") && method.equals("OPTIONS");
methods.addAll(isServerOptions ? virtualServer.getMethods() : methodHandlerMap.keySet()); methods.addAll(isServerOptions ? namedServer.getMethods() : methodHandlerMap.keySet());
serverResponse.setHeader(HttpHeaderNames.ALLOW, String.join(", ", methods)); serverResponse.setHeader(HttpHeaderNames.ALLOW, String.join(", ", methods));
if (method.equals("OPTIONS")) { // default OPTIONS handler if (method.equals("OPTIONS")) { // default OPTIONS handler
serverResponse.setHeader(HttpHeaderNames.CONTENT_LENGTH, "0"); // RFC2616#9.2 serverResponse.setHeader(HttpHeaderNames.CONTENT_LENGTH, "0"); // RFC2616#9.2
serverResponse.write(HttpResponseStatus.OK); serverResponse.write(HttpResponseStatus.OK);
} else if (virtualServer.getMethods().contains(method)) { } else if (namedServer.getMethods().contains(method)) {
serverResponse.write(HttpResponseStatus.METHOD_NOT_ALLOWED); // supported by server, but not this context (nor built-in) serverResponse.write(HttpResponseStatus.METHOD_NOT_ALLOWED); // supported by server, but not this context (nor built-in)
} else { } else {
serverResponse.writeError(HttpResponseStatus.NOT_IMPLEMENTED); // unsupported method serverResponse.writeError(HttpResponseStatus.NOT_IMPLEMENTED); // unsupported method

View file

@ -8,7 +8,7 @@ import io.netty.handler.codec.http2.Http2Settings;
import io.netty.handler.codec.http2.HttpConversionUtil; import io.netty.handler.codec.http2.HttpConversionUtil;
import org.xbib.netty.http.common.HttpAddress; import org.xbib.netty.http.common.HttpAddress;
import org.xbib.netty.http.server.Server; import org.xbib.netty.http.server.Server;
import org.xbib.netty.http.server.context.VirtualServer; import org.xbib.netty.http.server.endpoint.NamedServer;
import java.io.IOException; import java.io.IOException;
@ -26,13 +26,13 @@ public class Http2ServerTransport extends BaseServerTransport {
@Override @Override
public void requestReceived(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest, Integer sequenceId) throws IOException { public void requestReceived(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest, Integer sequenceId) throws IOException {
int requestId = requestCounter.incrementAndGet(); int requestId = requestCounter.incrementAndGet();
VirtualServer virtualServer = server.getVirtualServer(fullHttpRequest.headers().get(HttpHeaderNames.HOST)); NamedServer namedServer = server.getVirtualServer(fullHttpRequest.headers().get(HttpHeaderNames.HOST));
if (virtualServer == null) { if (namedServer == null) {
virtualServer = server.getDefaultVirtualServer(); namedServer = server.getDefaultVirtualServer();
} }
HttpAddress httpAddress = server.getServerConfig().getAddress(); HttpAddress httpAddress = server.getServerConfig().getAddress();
Integer streamId = fullHttpRequest.headers().getInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text()); Integer streamId = fullHttpRequest.headers().getInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text());
ServerRequest serverRequest = new ServerRequest(virtualServer, httpAddress, fullHttpRequest, ServerRequest serverRequest = new ServerRequest(namedServer, httpAddress, fullHttpRequest,
sequenceId, streamId, requestId); sequenceId, streamId, requestId);
ServerResponse serverResponse = new Http2ServerResponse(serverRequest, ctx); ServerResponse serverResponse = new Http2ServerResponse(serverRequest, ctx);
if (acceptRequest(serverRequest, serverResponse)) { if (acceptRequest(serverRequest, serverResponse)) {

View file

@ -7,7 +7,7 @@ import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http2.Http2Settings; import io.netty.handler.codec.http2.Http2Settings;
import org.xbib.netty.http.common.HttpAddress; import org.xbib.netty.http.common.HttpAddress;
import org.xbib.netty.http.server.Server; import org.xbib.netty.http.server.Server;
import org.xbib.netty.http.server.context.VirtualServer; import org.xbib.netty.http.server.endpoint.NamedServer;
import java.io.IOException; import java.io.IOException;
@ -26,12 +26,12 @@ public class HttpServerTransport extends BaseServerTransport {
public void requestReceived(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest, Integer sequenceId) public void requestReceived(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest, Integer sequenceId)
throws IOException { throws IOException {
int requestId = requestCounter.incrementAndGet(); int requestId = requestCounter.incrementAndGet();
VirtualServer virtualServer = server.getVirtualServer(fullHttpRequest.headers().get(HttpHeaderNames.HOST)); NamedServer namedServer = server.getVirtualServer(fullHttpRequest.headers().get(HttpHeaderNames.HOST));
if (virtualServer == null) { if (namedServer == null) {
virtualServer = server.getDefaultVirtualServer(); namedServer = server.getDefaultVirtualServer();
} }
HttpAddress httpAddress = server.getServerConfig().getAddress(); HttpAddress httpAddress = server.getServerConfig().getAddress();
ServerRequest serverRequest = new ServerRequest(virtualServer, httpAddress, fullHttpRequest, ServerRequest serverRequest = new ServerRequest(namedServer, httpAddress, fullHttpRequest,
sequenceId, null, requestId); sequenceId, null, requestId);
ServerResponse serverResponse = new HttpServerResponse(serverRequest, ctx); ServerResponse serverResponse = new HttpServerResponse(serverRequest, ctx);
if (acceptRequest(serverRequest, serverResponse)) { if (acceptRequest(serverRequest, serverResponse)) {

View file

@ -2,14 +2,14 @@ package org.xbib.netty.http.server.transport;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import org.xbib.netty.http.common.HttpAddress; import org.xbib.netty.http.common.HttpAddress;
import org.xbib.netty.http.server.context.VirtualServer; import org.xbib.netty.http.server.endpoint.NamedServer;
/** /**
* The {@code ServerRequest} class encapsulates a single request. * The {@code ServerRequest} class encapsulates a single request.
*/ */
public class ServerRequest { public class ServerRequest {
private final VirtualServer virtualServer; private final NamedServer namedServer;
private final HttpAddress httpAddress; private final HttpAddress httpAddress;
@ -23,9 +23,9 @@ public class ServerRequest {
private String contextPath; private String contextPath;
public ServerRequest(VirtualServer virtualServer, HttpAddress httpAddress, public ServerRequest(NamedServer namedServer, HttpAddress httpAddress,
FullHttpRequest httpRequest, Integer sequenceId, Integer streamId, Integer requestId) { FullHttpRequest httpRequest, Integer sequenceId, Integer streamId, Integer requestId) {
this.virtualServer = virtualServer; this.namedServer = namedServer;
this.httpAddress = httpAddress; this.httpAddress = httpAddress;
this.httpRequest = httpRequest; this.httpRequest = httpRequest;
this.sequenceId = sequenceId; this.sequenceId = sequenceId;
@ -33,8 +33,8 @@ public class ServerRequest {
this.requestId = requestId; this.requestId = requestId;
} }
public VirtualServer getVirtualServer() { public NamedServer getNamedServer() {
return virtualServer; return namedServer;
} }
public void setContextPath(String contextPath) { public void setContextPath(String contextPath) {

View file

@ -2,7 +2,8 @@ package org.xbib.netty.http.server.test;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.client.Client; import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.Request; import org.xbib.netty.http.client.Request;
import org.xbib.netty.http.client.listener.ResponseListener; import org.xbib.netty.http.client.listener.ResponseListener;
@ -18,18 +19,19 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
public class CleartextHttp1Test extends TestBase { @ExtendWith(NettyHttpExtension.class)
class CleartextHttp1Test {
private static final Logger logger = Logger.getLogger(CleartextHttp1Test.class.getName()); private static final Logger logger = Logger.getLogger(CleartextHttp1Test.class.getName());
@Test @Test
public void testSimpleClearTextHttp1() throws Exception { void testSimpleClearTextHttp1() throws Exception {
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008); HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
Server server = Server.builder() Server server = Server.builder()
.bind(httpAddress).build(); .bind(httpAddress).build();
server.getDefaultVirtualServer().addContext("/", (request, response) -> server.getDefaultVirtualServer().addHandler("/", (request, response) ->
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain())); response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()));
server.accept(); server.accept();
Client client = Client.builder() Client client = Client.builder()
@ -51,23 +53,20 @@ public class CleartextHttp1Test extends TestBase {
client.shutdownGracefully(); client.shutdownGracefully();
server.shutdownGracefully(); server.shutdownGracefully();
} }
logger.log(Level.INFO, "exepecting=1 counter=" + counter.get());
assertEquals(1, counter.get()); assertEquals(1, counter.get());
} }
@Test @Test
public void testPooledClearTextHttp1() throws Exception { void testPooledClearTextHttp1() throws Exception {
int loop = 4096; int loop = 4096;
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008); HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
Server server = Server.builder() Server server = Server.builder()
//.enableDebug()
.bind(httpAddress).build(); .bind(httpAddress).build();
server.getDefaultVirtualServer().addContext("/", (request, response) -> { server.getDefaultVirtualServer().addHandler("/", (request, response) -> {
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()); response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain());
}); });
server.accept(); server.accept();
Client client = Client.builder() Client client = Client.builder()
//.enableDebug()
.addPoolNode(httpAddress) .addPoolNode(httpAddress)
.setPoolNodeConnectionLimit(2) .setPoolNodeConnectionLimit(2)
.build(); .build();
@ -96,19 +95,17 @@ public class CleartextHttp1Test extends TestBase {
client.shutdownGracefully(); client.shutdownGracefully();
server.shutdownGracefully(); server.shutdownGracefully();
} }
logger.log(Level.INFO, "expecting=" + loop + " counter=" + counter.get());
assertEquals(loop, counter.get()); assertEquals(loop, counter.get());
} }
@Test @Test
public void testMultithreadedPooledClearTextHttp1() throws Exception { void testMultithreadedPooledClearTextHttp1() throws Exception {
int threads = 4; int threads = 4;
int loop = 4 * 1024; int loop = 4 * 1024;
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008); HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
Server server = Server.builder() Server server = Server.builder()
//.enableDebug()
.bind(httpAddress).build(); .bind(httpAddress).build();
server.getDefaultVirtualServer().addContext("/", (request, response) -> { server.getDefaultVirtualServer().addHandler("/", (request, response) -> {
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()); response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain());
}); });
server.accept(); server.accept();
@ -159,7 +156,6 @@ public class CleartextHttp1Test extends TestBase {
client.shutdownGracefully(); client.shutdownGracefully();
server.shutdownGracefully(); server.shutdownGracefully();
} }
logger.log(Level.INFO, "expecting=" + (threads * loop) + " counter=" + counter.get());
assertEquals(threads * loop, counter.get()); assertEquals(threads * loop, counter.get());
} }
} }

View file

@ -1,7 +1,8 @@
package org.xbib.netty.http.server.test; package org.xbib.netty.http.server.test;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.client.Client; import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.Request; import org.xbib.netty.http.client.Request;
import org.xbib.netty.http.client.listener.ResponseListener; import org.xbib.netty.http.client.listener.ResponseListener;
@ -18,19 +19,20 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
public class CleartextHttp2Test extends TestBase { @ExtendWith(NettyHttpExtension.class)
class CleartextHttp2Test {
private static final Logger logger = Logger.getLogger(CleartextHttp2Test.class.getName()); private static final Logger logger = Logger.getLogger(CleartextHttp2Test.class.getName());
@Test @Test
public void testSimpleCleartextHttp2() throws Exception { void testSimpleCleartextHttp2() throws Exception {
HttpAddress httpAddress = HttpAddress.http2("localhost", 8008); HttpAddress httpAddress = HttpAddress.http2("localhost", 8008);
Server server = Server.builder() Server server = Server.builder()
.bind(httpAddress) .bind(httpAddress)
.build(); .build();
server.getDefaultVirtualServer().addContext("/", (request, response) -> server.getDefaultVirtualServer().addHandler("/", (request, response) ->
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain())); response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()));
server.accept(); server.accept();
Client client = Client.builder() Client client = Client.builder()
@ -60,23 +62,19 @@ public class CleartextHttp2Test extends TestBase {
client.shutdownGracefully(); client.shutdownGracefully();
server.shutdownGracefully(); server.shutdownGracefully();
} }
logger.log(Level.INFO, "counter = " + counter.get());
assertEquals(1, counter.get()); assertEquals(1, counter.get());
} }
@Test @Test
public void testPooledClearTextHttp2() throws Exception { void testPooledClearTextHttp2() throws Exception {
int loop = 4096; int loop = 4096;
HttpAddress httpAddress = HttpAddress.http2("localhost", 8008); HttpAddress httpAddress = HttpAddress.http2("localhost", 8008);
Server server = Server.builder() Server server = Server.builder()
.bind(httpAddress).build(); .bind(httpAddress).build();
server.getDefaultVirtualServer().addContext("/", (request, response) -> server.getDefaultVirtualServer().addHandler("/", (request, response) ->
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain())); response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()));
//server.getDefaultVirtualServer().addContext("/", (request, response) ->
// response.write(request.getRequest().content().toString(StandardCharsets.UTF_8)));
server.accept(); server.accept();
Client client = Client.builder() Client client = Client.builder()
//.enableDebug()
.addPoolNode(httpAddress) .addPoolNode(httpAddress)
.setPoolNodeConnectionLimit(2) .setPoolNodeConnectionLimit(2)
.build(); .build();
@ -114,14 +112,14 @@ public class CleartextHttp2Test extends TestBase {
} }
@Test @Test
public void testMultithreadPooledClearTextHttp2() throws Exception { void testMultithreadPooledClearTextHttp2() throws Exception {
int threads = 2; int threads = 2;
int loop = 4 * 1024; int loop = 4 * 1024;
HttpAddress httpAddress = HttpAddress.http2("localhost", 8008); HttpAddress httpAddress = HttpAddress.http2("localhost", 8008);
Server server = Server.builder() Server server = Server.builder()
.bind(httpAddress) .bind(httpAddress)
.build(); .build();
server.getDefaultVirtualServer().addContext("/", (request, response) -> server.getDefaultVirtualServer().addHandler("/", (request, response) ->
response.write(request.getRequest().content().toString(StandardCharsets.UTF_8)) response.write(request.getRequest().content().toString(StandardCharsets.UTF_8))
); );
server.accept(); server.accept();
@ -176,7 +174,7 @@ public class CleartextHttp2Test extends TestBase {
} }
@Test @Test
public void testTwoPooledClearTextHttp2() throws Exception { void testTwoPooledClearTextHttp2() throws Exception {
int threads = 2; int threads = 2;
int loop = 4 * 1024; int loop = 4 * 1024;
@ -184,7 +182,7 @@ public class CleartextHttp2Test extends TestBase {
AtomicInteger counter1 = new AtomicInteger(); AtomicInteger counter1 = new AtomicInteger();
Server server1 = Server.builder() Server server1 = Server.builder()
.bind(httpAddress1).build(); .bind(httpAddress1).build();
server1.getDefaultVirtualServer().addContext("/", (request, response) -> { server1.getDefaultVirtualServer().addHandler("/", (request, response) -> {
response.write(request.getRequest().content().toString(StandardCharsets.UTF_8)); response.write(request.getRequest().content().toString(StandardCharsets.UTF_8));
counter1.incrementAndGet(); counter1.incrementAndGet();
}); });
@ -194,7 +192,7 @@ public class CleartextHttp2Test extends TestBase {
AtomicInteger counter2 = new AtomicInteger(); AtomicInteger counter2 = new AtomicInteger();
Server server2 = Server.builder() Server server2 = Server.builder()
.bind(httpAddress2).build(); .bind(httpAddress2).build();
server2.getDefaultVirtualServer().addContext("/", (request, response) -> { server2.getDefaultVirtualServer().addHandler("/", (request, response) -> {
response.write(request.getRequest().content().toString(StandardCharsets.UTF_8)); response.write(request.getRequest().content().toString(StandardCharsets.UTF_8));
counter2.incrementAndGet(); counter2.incrementAndGet();
}); });
@ -240,9 +238,9 @@ public class CleartextHttp2Test extends TestBase {
}); });
} }
executorService.shutdown(); executorService.shutdown();
boolean terminated = executorService.awaitTermination(30, TimeUnit.SECONDS); boolean terminated = executorService.awaitTermination(60, TimeUnit.SECONDS);
logger.log(Level.INFO, "terminated = " + terminated + ", now waiting for transport to complete"); logger.log(Level.INFO, "terminated = " + terminated + ", now waiting for transport to complete");
transport.get(30, TimeUnit.SECONDS); transport.get(60, TimeUnit.SECONDS);
} finally { } finally {
client.shutdownGracefully(); client.shutdownGracefully();
server1.shutdownGracefully(); server1.shutdownGracefully();
@ -250,6 +248,6 @@ public class CleartextHttp2Test extends TestBase {
} }
logger.log(Level.INFO, "counter1=" + counter1.get() + " counter2=" + counter2.get()); logger.log(Level.INFO, "counter1=" + counter1.get() + " counter2=" + counter2.get());
logger.log(Level.INFO, "expecting=" + threads * loop + " counter=" + counter.get()); logger.log(Level.INFO, "expecting=" + threads * loop + " counter=" + counter.get());
//assertEquals(threads * loop, counter.get()); assertEquals(threads * loop, counter.get());
} }
} }

View file

@ -1,5 +1,10 @@
package org.xbib; package org.xbib.netty.http.server.test;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import java.security.Security;
import java.util.logging.ConsoleHandler; import java.util.logging.ConsoleHandler;
import java.util.logging.Handler; import java.util.logging.Handler;
import java.util.logging.Level; import java.util.logging.Level;
@ -7,9 +12,13 @@ import java.util.logging.LogManager;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.logging.SimpleFormatter; import java.util.logging.SimpleFormatter;
public class TestBase { public class NettyHttpExtension implements BeforeAllCallback {
static { @Override
public void beforeAll(ExtensionContext context) {
if (Security.getProvider("BC") == null) {
Security.addProvider(new BouncyCastleProvider());
}
System.setProperty("io.netty.noUnsafe", Boolean.toString(true)); System.setProperty("io.netty.noUnsafe", Boolean.toString(true));
System.setProperty("io.netty.noKeySetOptimization", Boolean.toString(true)); System.setProperty("io.netty.noKeySetOptimization", Boolean.toString(true));
//System.setProperty("io.netty.recycler.maxCapacity", Integer.toString(0)); //System.setProperty("io.netty.recycler.maxCapacity", Integer.toString(0));

View file

@ -2,8 +2,8 @@ package org.xbib.netty.http.server.test;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.jupiter.api.Test;
import org.junit.Test; import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.client.Client; import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.Request; import org.xbib.netty.http.client.Request;
import org.xbib.netty.http.client.listener.ResponseListener; import org.xbib.netty.http.client.listener.ResponseListener;
@ -13,7 +13,6 @@ import org.xbib.netty.http.server.Server;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -21,20 +20,15 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
public class SecureHttp1Test extends TestBase { @ExtendWith(NettyHttpExtension.class)
class SecureHttp1Test {
private static final Logger logger = Logger.getLogger(SecureHttp1Test.class.getName()); private static final Logger logger = Logger.getLogger(SecureHttp1Test.class.getName());
static {
if (Security.getProvider("BC") == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
@Test @Test
public void testSimpleSecureHttp1() throws Exception { void testSimpleSecureHttp1() throws Exception {
Server server = Server.builder() Server server = Server.builder()
.setJdkSslProvider() .setJdkSslProvider()
.setSelfCert() .setSelfCert()
@ -51,7 +45,7 @@ public class SecureHttp1Test extends TestBase {
counter.getAndIncrement(); counter.getAndIncrement();
}; };
try { try {
server.getDefaultVirtualServer().addContext("/", (request, response) -> server.getDefaultVirtualServer().addHandler("/", (request, response) ->
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain())); response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()));
server.accept(); server.accept();
Request request = Request.get().setVersion(HttpVersion.HTTP_1_1) Request request = Request.get().setVersion(HttpVersion.HTTP_1_1)
@ -63,19 +57,18 @@ public class SecureHttp1Test extends TestBase {
client.shutdownGracefully(); client.shutdownGracefully();
server.shutdownGracefully(); server.shutdownGracefully();
} }
logger.log(Level.INFO, "counter=" + counter.get());
assertEquals(1, counter.get()); assertEquals(1, counter.get());
} }
@Test @Test
public void testPooledSecureHttp1() throws Exception { void testPooledSecureHttp1() throws Exception {
int loop = 4096; int loop = 4096;
HttpAddress httpAddress = HttpAddress.secureHttp1("localhost", 8143); HttpAddress httpAddress = HttpAddress.secureHttp1("localhost", 8143);
Server server = Server.builder() Server server = Server.builder()
.setJdkSslProvider() .setJdkSslProvider()
.setSelfCert() .setSelfCert()
.bind(httpAddress).build(); .bind(httpAddress).build();
server.getDefaultVirtualServer().addContext("/", (request, response) -> server.getDefaultVirtualServer().addHandler("/", (request, response) ->
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain())); response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()));
server.accept(); server.accept();
Client client = Client.builder() Client client = Client.builder()
@ -114,7 +107,7 @@ public class SecureHttp1Test extends TestBase {
} }
@Test @Test
public void testMultithreadPooledSecureHttp1() throws Exception { void testMultithreadPooledSecureHttp1() throws Exception {
int threads = 4; int threads = 4;
int loop = 4 * 1024; int loop = 4 * 1024;
HttpAddress httpAddress = HttpAddress.secureHttp1("localhost", 8143); HttpAddress httpAddress = HttpAddress.secureHttp1("localhost", 8143);
@ -123,7 +116,7 @@ public class SecureHttp1Test extends TestBase {
.setSelfCert() .setSelfCert()
.bind(httpAddress) .bind(httpAddress)
.build(); .build();
server.getDefaultVirtualServer().addContext("/", (request, response) -> server.getDefaultVirtualServer().addHandler("/", (request, response) ->
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()) response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain())
); );
server.accept(); server.accept();
@ -147,7 +140,7 @@ public class SecureHttp1Test extends TestBase {
executorService.submit(() -> { executorService.submit(() -> {
try { try {
for (int i = 0; i < loop; i++) { for (int i = 0; i < loop; i++) {
String payload = Integer.toString(t) + "/" + Integer.toString(i); String payload = t + "/" + i;
Request request = Request.get().setVersion(HttpVersion.HTTP_1_1) Request request = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base()) .url(server.getServerConfig().getAddress().base())
.content(payload, "text/plain") .content(payload, "text/plain")
@ -168,13 +161,12 @@ public class SecureHttp1Test extends TestBase {
}); });
} }
executorService.shutdown(); executorService.shutdown();
boolean terminated = executorService.awaitTermination(30, TimeUnit.SECONDS); boolean terminated = executorService.awaitTermination(60, TimeUnit.SECONDS);
logger.log(Level.INFO, "terminated = " + terminated); logger.log(Level.INFO, "terminated = " + terminated);
} finally { } finally {
client.shutdownGracefully(); client.shutdownGracefully();
server.shutdownGracefully(); server.shutdownGracefully();
} }
logger.log(Level.INFO, "expecting=" + (threads * loop) + " counter=" + counter.get());
assertEquals(threads * loop , counter.get()); assertEquals(threads * loop , counter.get());
} }
} }

View file

@ -1,8 +1,8 @@
package org.xbib.netty.http.server.test; package org.xbib.netty.http.server.test;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.jupiter.api.Test;
import org.junit.Test; import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.client.Client; import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.Request; import org.xbib.netty.http.client.Request;
import org.xbib.netty.http.client.listener.ResponseListener; import org.xbib.netty.http.client.listener.ResponseListener;
@ -12,7 +12,6 @@ import org.xbib.netty.http.server.Server;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -20,27 +19,22 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
public class SecureHttp2Test extends TestBase { @ExtendWith(NettyHttpExtension.class)
class SecureHttp2Test {
private static final Logger logger = Logger.getLogger(SecureHttp2Test.class.getName()); private static final Logger logger = Logger.getLogger(SecureHttp2Test.class.getName());
static {
if (Security.getProvider("BC") == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
@Test @Test
public void testSimpleSecureHttp2() throws Exception { void testSimpleSecureHttp2() throws Exception {
HttpAddress httpAddress = HttpAddress.secureHttp2("localhost", 8143); HttpAddress httpAddress = HttpAddress.secureHttp2("localhost", 8143);
Server server = Server.builder() Server server = Server.builder()
.setJdkSslProvider() .setJdkSslProvider()
.setSelfCert() .setSelfCert()
.bind(httpAddress) .bind(httpAddress)
.build(); .build();
server.getDefaultVirtualServer().addContext("/", (request, response) -> server.getDefaultVirtualServer().addHandler("/", (request, response) ->
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain())); response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()));
server.accept(); server.accept();
Client client = Client.builder() Client client = Client.builder()
@ -57,7 +51,7 @@ public class SecureHttp2Test extends TestBase {
}; };
try { try {
Transport transport = client.newTransport(httpAddress); Transport transport = client.newTransport(httpAddress);
String payload = Integer.toString(0) + "/" + Integer.toString(0); String payload = 0 + "/" + 0;
Request request = Request.get() Request request = Request.get()
.setVersion("HTTP/2.0") .setVersion("HTTP/2.0")
.uri("/") .uri("/")
@ -71,12 +65,11 @@ public class SecureHttp2Test extends TestBase {
client.shutdownGracefully(); client.shutdownGracefully();
server.shutdownGracefully(); server.shutdownGracefully();
} }
logger.log(Level.INFO, "counter = " + counter.get());
assertEquals(1, counter.get()); assertEquals(1, counter.get());
} }
@Test @Test
public void testPooledSecureHttp2() throws Exception { void testPooledSecureHttp2() throws Exception {
int loop = 4096; int loop = 4096;
HttpAddress httpAddress = HttpAddress.secureHttp2("localhost", 8143); HttpAddress httpAddress = HttpAddress.secureHttp2("localhost", 8143);
Server server = Server.builder() Server server = Server.builder()
@ -84,7 +77,7 @@ public class SecureHttp2Test extends TestBase {
.setSelfCert() .setSelfCert()
.bind(httpAddress) .bind(httpAddress)
.build(); .build();
server.getDefaultVirtualServer().addContext("/", (request, response) -> server.getDefaultVirtualServer().addHandler("/", (request, response) ->
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain())); response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()));
server.accept(); server.accept();
Client client = Client.builder() Client client = Client.builder()
@ -122,12 +115,11 @@ public class SecureHttp2Test extends TestBase {
client.shutdownGracefully(); client.shutdownGracefully();
server.shutdownGracefully(); server.shutdownGracefully();
} }
logger.log(Level.INFO, "counter=" + counter.get());
assertEquals(loop, counter.get()); assertEquals(loop, counter.get());
} }
@Test @Test
public void testMultithreadPooledSecureHttp2() throws Exception { void testMultithreadPooledSecureHttp2() throws Exception {
int threads = 4; int threads = 4;
int loop = 4 * 1024; int loop = 4 * 1024;
HttpAddress httpAddress = HttpAddress.secureHttp2("localhost", 8143); HttpAddress httpAddress = HttpAddress.secureHttp2("localhost", 8143);
@ -136,7 +128,7 @@ public class SecureHttp2Test extends TestBase {
.setSelfCert() .setSelfCert()
.bind(httpAddress) .bind(httpAddress)
.build(); .build();
server.getDefaultVirtualServer().addContext("/", (request, response) -> server.getDefaultVirtualServer().addHandler("/", (request, response) ->
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()) response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain())
); );
server.accept(); server.accept();
@ -163,7 +155,7 @@ public class SecureHttp2Test extends TestBase {
executorService.submit(() -> { executorService.submit(() -> {
try { try {
for (int i = 0; i < loop; i++) { for (int i = 0; i < loop; i++) {
String payload = Integer.toString(t) + "/" + Integer.toString(i); String payload = t + "/" + i;
Request request = Request.get().setVersion("HTTP/2.0") Request request = Request.get().setVersion("HTTP/2.0")
.url(server.getServerConfig().getAddress().base()) .url(server.getServerConfig().getAddress().base())
.content(payload, "text/plain") .content(payload, "text/plain")
@ -181,14 +173,13 @@ public class SecureHttp2Test extends TestBase {
}); });
} }
executorService.shutdown(); executorService.shutdown();
boolean terminated = executorService.awaitTermination(30, TimeUnit.SECONDS); boolean terminated = executorService.awaitTermination(60, TimeUnit.SECONDS);
logger.log(Level.INFO, "terminated = " + terminated + ", now waiting for transport to complete"); logger.log(Level.INFO, "terminated = " + terminated + ", now waiting for transport to complete");
transport.get(30, TimeUnit.SECONDS); transport.get(60, TimeUnit.SECONDS);
} finally { } finally {
client.shutdownGracefully(); client.shutdownGracefully();
server.shutdownGracefully(); server.shutdownGracefully();
} }
logger.log(Level.INFO, "expected=" + (threads * loop) + " counter=" + counter.get());
assertEquals(threads * loop , counter.get()); assertEquals(threads * loop , counter.get());
} }
} }

View file

@ -1,16 +1,16 @@
package org.xbib.netty.http.server.test; package org.xbib.netty.http.server.test;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.xbib.netty.http.server.security.tls.SelfSignedCertificate; import org.xbib.netty.http.server.security.tls.SelfSignedCertificate;
import java.security.Security; import java.security.Security;
import java.util.logging.Logger; import java.util.logging.Logger;
public class SelfSignedCertificateTest { class SelfSignedCertificateTest {
@Test @Test
public void testSelfSignedCertificate() throws Exception { void testSelfSignedCertificate() throws Exception {
Security.addProvider(new BouncyCastleProvider()); Security.addProvider(new BouncyCastleProvider());
SelfSignedCertificate selfSignedCertificate = new SelfSignedCertificate("localhost"); SelfSignedCertificate selfSignedCertificate = new SelfSignedCertificate("localhost");
selfSignedCertificate.exportPEM(Logger.getLogger("test")); selfSignedCertificate.exportPEM(Logger.getLogger("test"));

View file

@ -1,17 +1,17 @@
package org.xbib.netty.http.server.test; package org.xbib.netty.http.server.test;
import org.junit.Ignore; import org.junit.jupiter.api.Disabled;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.xbib.netty.http.server.Server; import org.xbib.netty.http.server.Server;
@Ignore @Disabled
public class ServerTest { class ServerTest {
@Test @Test
public void testServer() throws Exception { void testServer() throws Exception {
Server server = Server.builder() Server server = Server.builder()
.build(); .build();
server.getDefaultVirtualServer().addContext("/", (request, response) -> server.getDefaultVirtualServer().addHandler("/", (request, response) ->
response.write("Hello World")); response.write("Hello World"));
try { try {
server.accept().channel().closeFuture().sync(); server.accept().channel().closeFuture().sync();

View file

@ -1,12 +1,12 @@
package org.xbib.netty.http.server.test; package org.xbib.netty.http.server.test;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.xbib.netty.http.client.Client; import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.Request; import org.xbib.netty.http.client.Request;
import org.xbib.netty.http.common.HttpAddress;
import org.xbib.netty.http.server.Server; import org.xbib.netty.http.server.Server;
import org.xbib.netty.http.server.context.NioContextHandler; import org.xbib.netty.http.server.endpoint.NioHandler;
import org.xbib.netty.http.server.context.VirtualServer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
@ -16,18 +16,19 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
public class StaticFileServerTest { class StaticFileServerTest {
private static final Logger logger = Logger.getLogger(StaticFileServerTest.class.getName()); private static final Logger logger = Logger.getLogger(StaticFileServerTest.class.getName());
@Test @Test
public void testStaticFileServer() throws Exception { void testStaticFileServer() throws Exception {
Path vartmp = Paths.get("/var/tmp/"); Path vartmp = Paths.get("/var/tmp/");
Server server = Server.builder() Server server = Server.builder()
.addVirtualServer(new VirtualServer().addContext("/static", new NioContextHandler(vartmp))) .bind(HttpAddress.http1("localhost", 8008))
.addHandler("/static", new NioHandler(vartmp))
.build(); .build();
Client client = Client.builder() Client client = Client.builder()
.build(); .build();

View file

@ -1,8 +1,10 @@
package org.xbib.netty.http.server.test; package org.xbib.netty.http.server.test;
import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.buffer.UnpooledByteBufAllocator;
import org.junit.After; import org.junit.jupiter.api.AfterAll;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.server.Server; import org.xbib.netty.http.server.Server;
import java.io.IOException; import java.io.IOException;
@ -10,16 +12,18 @@ import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
public class ThreadLeakTest extends TestBase { @TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ExtendWith(NettyHttpExtension.class)
class ThreadLeakTest {
private static final Logger logger = Logger.getLogger(ThreadLeakTest.class.getName()); private static final Logger logger = Logger.getLogger(ThreadLeakTest.class.getName());
@Test @Test
public void testForLeaks() throws IOException { void testForLeaks() throws IOException {
Server server = Server.builder() Server server = Server.builder()
.setByteBufAllocator(UnpooledByteBufAllocator.DEFAULT) .setByteBufAllocator(UnpooledByteBufAllocator.DEFAULT)
.build(); .build();
server.getDefaultVirtualServer().addContext("/", (request, response) -> server.getDefaultVirtualServer().addHandler("/", (request, response) ->
response.write("Hello World")); response.write("Hello World"));
try { try {
server.accept(); server.accept();
@ -28,8 +32,8 @@ public class ThreadLeakTest extends TestBase {
} }
} }
@After @AfterAll
public void checkThreads() throws Exception { void checkThreads() throws Exception {
Thread.sleep(1000L); Thread.sleep(1000L);
System.gc(); System.gc();
Thread.sleep(3000L); Thread.sleep(3000L);

View file

@ -29,9 +29,9 @@ import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder;
import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder; import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder;
import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler; import io.netty.handler.logging.LoggingHandler;
import org.junit.Ignore; import org.junit.jupiter.api.Test;
import org.junit.Test; import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.server.test.TestBase; import org.xbib.netty.http.server.test.NettyHttpExtension;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@ -39,8 +39,8 @@ import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@Ignore @ExtendWith(NettyHttpExtension.class)
public class CleartextHttp2Test extends TestBase { class CleartextHttp2Test {
private static final Logger clientLogger = Logger.getLogger("client"); private static final Logger clientLogger = Logger.getLogger("client");
private static final Logger serverLogger = Logger.getLogger("server"); private static final Logger serverLogger = Logger.getLogger("server");
@ -56,17 +56,12 @@ public class CleartextHttp2Test extends TestBase {
private CompletableFuture<Boolean> completableFuture; private CompletableFuture<Boolean> completableFuture;
@Test @Test
public void testHttp2() throws Exception { void testHttp2() throws Exception {
final InetSocketAddress inetSocketAddress = new InetSocketAddress("localhost", 8008); final InetSocketAddress inetSocketAddress = new InetSocketAddress("localhost", 8008);
settingsPrefaceFuture = new CompletableFuture<>(); settingsPrefaceFuture = new CompletableFuture<>();
completableFuture = new CompletableFuture<>(); completableFuture = new CompletableFuture<>();
EventLoopGroup serverEventLoopGroup = new NioEventLoopGroup(); EventLoopGroup serverEventLoopGroup = new NioEventLoopGroup();
EventLoopGroup clientEventLoopGroup = new NioEventLoopGroup(); EventLoopGroup clientEventLoopGroup = new NioEventLoopGroup();
try { try {
Http2Connection http2ServerConnection = new DefaultHttp2Connection(true); Http2Connection http2ServerConnection = new DefaultHttp2Connection(true);
ServerBootstrap serverBootstrap = new ServerBootstrap() ServerBootstrap serverBootstrap = new ServerBootstrap()
@ -132,7 +127,7 @@ public class CleartextHttp2Test extends TestBase {
clientChannel.writeAndFlush(request); clientChannel.writeAndFlush(request);
clientLogger.log(level, "waiting"); clientLogger.log(level, "waiting");
completableFuture.get(10, TimeUnit.SECONDS); completableFuture.get(30, TimeUnit.SECONDS);
if (completableFuture.isDone()) { if (completableFuture.isDone()) {
clientLogger.log(Level.INFO, "done"); clientLogger.log(Level.INFO, "done");
} }

View file

@ -18,13 +18,15 @@ import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.handler.codec.http.QueryStringDecoder;
import org.junit.After; import org.junit.jupiter.api.AfterAll;
import org.junit.Ignore; import org.junit.jupiter.api.Disabled;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.server.handler.http.HttpPipelinedRequest; import org.xbib.netty.http.server.handler.http.HttpPipelinedRequest;
import org.xbib.netty.http.server.handler.http.HttpPipelinedResponse; import org.xbib.netty.http.server.handler.http.HttpPipelinedResponse;
import org.xbib.netty.http.server.handler.http.HttpPipeliningHandler; import org.xbib.netty.http.server.handler.http.HttpPipeliningHandler;
import org.xbib.netty.http.server.test.TestBase; import org.xbib.netty.http.server.test.NettyHttpExtension;
import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedChannelException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -42,26 +44,30 @@ import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.hamcrest.core.Is.is; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.Assert.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Ignore /** flaky */
public class HttpPipeliningHandlerTest extends TestBase { @Disabled
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ExtendWith(NettyHttpExtension.class)
class HttpPipeliningHandlerTest {
private static final Logger logger = Logger.getLogger(HttpPipeliningHandlerTest.class.getName()); private static final Logger logger = Logger.getLogger(HttpPipeliningHandlerTest.class.getName());
private static Map<String, CountDownLatch> waitingRequests = new ConcurrentHashMap<>(); private static Map<String, CountDownLatch> waitingRequests = new ConcurrentHashMap<>();
@After @AfterAll
public void closeResources() { void closeResources() {
for (String url : waitingRequests.keySet()) { for (String url : waitingRequests.keySet()) {
finishRequest(url); finishRequest(url);
} }
} }
@Test @Test
public void testThatPipeliningWorksWithFastSerializedRequests() { void testThatPipeliningWorksWithFastSerializedRequests() {
WorkEmulatorHandler handler = new WorkEmulatorHandler(); WorkEmulatorHandler handler = new WorkEmulatorHandler();
EmbeddedChannel embeddedChannel = new EmbeddedChannel(new HttpPipeliningHandler(10000), EmbeddedChannel embeddedChannel = new EmbeddedChannel(new HttpPipeliningHandler(10000),
handler); handler);
@ -75,11 +81,11 @@ public class HttpPipeliningHandlerTest extends TestBase {
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
assertReadHttpMessageHasContent(embeddedChannel, String.valueOf(i)); assertReadHttpMessageHasContent(embeddedChannel, String.valueOf(i));
} }
assertThat(embeddedChannel.isOpen(), is(true)); assertTrue(embeddedChannel.isOpen());
} }
@Test @Test
public void testThatPipeliningWorksWhenSlowRequestsInDifferentOrder() { void testThatPipeliningWorksWhenSlowRequestsInDifferentOrder() {
WorkEmulatorHandler handler = new WorkEmulatorHandler(); WorkEmulatorHandler handler = new WorkEmulatorHandler();
EmbeddedChannel embeddedChannel = new EmbeddedChannel(new HttpPipeliningHandler(10000), EmbeddedChannel embeddedChannel = new EmbeddedChannel(new HttpPipeliningHandler(10000),
handler); handler);
@ -95,11 +101,11 @@ public class HttpPipeliningHandlerTest extends TestBase {
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
assertReadHttpMessageHasContent(embeddedChannel, String.valueOf(i)); assertReadHttpMessageHasContent(embeddedChannel, String.valueOf(i));
} }
assertThat(embeddedChannel.isOpen(), is(true)); assertTrue(embeddedChannel.isOpen());
} }
@Test @Test
public void testThatPipeliningWorksWithChunkedRequests() { void testThatPipeliningWorksWithChunkedRequests() {
WorkEmulatorHandler handler = new WorkEmulatorHandler(); WorkEmulatorHandler handler = new WorkEmulatorHandler();
EmbeddedChannel embeddedChannel = new EmbeddedChannel(new AggregateUrisAndHeadersHandler(), EmbeddedChannel embeddedChannel = new EmbeddedChannel(new AggregateUrisAndHeadersHandler(),
new HttpPipeliningHandler(10000), handler); new HttpPipeliningHandler(10000), handler);
@ -115,11 +121,12 @@ public class HttpPipeliningHandlerTest extends TestBase {
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
assertReadHttpMessageHasContent(embeddedChannel, String.valueOf(i)); assertReadHttpMessageHasContent(embeddedChannel, String.valueOf(i));
} }
assertThat(embeddedChannel.isOpen(), is(true)); assertTrue(embeddedChannel.isOpen());
} }
@Test(expected = ClosedChannelException.class) @Test
public void testThatPipeliningClosesConnectionWithTooManyEvents() { void testThatPipeliningClosesConnectionWithTooManyEvents() {
assertThrows(ClosedChannelException.class, () -> {
WorkEmulatorHandler handler = new WorkEmulatorHandler(); WorkEmulatorHandler handler = new WorkEmulatorHandler();
EmbeddedChannel embeddedChannel = new EmbeddedChannel(new HttpPipeliningHandler(2), EmbeddedChannel embeddedChannel = new EmbeddedChannel(new HttpPipeliningHandler(2),
handler); handler);
@ -133,14 +140,15 @@ public class HttpPipeliningHandlerTest extends TestBase {
finishRequest("0"); finishRequest("0");
handler.shutdownExecutorService(); handler.shutdownExecutorService();
embeddedChannel.writeInbound(createHttpRequest("/")); embeddedChannel.writeInbound(createHttpRequest("/"));
});
} }
private void assertReadHttpMessageHasContent(EmbeddedChannel embeddedChannel, String expectedContent) { private void assertReadHttpMessageHasContent(EmbeddedChannel embeddedChannel, String expectedContent) {
FullHttpResponse response = (FullHttpResponse) embeddedChannel.outboundMessages().poll(); FullHttpResponse response = (FullHttpResponse) embeddedChannel.outboundMessages().poll();
assertNotNull("Expected response to exist, maybe you did not wait long enough?", response); assertNotNull(response);
assertNotNull("Expected response to have content " + expectedContent, response.content()); assertNotNull(response.content());
String data = new String(ByteBufUtil.getBytes(response.content()), StandardCharsets.UTF_8); String data = new String(ByteBufUtil.getBytes(response.content()), StandardCharsets.UTF_8);
assertThat(data, is(expectedContent)); assertEquals(expectedContent, data);
} }
private void finishRequest(String url) { private void finishRequest(String url) {
@ -163,7 +171,7 @@ public class HttpPipeliningHandlerTest extends TestBase {
private class WorkEmulatorHandler extends SimpleChannelInboundHandler<HttpPipelinedRequest> { private class WorkEmulatorHandler extends SimpleChannelInboundHandler<HttpPipelinedRequest> {
private final ExecutorService executorService = Executors.newFixedThreadPool(5); private final ExecutorService executorService = Executors.newFixedThreadPool(8);
@Override @Override
protected void channelRead0(ChannelHandlerContext ctx, HttpPipelinedRequest pipelinedRequest) { protected void channelRead0(ChannelHandlerContext ctx, HttpPipelinedRequest pipelinedRequest) {
@ -181,6 +189,7 @@ public class HttpPipeliningHandlerTest extends TestBase {
httpResponse.headers().add(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes()); httpResponse.headers().add(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
waitingRequests.put(uri, latch); waitingRequests.put(uri, latch);
// can cause RejectedExecutionException if executorService is too small
executorService.submit(() -> { executorService.submit(() -> {
try { try {
latch.await(2, TimeUnit.SECONDS); latch.await(2, TimeUnit.SECONDS);

View file

@ -25,7 +25,6 @@ import io.netty.handler.codec.http.HttpServerUpgradeHandler;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http2.CleartextHttp2ServerUpgradeHandler; import io.netty.handler.codec.http2.CleartextHttp2ServerUpgradeHandler;
import io.netty.handler.codec.http2.DefaultHttp2Headers; import io.netty.handler.codec.http2.DefaultHttp2Headers;
//import io.netty.handler.codec.http2.DefaultHttp2PushPromiseFrame;
import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame;
import io.netty.handler.codec.http2.Http2CodecUtil; import io.netty.handler.codec.http2.Http2CodecUtil;
import io.netty.handler.codec.http2.Http2ConnectionPrefaceAndSettingsFrameWrittenEvent; import io.netty.handler.codec.http2.Http2ConnectionPrefaceAndSettingsFrameWrittenEvent;
@ -41,9 +40,9 @@ import io.netty.handler.codec.http2.Http2StreamFrameToHttpObjectCodec;
import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler; import io.netty.handler.logging.LoggingHandler;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import org.junit.Ignore; import org.junit.jupiter.api.Test;
import org.junit.Test; import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.server.test.TestBase; import org.xbib.netty.http.server.test.NettyHttpExtension;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@ -69,8 +68,8 @@ import java.util.logging.Logger;
* *
* *
*/ */
@Ignore @ExtendWith(NettyHttpExtension.class)
public class MultiplexCodecCleartextHttp2Test extends TestBase { class MultiplexCodecCleartextHttp2Test {
private static final Logger clientLogger = Logger.getLogger("client"); private static final Logger clientLogger = Logger.getLogger("client");
private static final Logger serverLogger = Logger.getLogger("server"); private static final Logger serverLogger = Logger.getLogger("server");
@ -82,14 +81,11 @@ public class MultiplexCodecCleartextHttp2Test extends TestBase {
private final CompletableFuture<Boolean> responseFuture = new CompletableFuture<>(); private final CompletableFuture<Boolean> responseFuture = new CompletableFuture<>();
@Test @Test
public void testMultiplexHttp2() throws Exception { void testMultiplexHttp2() throws Exception {
Http2FrameLogger serverFrameLogger = new Http2FrameLogger(LogLevel.INFO, "server"); Http2FrameLogger serverFrameLogger = new Http2FrameLogger(LogLevel.INFO, "server");
Http2FrameLogger clientFrameLogger = new Http2FrameLogger(LogLevel.INFO, "client"); Http2FrameLogger clientFrameLogger = new Http2FrameLogger(LogLevel.INFO, "client");
EventLoopGroup serverEventLoopGroup = new NioEventLoopGroup(); EventLoopGroup serverEventLoopGroup = new NioEventLoopGroup();
EventLoopGroup clientEventLoopGroup = new NioEventLoopGroup(); EventLoopGroup clientEventLoopGroup = new NioEventLoopGroup();
try { try {
ServerBootstrap serverBootstrap = new ServerBootstrap() ServerBootstrap serverBootstrap = new ServerBootstrap()
.group(serverEventLoopGroup) .group(serverEventLoopGroup)

View file

@ -27,9 +27,9 @@ import io.netty.handler.codec.http2.Http2ConnectionPrefaceAndSettingsFrameWritte
import io.netty.handler.codec.http2.Http2Settings; import io.netty.handler.codec.http2.Http2Settings;
import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder; import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder;
import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder; import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder;
import org.junit.Ignore; import org.junit.jupiter.api.Test;
import org.junit.Test; import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.server.test.TestBase; import org.xbib.netty.http.server.test.NettyHttpExtension;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@ -40,8 +40,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@Ignore @ExtendWith(NettyHttpExtension.class)
public class MultithreadedCleartextHttp2Test extends TestBase { class MultithreadedCleartextHttp2Test {
private static final Logger clientLogger = Logger.getLogger("client"); private static final Logger clientLogger = Logger.getLogger("client");
private static final Logger serverLogger = Logger.getLogger("server"); private static final Logger serverLogger = Logger.getLogger("server");
@ -61,7 +61,7 @@ public class MultithreadedCleartextHttp2Test extends TestBase {
private final AtomicInteger responseCounter = new AtomicInteger(); private final AtomicInteger responseCounter = new AtomicInteger();
@Test @Test
public void testMultiThreadedHttp2() throws Exception { void testMultiThreadedHttp2() throws Exception {
inetSocketAddress = new InetSocketAddress("localhost", 8008); inetSocketAddress = new InetSocketAddress("localhost", 8008);
settingsPrefaceFuture = new CompletableFuture<>(); settingsPrefaceFuture = new CompletableFuture<>();

View file

@ -35,9 +35,9 @@ import io.netty.handler.codec.http2.Http2StreamChannel;
import io.netty.handler.codec.http2.Http2StreamChannelBootstrap; import io.netty.handler.codec.http2.Http2StreamChannelBootstrap;
import io.netty.handler.codec.http2.Http2StreamFrameToHttpObjectCodec; import io.netty.handler.codec.http2.Http2StreamFrameToHttpObjectCodec;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import org.junit.Ignore; import org.junit.jupiter.api.Test;
import org.junit.Test; import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.server.test.TestBase; import org.xbib.netty.http.server.test.NettyHttpExtension;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@ -53,8 +53,8 @@ import java.util.logging.Logger;
* Multithreaded Http2MultiplexCodec demo for cleartext HTTP/2 between a server and a client. * Multithreaded Http2MultiplexCodec demo for cleartext HTTP/2 between a server and a client.
* *
*/ */
@Ignore @ExtendWith(NettyHttpExtension.class)
public class MultithreadedMultiplexCodecCleartextHttp2Test extends TestBase { class MultithreadedMultiplexCodecCleartextHttp2Test {
private static final Logger clientLogger = Logger.getLogger("client"); private static final Logger clientLogger = Logger.getLogger("client");
private static final Logger serverLogger = Logger.getLogger("server"); private static final Logger serverLogger = Logger.getLogger("server");
@ -74,7 +74,7 @@ public class MultithreadedMultiplexCodecCleartextHttp2Test extends TestBase {
private final AtomicInteger responseCounter = new AtomicInteger(); private final AtomicInteger responseCounter = new AtomicInteger();
@Test @Test
public void testMultithreadedMultiplexHttp2() throws Exception { void testMultithreadedMultiplexHttp2() throws Exception {
inetSocketAddress = new InetSocketAddress("localhost", 8008); inetSocketAddress = new InetSocketAddress("localhost", 8008);
settingsPrefaceFuture = new CompletableFuture<>(); settingsPrefaceFuture = new CompletableFuture<>();

View file

@ -1,3 +1,4 @@
include 'netty-http-common' include 'netty-http-common'
include 'netty-http-client' include 'netty-http-client'
include 'netty-http-server' include 'netty-http-server'
include 'netty-http-server-rest'