Skip to content

Commit a527fb1

Browse files
authored
Merge pull request #2571 from U1F984/https-only
Add httpsOnly option
2 parents 5807c16 + c979bdd commit a527fb1

File tree

8 files changed

+87
-26
lines changed

8 files changed

+87
-26
lines changed

docs/asciidoc/servers.adoc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ Server options are available via javadoc:ServerOptions[] class:
4848
.setMaxRequestSize(10485760)
4949
.setSecurePort(8433)
5050
.setSsl(SslOptions.selfSigned())
51+
.setHttpsOnly(false)
5152
.setHttp2(true)
5253
.setExpectContinue(true)
5354
);
@@ -70,8 +71,9 @@ Server options are available via javadoc:ServerOptions[] class:
7071
maxRequestSize = 10485760
7172
securePort = 8443
7273
ssl = SslOptions.selfSigned()
73-
http2 = true
74-
expectContinue = true
74+
isHttpsOnly = true
75+
isHttp2 = true
76+
isExpectContinue = true
7577
}
7678
}
7779
----
@@ -87,8 +89,9 @@ Server options are available via javadoc:ServerOptions[] class:
8789
- maxRequestSize: Maximum request size in bytes. Request exceeding this value results in 413(REQUEST_ENTITY_TOO_LARGE) response. Default is `10mb`.
8890
- securePort: Enable HTTPS. This option is fully covered in next section.
8991
- ssl: SSL options with certificate details. This option is fully covered in next section.
90-
- http2: Enable HTTP 2.0.
91-
- expectContinue: Whenever 100-Expect and continue requests are handled by the server. This is off
92+
- isHttpsOnly: bind only to HTTPS port, not HTTP. This requires SSL options to be configured.
93+
- isHttp2: Enable HTTP 2.0.
94+
- isExpectContinue: Whenever 100-Expect and continue requests are handled by the server. This is off
9295
by default, except for Jetty which is always ON.
9396

9497
Server options are available as application configuration properties too:
@@ -107,6 +110,7 @@ server.defaultHeaders = true
107110
server.maxRequestSize = 10485760
108111
server.securePort = 8443
109112
server.ssl.type = self-signed | PKCS12 | X509
113+
server.ssl.httpsOnly = false
110114
server.http2 = true
111115
server.expectContinue = false
112116
----

