package org.jooby.internal.netty;
import static org.easymock.EasyMock.expect;
import static org.junit.Assert.assertNotNull;
import java.io.FileNotFoundException;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import org.jooby.spi.HttpHandler;
import org.jooby.test.MockUnit;
import org.jooby.test.MockUnit.Block;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigValueFactory;
import io.netty.handler.codec.http2.Http2SecurityUtil;
import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
@RunWith(PowerMockRunner.class)
@PrepareForTest({NettySslContext.class, SslContextBuilder.class, OpenSsl.class,
ApplicationProtocolConfig.class })
public class NettySslContextTest {
Config conf = ConfigFactory.empty()
.withValue("server.http2.enabled", ConfigValueFactory.fromAnyRef(false))
.withValue("application.tmpdir", ConfigValueFactory.fromAnyRef("target"))
.withValue("application.securePort", ConfigValueFactory.fromAnyRef(8443))
.withValue("ssl.keystore.cert", ConfigValueFactory.fromAnyRef("org/jooby/unsecure.crt"))
.withValue("ssl.keystore.key", ConfigValueFactory.fromAnyRef("org/jooby/unsecure.key"));
@Test
public void sslContext() throws Exception {
new NettySslContext();
new MockUnit(HttpHandler.class)
.expect(ssl(null))
.run(unit -> {
assertNotNull(NettySslContext.build(conf));
});
}
@Test
public void sslTrustCert() throws Exception {
new MockUnit(HttpHandler.class)
.expect(ssl(null))
.expect(unit-> {
SslContextBuilder scb = unit.get(SslContextBuilder.class);
expect(scb.trustManager(Paths.get("target", "unsecure.crt").toFile())).andReturn(scb);
})
.run(unit -> {
assertNotNull(NettySslContext.build(conf.withValue("ssl.trust.cert",
ConfigValueFactory.fromAnyRef("org/jooby/unsecure.crt"))));
});
}
@Test
public void sslContextWithPassword() throws Exception {
new MockUnit(HttpHandler.class)
.expect(ssl("password"))
.run(unit -> {
assertNotNull(NettySslContext
.build(conf.withValue("ssl.keystore.password",
ConfigValueFactory.fromAnyRef("password"))));
});
}
@Test
public void http2jdk() throws Exception {
new MockUnit(HttpHandler.class)
.expect(ssl(null))
.expect(unit -> {
unit.mockStatic(OpenSsl.class);
expect(OpenSsl.isAlpnSupported()).andReturn(false);
})
.expect(alpn(SslProvider.JDK))
.run(unit -> {
assertNotNull(NettySslContext
.build(conf.withValue("server.http2.enabled", ConfigValueFactory.fromAnyRef(true))));
});
}
private Block ssl(final String password) {
return unit -> {
unit.mockStatic(SslContextBuilder.class);
SslContext sslCtx = unit.mock(SslContext.class);
SslContextBuilder scb = unit.mock(SslContextBuilder.class);
expect(scb.build()).andReturn(sslCtx);
expect(SslContextBuilder.forServer(Paths.get("target", "unsecure.crt").toFile(),
Paths.get("target", "unsecure.key").toFile(), password)).andReturn(scb);
unit.registerMock(SslContextBuilder.class, scb);
};
}
@Test
public void http2OpenSSL() throws Exception {
new MockUnit(HttpHandler.class)
.expect(ssl(null))
.expect(unit -> {
unit.mockStatic(OpenSsl.class);
expect(OpenSsl.isAlpnSupported()).andReturn(true);
})
.expect(alpn(SslProvider.OPENSSL))
.run(unit -> {
assertNotNull(NettySslContext
.build(conf.withValue("server.http2.enabled", ConfigValueFactory.fromAnyRef(true))));
});
}
private Block alpn(final SslProvider provider) {
return unit -> {
SslContextBuilder scb = unit.get(SslContextBuilder.class);
expect(scb.sslProvider(provider)).andReturn(scb);
expect(scb.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE))
.andReturn(scb);
ApplicationProtocolConfig apc = unit.constructor(ApplicationProtocolConfig.class)
.args(Protocol.class, SelectorFailureBehavior.class,
SelectedListenerFailureBehavior.class, List.class)
.build(Protocol.ALPN,
SelectorFailureBehavior.NO_ADVERTISE,
SelectedListenerFailureBehavior.ACCEPT,
Arrays.asList(ApplicationProtocolNames.HTTP_2,
ApplicationProtocolNames.HTTP_1_1));
expect(scb.applicationProtocolConfig(apc)).andReturn(scb);
};
}
@Test
public void shouldNotCopyFileIfPresent() throws Exception {
assertNotNull(NettySslContext.toFile(Paths.get("pom.xml").toString(), "target"));
}
@Test
public void shouldCopyCpFile() throws Exception {
assertNotNull(NettySslContext.toFile(Paths.get("org/jooby/unsecure.crt").toString(), "target"));
}
@Test(expected = FileNotFoundException.class)
public void shouldFailOnMissingFile() throws Exception {
assertNotNull(NettySslContext.toFile(Paths.get("org/jooby/missing.crt").toString(), "target"));
}
}