Skip to content

Commit 31990bc

Browse files
committed
unit tests+code cleanup
1 parent 1cc04fe commit 31990bc

File tree

11 files changed

+378
-243
lines changed

11 files changed

+378
-243
lines changed

jooby-netty/src/main/java/org/jooby/internal/netty/NettyHandler.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,21 @@
3333
import io.netty.channel.SimpleChannelInboundHandler;
3434
import io.netty.handler.codec.http.DefaultFullHttpResponse;
3535
import io.netty.handler.codec.http.FullHttpRequest;
36+
import io.netty.handler.codec.http.HttpRequest;
3637
import io.netty.handler.codec.http.HttpResponseStatus;
3738
import io.netty.handler.codec.http.HttpServerUpgradeHandler;
3839
import io.netty.handler.codec.http.HttpUtil;
3940
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
4041
import io.netty.handler.codec.http2.HttpConversionUtil;
4142
import io.netty.handler.timeout.IdleStateEvent;
43+
import io.netty.util.AsciiString;
4244
import io.netty.util.Attribute;
4345
import io.netty.util.AttributeKey;
4446

4547
public class NettyHandler extends SimpleChannelInboundHandler<Object> {
4648

49+
private static AsciiString STREAM_ID = HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text();
50+
4751
/** The logging system. */
4852
private final Logger log = LoggerFactory.getLogger(getClass());
4953

@@ -70,22 +74,20 @@ public NettyHandler(final HttpHandler handler, final Config config) {
7074

7175
@Override
7276
public void channelRead0(final ChannelHandlerContext ctx, final Object msg) {
73-
if (msg instanceof FullHttpRequest) {
77+
if (msg instanceof HttpRequest) {
7478
ctx.channel().attr(NettyRequest.NEED_FLUSH).set(true);
7579

76-
FullHttpRequest req = (FullHttpRequest) msg;
80+
HttpRequest req = (HttpRequest) msg;
7781
ctx.channel().attr(PATH).set(req.method().name() + " " + req.uri());
7882

7983
if (HttpUtil.is100ContinueExpected(req)) {
8084
ctx.write(new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.CONTINUE));
81-
return;
8285
}
8386

8487
boolean keepAlive = HttpUtil.isKeepAlive(req);
8588

8689
try {
87-
String streamId = req.headers()
88-
.get(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text());
90+
String streamId = req.headers().get(STREAM_ID);
8991

9092
handler.handle(
9193
new NettyRequest(ctx, req, tmpdir, wsMaxMessageSize),
@@ -144,7 +146,7 @@ public void userEventTriggered(final ChannelHandlerContext ctx, final Object evt
144146
} else if (evt instanceof HttpServerUpgradeHandler.UpgradeEvent) {
145147
// Write an HTTP/2 response to the upgrade request
146148
FullHttpRequest req = ((HttpServerUpgradeHandler.UpgradeEvent) evt).upgradeRequest();
147-
req.headers().set(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 1);
149+
req.headers().set(STREAM_ID, req.headers().get(STREAM_ID, "1"));
148150
channelRead0(ctx, req);
149151
} else {
150152
super.userEventTriggered(ctx, evt);

jooby-netty/src/main/java/org/jooby/internal/netty/NettyRequest.java

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import java.io.InputStream;
2525
import java.net.InetSocketAddress;
2626
import java.net.URLDecoder;
27-
import java.util.Collection;
2827
import java.util.Collections;
2928
import java.util.List;
3029
import java.util.Optional;
@@ -58,10 +57,10 @@
5857
import io.netty.handler.codec.http.multipart.FileUpload;
5958
import io.netty.handler.codec.http.multipart.HttpData;
6059
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
60+
import io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType;
6161
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
6262
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
6363
import io.netty.handler.codec.http2.HttpConversionUtil;
64-
import io.netty.handler.ssl.SslHandler;
6564
import io.netty.util.AttributeKey;
6665

6766
public class NettyRequest implements NativeRequest {
@@ -166,11 +165,7 @@ public List<org.jooby.Cookie> cookies() {
166165
@Override
167166
public List<NativeUpload> files(final String name) throws IOException {
168167
decodeParams();
169-
if (files.size() == 0) {
170-
return Collections.emptyList();
171-
}
172-
Collection<NativeUpload> files = this.files.get(name);
173-
return files == null ? Collections.emptyList() : ImmutableList.copyOf(files);
168+
return ImmutableList.copyOf(this.files.get(name));
174169
}
175170

176171
@Override
@@ -194,19 +189,18 @@ public String protocol() {
194189

195190
@Override
196191
public boolean secure() {
197-
return ctx.pipeline().get(SslHandler.class) != null;
192+
return ifSecure(Boolean.TRUE, Boolean.FALSE).booleanValue();
198193
}
199194

200195
@SuppressWarnings("unchecked")
201196
@Override
202197
public <T> T upgrade(final Class<T> type) throws Exception {
203198
if (type == NativeWebSocket.class) {
204-
String protocol = secure() ? "wss" : "ws";
199+
String protocol = ifSecure("wss", "ws");
205200
String webSocketURL = protocol + "://" + req.headers().get(HttpHeaderNames.HOST) + path;
206201

207202
WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
208-
webSocketURL,
209-
null, true, wsMaxMessageSize);
203+
webSocketURL, null, true, wsMaxMessageSize);
210204
WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req);
211205
NettyWebSocket result = new NettyWebSocket(ctx, handshaker, (ws) -> {
212206
handshaker.handshake(ctx.channel(), (FullHttpRequest) req)
@@ -222,7 +216,7 @@ public <T> T upgrade(final Class<T> type) throws Exception {
222216
} else if (type == NativePushPromise.class) {
223217
return (T) new NettyPush(ctx,
224218
req.headers().getInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text()),
225-
header("host").orElse(ip()), secure() ? "https" : "http");
219+
header("host").orElse(ip()), ifSecure("https", "http"));
226220
}
227221
throw new UnsupportedOperationException("Not Supported: " + type);
228222
}
@@ -273,15 +267,10 @@ private Multimap<String, String> decodeParams() throws IOException {
273267
HttpData field = (HttpData) decoder.next();
274268
try {
275269
String name = field.getName();
276-
switch (field.getHttpDataType()) {
277-
case FileUpload:
278-
files.put(name, new NettyUpload((FileUpload) field, tmpdir));
279-
// excludes upload from param names.
280-
break;
281-
default:
282-
String value = field.getString();
283-
params.put(name, value);
284-
break;
270+
if (field.getHttpDataType() == HttpDataType.FileUpload) {
271+
files.put(name, new NettyUpload((FileUpload) field, tmpdir));
272+
} else {
273+
params.put(name, field.getString());
285274
}
286275
} finally {
287276
field.release();
@@ -294,4 +283,8 @@ private Multimap<String, String> decodeParams() throws IOException {
294283
}
295284
return params;
296285
}
286+
287+
private <T> T ifSecure(final T then, final T otherwise) {
288+
return ctx.pipeline().get("ssl") != null ? then : otherwise;
289+
}
297290
}

jooby-netty/src/main/java/org/jooby/internal/netty/NettyServer.java

Lines changed: 7 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,8 @@
2222
import static javaslang.API.Match;
2323
import static javaslang.Predicates.is;
2424

25-
import java.io.File;
26-
import java.io.FileNotFoundException;
27-
import java.io.IOException;
28-
import java.io.InputStream;
2925
import java.lang.reflect.Field;
3026
import java.lang.reflect.ParameterizedType;
31-
import java.nio.file.Files;
32-
import java.nio.file.Paths;
33-
import java.nio.file.StandardCopyOption;
34-
import java.security.cert.CertificateException;
3527
import java.util.Map;
3628
import java.util.Map.Entry;
3729
import java.util.concurrent.ThreadFactory;
@@ -57,19 +49,9 @@
5749
import io.netty.channel.epoll.EpollServerSocketChannel;
5850
import io.netty.channel.nio.NioEventLoopGroup;
5951
import io.netty.channel.socket.nio.NioServerSocketChannel;
60-
import io.netty.handler.codec.http2.Http2SecurityUtil;
6152
import io.netty.handler.logging.LogLevel;
6253
import io.netty.handler.logging.LoggingHandler;
63-
import io.netty.handler.ssl.ApplicationProtocolConfig;
64-
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
65-
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
66-
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
67-
import io.netty.handler.ssl.ApplicationProtocolNames;
68-
import io.netty.handler.ssl.OpenSsl;
6954
import io.netty.handler.ssl.SslContext;
70-
import io.netty.handler.ssl.SslContextBuilder;
71-
import io.netty.handler.ssl.SslProvider;
72-
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
7355
import io.netty.util.ResourceLeakDetector;
7456
import io.netty.util.concurrent.DefaultEventExecutorGroup;
7557
import io.netty.util.concurrent.DefaultThreadFactory;
@@ -102,11 +84,11 @@ public NettyServer(final HttpHandler dispatcher, final Config config) {
10284

10385
@Override
10486
public void start() throws Exception {
105-
int parentThreads = conf.getInt("netty.threads.Parent");
106-
bossLoop = eventLoop(parentThreads, "parent");
107-
if (conf.hasPath("netty.threads.Child")) {
108-
int childThreads = conf.getInt("netty.threads.Child");
109-
workerLoop = eventLoop(childThreads, "child");
87+
int parentThreads = conf.getInt("netty.threads.Boss");
88+
bossLoop = eventLoop(parentThreads, "boss");
89+
if (conf.hasPath("netty.threads.Worker")) {
90+
int childThreads = conf.getInt("netty.threads.Worker");
91+
workerLoop = eventLoop(childThreads, "worker");
11092
} else {
11193
workerLoop = bossLoop;
11294
}
@@ -120,7 +102,7 @@ public void start() throws Exception {
120102
boolean securePort = conf.hasPath("application.securePort");
121103

122104
if (securePort) {
123-
bootstrap(executor, sslCtx(conf), conf.getInt("application.securePort"));
105+
bootstrap(executor, NettySslContext.build(conf), conf.getInt("application.securePort"));
124106
}
125107
}
126108

@@ -137,7 +119,7 @@ private Channel bootstrap(final EventExecutorGroup executor, final SslContext ss
137119
configure(conf.getConfig("netty.options"), "netty.options",
138120
(option, value) -> bootstrap.option(option, value));
139121

140-
configure(conf.getConfig("netty.child.options"), "netty.child.options",
122+
configure(conf.getConfig("netty.worker.options"), "netty.child.options",
141123
(option, value) -> bootstrap.childOption(option, value));
142124

143125
return bootstrap
@@ -207,48 +189,5 @@ private EventLoopGroup eventLoop(final int threads, final String name) {
207189
return new NioEventLoopGroup(threads, threadFactory);
208190
}
209191

210-
private SslContext sslCtx(final Config config)
211-
throws IOException, CertificateException {
212-
String tmpdir = config.getString("application.tmpdir");
213-
boolean http2 = conf.getBoolean("server.http2.enabled");
214-
File keyStoreCert = toFile(config.getString("ssl.keystore.cert"), tmpdir);
215-
File keyStoreKey = toFile(config.getString("ssl.keystore.key"), tmpdir);
216-
String keyStorePass = config.hasPath("ssl.keystore.password")
217-
? config.getString("ssl.keystore.password") : null;
218-
SslContextBuilder scb = SslContextBuilder.forServer(keyStoreCert, keyStoreKey, keyStorePass);
219-
if (config.hasPath("ssl.trust.cert")) {
220-
scb.trustManager(toFile(config.getString("ssl.trust.cert"), tmpdir));
221-
}
222-
if (http2) {
223-
SslProvider provider = OpenSsl.isAlpnSupported() ? SslProvider.OPENSSL : SslProvider.JDK;
224-
return scb.sslProvider(provider)
225-
.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
226-
.applicationProtocolConfig(new ApplicationProtocolConfig(
227-
Protocol.ALPN,
228-
SelectorFailureBehavior.NO_ADVERTISE,
229-
SelectedListenerFailureBehavior.ACCEPT,
230-
ApplicationProtocolNames.HTTP_2,
231-
ApplicationProtocolNames.HTTP_1_1))
232-
.build();
233-
}
234-
return scb.build();
235-
}
236-
237-
static File toFile(final String path, final String tmpdir) throws IOException {
238-
File file = new File(path);
239-
if (file.exists()) {
240-
return file;
241-
}
242-
file = new File(tmpdir, Paths.get(path).getFileName().toString());
243-
// classpath resource?
244-
try (InputStream in = NettyServer.class.getClassLoader().getResourceAsStream(path)) {
245-
if (in == null) {
246-
throw new FileNotFoundException(path);
247-
}
248-
Files.copy(in, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
249-
}
250-
file.deleteOnExit();
251-
return file;
252-
}
253192

254193
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.jooby.internal.netty;
20+
21+
import java.io.File;
22+
import java.io.FileNotFoundException;
23+
import java.io.IOException;
24+
import java.io.InputStream;
25+
import java.nio.file.Files;
26+
import java.nio.file.Paths;
27+
import java.nio.file.StandardCopyOption;
28+
import java.security.cert.CertificateException;
29+
import java.util.Arrays;
30+
31+
import com.google.common.io.Closeables;
32+
import com.typesafe.config.Config;
33+
34+
import io.netty.handler.codec.http2.Http2SecurityUtil;
35+
import io.netty.handler.ssl.ApplicationProtocolConfig;
36+
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
37+
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
38+
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
39+
import io.netty.handler.ssl.ApplicationProtocolNames;
40+
import io.netty.handler.ssl.OpenSsl;
41+
import io.netty.handler.ssl.SslContext;
42+
import io.netty.handler.ssl.SslContextBuilder;
43+
import io.netty.handler.ssl.SslProvider;
44+
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
45+
46+
public class NettySslContext {
47+
48+
static SslContext build(final Config conf) throws IOException, CertificateException {
49+
String tmpdir = conf.getString("application.tmpdir");
50+
boolean http2 = conf.getBoolean("server.http2.enabled");
51+
File keyStoreCert = toFile(conf.getString("ssl.keystore.cert"), tmpdir);
52+
File keyStoreKey = toFile(conf.getString("ssl.keystore.key"), tmpdir);
53+
String keyStorePass = conf.hasPath("ssl.keystore.password")
54+
? conf.getString("ssl.keystore.password") : null;
55+
SslContextBuilder scb = SslContextBuilder.forServer(keyStoreCert, keyStoreKey, keyStorePass);
56+
if (conf.hasPath("ssl.trust.cert")) {
57+
scb.trustManager(toFile(conf.getString("ssl.trust.cert"), tmpdir));
58+
}
59+
if (http2) {
60+
SslProvider provider = OpenSsl.isAlpnSupported() ? SslProvider.OPENSSL : SslProvider.JDK;
61+
return scb.sslProvider(provider)
62+
.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
63+
.applicationProtocolConfig(new ApplicationProtocolConfig(
64+
Protocol.ALPN,
65+
SelectorFailureBehavior.NO_ADVERTISE,
66+
SelectedListenerFailureBehavior.ACCEPT,
67+
Arrays.asList(ApplicationProtocolNames.HTTP_2, ApplicationProtocolNames.HTTP_1_1)))
68+
.build();
69+
}
70+
return scb.build();
71+
}
72+
73+
static File toFile(final String path, final String tmpdir) throws IOException {
74+
File file = new File(path);
75+
if (file.exists()) {
76+
return file;
77+
}
78+
file = new File(tmpdir, Paths.get(path).getFileName().toString());
79+
// classpath resource?
80+
InputStream in = NettyServer.class.getClassLoader().getResourceAsStream(path);
81+
if (in == null) {
82+
throw new FileNotFoundException(path);
83+
}
84+
try {
85+
Files.copy(in, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
86+
file.deleteOnExit();
87+
return file;
88+
} finally {
89+
Closeables.close(in, true);
90+
}
91+
}
92+
93+
}

0 commit comments

Comments
 (0)