jooby/src/main/java/io/jooby/Jooby.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -893,17 +893,19 @@ public Jooby errorCode(@Nonnull Class<? extends Throwable> type,
893893
log.info(" app dir: {}", System.getProperty("user.dir"));
894894
log.info(" tmp dir: {}", tmpdir);
895895

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

899901
ServerOptions options = server.getOptions();
900902
String host = options.getHost().replace("0.0.0.0", "localhost");
901-
List<Object> args = new ArrayList<>();
902-
args.add(router);
903-
args.add(host);
904-
args.add(options.getPort());
905-
args.add(router.getContextPath());
906-
buff.append(" http://{}:{}{}\n");
903+
if (!options.isHttpsOnly()) {
904+
args.add(host);
905+
args.add(options.getPort());
906+
args.add(router.getContextPath());
907+
buff.append(" http://{}:{}{}\n");
908+
}
907909

908910
if (options.isSSLEnabled()) {
909911
args.add(host);

jooby/src/main/java/io/jooby/ServerOptions.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ public class ServerOptions {
101101

102102
private Integer securePort;
103103

104+
/**
105+
* Bind only https port. Default is false.
106+
*/
107+
private boolean httpsOnly;
108+
104109
private Integer compressionLevel;
105110

106111
private Boolean http2;
@@ -155,6 +160,9 @@ public class ServerOptions {
155160
}
156161
// ssl
157162
SslOptions.from(conf, "server.ssl").ifPresent(options::setSsl);
163+
if (conf.hasPath("server.httpsOnly")) {
164+
options.httpsOnly = conf.getBoolean("server.httpsOnly");
165+
}
158166
if (conf.hasPath("server.http2")) {
159167
options.setHttp2(conf.getBoolean("server.http2"));
160168
}
@@ -174,6 +182,7 @@ public class ServerOptions {
174182
buff.append(", workerThreads: ").append(getWorkerThreads());
175183
buff.append(", bufferSize: ").append(bufferSize);
176184
buff.append(", maxRequestSize: ").append(maxRequestSize);
185+
buff.append(", httpsOnly: ").append(httpsOnly);
177186
if (compressionLevel != null) {
178187
buff.append(", gzip");
179188
}
@@ -246,7 +255,7 @@ public boolean isSSLEnabled() {
246255
* @param securePort Port number or <code>0</code> for random number.
247256
* @return This options.
248257
*/
249-
public @Nullable ServerOptions setSecurePort(@Nullable Integer securePort) {
258+
public @Nonnull ServerOptions setSecurePort(@Nullable Integer securePort) {
250259
if (securePort == null) {
251260
this.securePort = null;
252261
} else {
@@ -255,6 +264,21 @@ public boolean isSSLEnabled() {
255264
return this;
256265
}
257266

267+
/**
268+
* Bind only https port. Default is false.
269+
*/
270+
public boolean isHttpsOnly() {
271+
return httpsOnly;
272+
}
273+
274+
/**
275+
* Bind only https port. Default is false.
276+
*/
277+
public @Nonnull ServerOptions setHttpsOnly(boolean httpsOnly) {
278+
this.httpsOnly = httpsOnly;
279+
return this;
280+
}
281+
258282
/**
259283
* Number of IO threads used by the server. Required by Netty and Undertow.
260284
*

jooby/src/test/java/io/jooby/ServerOptionsTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public void shouldParseFromConfig() {
2222
.withValue("server.maxRequestSize", fromAnyRef(2048))
2323
.withValue("server.workerThreads", fromAnyRef(32))
2424
.withValue("server.host", fromAnyRef("0.0.0.0"))
25+
.withValue("server.httpsOnly", fromAnyRef(true))
2526
.resolve()
2627
).get();
2728
assertEquals(9090, options.getPort());
@@ -34,5 +35,6 @@ public void shouldParseFromConfig() {
3435
assertEquals(2048, options.getMaxRequestSize());
3536
assertEquals(32, options.getWorkerThreads());
3637
assertEquals("0.0.0.0", options.getHost());
38+
assertEquals(true, options.isHttpsOnly());
3739
}
3840
}

modules/jooby-jetty/src/main/java/io/jooby/jetty/Jetty.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,12 +125,14 @@ public class Jetty extends io.jooby.Server.Base {
125125
Optional.ofNullable(http2)
126126
.ifPresent(extension -> connectionFactories.addAll(extension.configure(httpConf)));
127127

128-
ServerConnector http = new ServerConnector(server,
129-
connectionFactories.toArray(new ConnectionFactory[0]));
130-
http.setPort(options.getPort());
131-
http.setHost(options.getHost());
128+
if (!options.isHttpsOnly()) {
129+
ServerConnector http = new ServerConnector(server,
130+
connectionFactories.toArray(new ConnectionFactory[0]));
131+
http.setPort(options.getPort());
132+
http.setHost(options.getHost());
132133

133-
server.addConnector(http);
134+
server.addConnector(http);
135+
}
134136

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

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

175179
ContextHandler context = new ContextHandler();

modules/jooby-netty/src/main/java/io/jooby/netty/Netty.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,13 @@ public class Netty extends Server.Base {
127127
}
128128

129129
/** Bootstrap: */
130-
ServerBootstrap http = transport.configure(acceptorloop, eventloop)
131-
.childHandler(newPipeline(factory, null, http2))
132-
.childOption(ChannelOption.SO_REUSEADDR, true)
133-
.childOption(ChannelOption.TCP_NODELAY, true);
134-
135-
http.bind(options.getHost(), options.getPort()).get();
130+
if (!options.isHttpsOnly()) {
131+
ServerBootstrap http = transport.configure(acceptorloop, eventloop)
132+
.childHandler(newPipeline(factory, null, http2))
133+
.childOption(ChannelOption.SO_REUSEADDR, true)
134+
.childOption(ChannelOption.TCP_NODELAY, true);
135+
http.bind(options.getHost(), options.getPort()).get();
136+
}
136137

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

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

157160
fireReady(applications);

modules/jooby-utow/src/main/java/io/jooby/utow/Utow.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ public class Utow extends Server.Base {
6161
.setIoThreads(ServerOptions.IO_THREADS)
6262
.setServer("utow");
6363

64-
@Nonnull @Override public Utow setOptions(@Nonnull ServerOptions options) {
64+
@Nonnull
65+
@Override public Utow setOptions(@Nonnull ServerOptions options) {
6566
this.options = options
6667
.setIoThreads(options.getIoThreads());
6768
return this;
@@ -93,7 +94,6 @@ public class Utow extends Server.Base {
9394
}
9495

9596
Undertow.Builder builder = Undertow.builder()
96-
.addHttpListener(options.getPort(), options.getHost())
9797
.setBufferSize(options.getBufferSize())
9898
/** Socket : */
9999
.setSocketOption(Options.BACKLOG, BACKLOG)
@@ -110,10 +110,14 @@ public class Utow extends Server.Base {
110110
.setWorkerThreads(options.getWorkerThreads())
111111
.setHandler(handler);
112112

113+
if (!options.isHttpsOnly()) {
114+
builder.addHttpListener(options.getPort(), options.getHost());
115+
}
116+
113117
if (options.isHttp2() == null || options.isHttp2() == Boolean.TRUE) {
114118
stream(spliteratorUnknownSize(
115-
ServiceLoader.load(Http2Configurer.class).iterator(),
116-
Spliterator.ORDERED),
119+
ServiceLoader.load(Http2Configurer.class).iterator(),
120+
Spliterator.ORDERED),
117121
false
118122
)
119123
.filter(it -> it.support(Undertow.Builder.class))
@@ -134,6 +138,8 @@ public class Utow extends Server.Base {
134138
.map(this::toSslClientAuthMode)
135139
.ifPresent(
136140
clientAuth -> builder.setSocketOption(Options.SSL_CLIENT_AUTH_MODE, clientAuth));
141+
} else if (options.isHttpsOnly()) {
142+
throw new IllegalArgumentException("Server configured for httpsOnly, but ssl options not set");
137143
}
138144

139145
server = builder.build();

tests/src/test/java/io/jooby/HttpsTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
import io.jooby.junit.ServerTest;
44
import io.jooby.junit.ServerTestRunner;
55

6+
import java.net.ConnectException;
7+
68
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
import static org.junit.jupiter.api.Assertions.assertThrows;
710

811
public class HttpsTest {
912

@@ -151,4 +154,17 @@ public void forceSSLStatic2(ServerTestRunner runner) {
151154
});
152155
});
153156
}
157+
158+
@ServerTest
159+
public void httpsOnly(ServerTestRunner runner) {
160+
runner.define(app -> {
161+
app.setServerOptions(new ServerOptions().setSecurePort(8443).setHttpsOnly(true));
162+
163+
app.get("/test", ctx -> "test");
164+
}).ready((http, https) -> {
165+
assertThrows(ConnectException.class, () -> http.get("/test", null));
166+
167+
https.get("/test", rsp -> assertEquals("test", rsp.body().string()));
168+
});
169+
}
154170
}

0 commit comments

Comments
 (0)