Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Add httpsOnly option
Signed-off-by: U1F984 <4466497+u1f984@users.noreply.github.com>
  • Loading branch information
U1F984 committed Apr 29, 2022
commit c979bdd947f1c471dc2e83f3d326b2f71599906e
12 changes: 8 additions & 4 deletions docs/asciidoc/servers.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Server options are available via javadoc:ServerOptions[] class:
.setMaxRequestSize(10485760)
.setSecurePort(8433)
.setSsl(SslOptions.selfSigned())
.setHttpsOnly(false)
.setHttp2(true)
.setExpectContinue(true)
);
Expand All @@ -70,8 +71,9 @@ Server options are available via javadoc:ServerOptions[] class:
maxRequestSize = 10485760
securePort = 8443
ssl = SslOptions.selfSigned()
http2 = true
expectContinue = true
isHttpsOnly = true
isHttp2 = true
isExpectContinue = true
}
}
----
Expand All @@ -87,8 +89,9 @@ Server options are available via javadoc:ServerOptions[] class:
- maxRequestSize: Maximum request size in bytes. Request exceeding this value results in 413(REQUEST_ENTITY_TOO_LARGE) response. Default is `10mb`.
- securePort: Enable HTTPS. This option is fully covered in next section.
- ssl: SSL options with certificate details. This option is fully covered in next section.
- http2: Enable HTTP 2.0.
- expectContinue: Whenever 100-Expect and continue requests are handled by the server. This is off
- isHttpsOnly: bind only to HTTPS port, not HTTP. This requires SSL options to be configured.
- isHttp2: Enable HTTP 2.0.
- isExpectContinue: Whenever 100-Expect and continue requests are handled by the server. This is off
by default, except for Jetty which is always ON.

Server options are available as application configuration properties too:
Expand All @@ -107,6 +110,7 @@ server.defaultHeaders = true
server.maxRequestSize = 10485760
server.securePort = 8443
server.ssl.type = self-signed | PKCS12 | X509
server.ssl.httpsOnly = false
server.http2 = true
server.expectContinue = false
----
Expand Down
14 changes: 8 additions & 6 deletions jooby/src/main/java/io/jooby/Jooby.java
Original file line number Diff line number Diff line change
Expand Up @@ -893,17 +893,19 @@ public Jooby errorCode(@Nonnull Class<? extends Throwable> type,
log.info(" app dir: {}", System.getProperty("user.dir"));
log.info(" tmp dir: {}", tmpdir);

List<Object> args = new ArrayList<>();
StringBuilder buff = new StringBuilder();
buff.append("routes: \n\n{}\n\nlistening on:\n");
args.add(router);

ServerOptions options = server.getOptions();
String host = options.getHost().replace("0.0.0.0", "localhost");
List<Object> args = new ArrayList<>();
args.add(router);
args.add(host);
args.add(options.getPort());
args.add(router.getContextPath());
buff.append(" http://{}:{}{}\n");
if (!options.isHttpsOnly()) {
args.add(host);
args.add(options.getPort());
args.add(router.getContextPath());
buff.append(" http://{}:{}{}\n");
}

if (options.isSSLEnabled()) {
args.add(host);
Expand Down
26 changes: 25 additions & 1 deletion jooby/src/main/java/io/jooby/ServerOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ public class ServerOptions {

private Integer securePort;

/**
* Bind only https port. Default is false.
*/
private boolean httpsOnly;

private Integer compressionLevel;

private Boolean http2;
Expand Down Expand Up @@ -155,6 +160,9 @@ public class ServerOptions {
}
// ssl
SslOptions.from(conf, "server.ssl").ifPresent(options::setSsl);
if (conf.hasPath("server.httpsOnly")) {
options.httpsOnly = conf.getBoolean("server.httpsOnly");
}
if (conf.hasPath("server.http2")) {
options.setHttp2(conf.getBoolean("server.http2"));
}
Expand All @@ -174,6 +182,7 @@ public class ServerOptions {
buff.append(", workerThreads: ").append(getWorkerThreads());
buff.append(", bufferSize: ").append(bufferSize);
buff.append(", maxRequestSize: ").append(maxRequestSize);
buff.append(", httpsOnly: ").append(httpsOnly);
if (compressionLevel != null) {
buff.append(", gzip");
}
Expand Down Expand Up @@ -246,7 +255,7 @@ public boolean isSSLEnabled() {
* @param securePort Port number or <code>0</code> for random number.
* @return This options.
*/
public @Nullable ServerOptions setSecurePort(@Nullable Integer securePort) {
public @Nonnull ServerOptions setSecurePort(@Nullable Integer securePort) {
if (securePort == null) {
this.securePort = null;
} else {
Expand All @@ -255,6 +264,21 @@ public boolean isSSLEnabled() {
return this;
}

/**
* Bind only https port. Default is false.
*/
public boolean isHttpsOnly() {
return httpsOnly;
}

/**
* Bind only https port. Default is false.
*/
public @Nonnull ServerOptions setHttpsOnly(boolean httpsOnly) {
this.httpsOnly = httpsOnly;
return this;
}

/**
* Number of IO threads used by the server. Required by Netty and Undertow.
*
Expand Down
2 changes: 2 additions & 0 deletions jooby/src/test/java/io/jooby/ServerOptionsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public void shouldParseFromConfig() {
.withValue("server.maxRequestSize", fromAnyRef(2048))
.withValue("server.workerThreads", fromAnyRef(32))
.withValue("server.host", fromAnyRef("0.0.0.0"))
.withValue("server.httpsOnly", fromAnyRef(true))
.resolve()
).get();
assertEquals(9090, options.getPort());
Expand All @@ -34,5 +35,6 @@ public void shouldParseFromConfig() {
assertEquals(2048, options.getMaxRequestSize());
assertEquals(32, options.getWorkerThreads());
assertEquals("0.0.0.0", options.getHost());
assertEquals(true, options.isHttpsOnly());
}
}
14 changes: 9 additions & 5 deletions modules/jooby-jetty/src/main/java/io/jooby/jetty/Jetty.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,14 @@ public class Jetty extends io.jooby.Server.Base {
Optional.ofNullable(http2)
.ifPresent(extension -> connectionFactories.addAll(extension.configure(httpConf)));

ServerConnector http = new ServerConnector(server,
connectionFactories.toArray(new ConnectionFactory[0]));
http.setPort(options.getPort());
http.setHost(options.getHost());
if (!options.isHttpsOnly()) {
ServerConnector http = new ServerConnector(server,
connectionFactories.toArray(new ConnectionFactory[0]));
http.setPort(options.getPort());
http.setHost(options.getHost());

server.addConnector(http);
server.addConnector(http);
}

if (options.isSSLEnabled()) {
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
Expand Down Expand Up @@ -170,6 +172,8 @@ public class Jetty extends io.jooby.Server.Base {
secureConnector.setHost(options.getHost());

server.addConnector(secureConnector);
} else if (options.isHttpsOnly()) {
throw new IllegalArgumentException("Server configured for httpsOnly, but ssl options not set");
}

ContextHandler context = new ContextHandler();
Expand Down
15 changes: 9 additions & 6 deletions modules/jooby-netty/src/main/java/io/jooby/netty/Netty.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,13 @@ public class Netty extends Server.Base {
}

/** Bootstrap: */
ServerBootstrap http = transport.configure(acceptorloop, eventloop)
.childHandler(newPipeline(factory, null, http2))
.childOption(ChannelOption.SO_REUSEADDR, true)
.childOption(ChannelOption.TCP_NODELAY, true);

http.bind(options.getHost(), options.getPort()).get();
if (!options.isHttpsOnly()) {
ServerBootstrap http = transport.configure(acceptorloop, eventloop)
.childHandler(newPipeline(factory, null, http2))
.childOption(ChannelOption.SO_REUSEADDR, true)
.childOption(ChannelOption.TCP_NODELAY, true);
http.bind(options.getHost(), options.getPort()).get();
}

if (options.isSSLEnabled()) {
SSLContext javaSslContext = options
Expand All @@ -152,6 +153,8 @@ public class Netty extends Server.Base {
.childOption(ChannelOption.TCP_NODELAY, true);

https.bind(options.getHost(), options.getSecurePort()).get();
} else if (options.isHttpsOnly()) {
throw new IllegalArgumentException("Server configured for httpsOnly, but ssl options not set");
}

fireReady(applications);
Expand Down
14 changes: 10 additions & 4 deletions modules/jooby-utow/src/main/java/io/jooby/utow/Utow.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ public class Utow extends Server.Base {
.setIoThreads(ServerOptions.IO_THREADS)
.setServer("utow");

@Nonnull @Override public Utow setOptions(@Nonnull ServerOptions options) {
@Nonnull
@Override public Utow setOptions(@Nonnull ServerOptions options) {
this.options = options
.setIoThreads(options.getIoThreads());
return this;
Expand Down Expand Up @@ -93,7 +94,6 @@ public class Utow extends Server.Base {
}

Undertow.Builder builder = Undertow.builder()
.addHttpListener(options.getPort(), options.getHost())
.setBufferSize(options.getBufferSize())
/** Socket : */
.setSocketOption(Options.BACKLOG, BACKLOG)
Expand All @@ -110,10 +110,14 @@ public class Utow extends Server.Base {
.setWorkerThreads(options.getWorkerThreads())
.setHandler(handler);

if (!options.isHttpsOnly()) {
builder.addHttpListener(options.getPort(), options.getHost());
}

if (options.isHttp2() == null || options.isHttp2() == Boolean.TRUE) {
stream(spliteratorUnknownSize(
ServiceLoader.load(Http2Configurer.class).iterator(),
Spliterator.ORDERED),
ServiceLoader.load(Http2Configurer.class).iterator(),
Spliterator.ORDERED),
false
)
.filter(it -> it.support(Undertow.Builder.class))
Expand All @@ -134,6 +138,8 @@ public class Utow extends Server.Base {
.map(this::toSslClientAuthMode)
.ifPresent(
clientAuth -> builder.setSocketOption(Options.SSL_CLIENT_AUTH_MODE, clientAuth));
} else if (options.isHttpsOnly()) {
throw new IllegalArgumentException("Server configured for httpsOnly, but ssl options not set");
}

server = builder.build();
Expand Down
16 changes: 16 additions & 0 deletions tests/src/test/java/io/jooby/HttpsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import io.jooby.junit.ServerTest;
import io.jooby.junit.ServerTestRunner;

import java.net.ConnectException;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class HttpsTest {

Expand Down Expand Up @@ -151,4 +154,17 @@ public void forceSSLStatic2(ServerTestRunner runner) {
});
});
}

@ServerTest
public void httpsOnly(ServerTestRunner runner) {
runner.define(app -> {
app.setServerOptions(new ServerOptions().setSecurePort(8443).setHttpsOnly(true));

app.get("/test", ctx -> "test");
}).ready((http, https) -> {
assertThrows(ConnectException.class, () -> http.get("/test", null));

https.get("/test", rsp -> assertEquals("test", rsp.body().string()));
});
}
}