Skip to content

Commit f4dd111

Browse files
committed
avoid creation of SSLContext for each connection
Separate the creation of the SslContext and the SslHandler. Before, for each new connection an SslContext was created and from it the SslHandler was generated. However creating an SslContext is very expensive due to all the certificate handling. This PR moves the creation of SslHandler to a SslHandlerProvider that is instantiated with a SslContext instance. This way the SslContext is created once, and from it we get SslHandlers for each new connection. This largely improves the performance in usecases where ssl is enabled but http keepalive isn't set.
1 parent d7f173e commit f4dd111

File tree

8 files changed

+108
-139
lines changed

8 files changed

+108
-139
lines changed

lib/logstash/inputs/http.rb

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -212,37 +212,33 @@ def create_http_server(message_handler)
212212
def build_ssl_params
213213
return nil unless @ssl
214214

215+
ssl_builder = nil
216+
verify_mode_string = nil
217+
215218
if @keystore && @keystore_password
216219
ssl_builder = org.logstash.plugins.inputs.http.util.JksSslBuilder.new(@keystore, @keystore_password.value)
217-
if original_params.key?("verify_mode")
218-
if @verify_mode.upcase == "FORCE_PEER"
219-
ssl_builder.setVerifyMode(org.logstash.plugins.inputs.http.util.SslBuilder::SslClientVerifyMode::FORCE_PEER)
220-
elsif @verify_mode.upcase == "PEER"
221-
ssl_builder.setVerifyMode(org.logstash.plugins.inputs.http.util.SslBuilder::SslClientVerifyMode::VERIFY_PEER)
222-
end
220+
verify_mode_string = @verify_mode.upcase if original_params.key?("verify_mode")
221+
else
222+
begin
223+
ssl_builder = org.logstash.plugins.inputs.http.util.SslSimpleBuilder.new(@ssl_certificate, @ssl_key, @ssl_key_passphrase.nil? ? nil : @ssl_key_passphrase.value)
224+
.setCipherSuites(normalized_ciphers)
225+
rescue java.lang.IllegalArgumentException => e
226+
raise LogStash::ConfigurationError.new(e)
223227
end
224-
return ssl_builder
225-
end
226228

227-
begin
228-
ssl_builder = org.logstash.plugins.inputs.http.util.SslSimpleBuilder.new(@ssl_certificate, @ssl_key, @ssl_key_passphrase.nil? ? nil : @ssl_key_passphrase.value)
229-
.setProtocols(convert_protocols)
230-
.setCipherSuites(normalized_ciphers)
231-
rescue java.lang.IllegalArgumentException => e
232-
raise LogStash::ConfigurationError.new(e)
229+
if client_authentication?
230+
verify_mode_string = @ssl_verify_mode.upcase
231+
ssl_builder.setCertificateAuthorities(@ssl_certificate_authorities)
232+
end
233233
end
234234

235-
ssl_builder.setHandshakeTimeoutMilliseconds(@ssl_handshake_timeout)
235+
ssl_context = ssl_builder.build()
236+
ssl_handler_provider = org.logstash.plugins.inputs.http.util.SslHandlerProvider.new(ssl_context)
237+
ssl_handler_provider.setVerifyMode(verify_mode_string)
238+
ssl_handler_provider.setProtocols(convert_protocols)
239+
ssl_handler_provider.setHandshakeTimeoutMilliseconds(@ssl_handshake_timeout)
236240

237-
if client_authentication?
238-
if @ssl_verify_mode.upcase == "FORCE_PEER"
239-
ssl_builder.setVerifyMode(org.logstash.plugins.inputs.http.util.SslBuilder::SslClientVerifyMode::FORCE_PEER)
240-
elsif @ssl_verify_mode.upcase == "PEER"
241-
ssl_builder.setVerifyMode(org.logstash.plugins.inputs.http.util.SslBuilder::SslClientVerifyMode::VERIFY_PEER)
242-
end
243-
ssl_builder.setCertificateAuthorities(@ssl_certificate_authorities)
244-
end
245-
ssl_builder
241+
ssl_handler_provider
246242
end
247243

248244
def ssl_key_configured?

spec/inputs/http_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@
361361
"ssl_certificate" => ssl_certificate.path,
362362
"ssl_key" => ssl_key.path) }
363363
it "should not raise exception" do
364+
expect(subject).to receive(:build_ssl_params)
364365
expect { subject.register }.to_not raise_exception
365366
end
366367
end

src/main/java/org/logstash/plugins/inputs/http/HttpInitializer.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import io.netty.handler.codec.http.HttpObjectAggregator;
88
import io.netty.handler.codec.http.HttpServerCodec;
99
import io.netty.handler.ssl.SslHandler;
10-
import org.logstash.plugins.inputs.http.util.SslBuilder;
10+
import org.logstash.plugins.inputs.http.util.SslHandlerProvider;
1111

1212
import java.util.concurrent.ThreadPoolExecutor;
1313

@@ -16,7 +16,7 @@
1616
*/
1717
public class HttpInitializer extends ChannelInitializer<SocketChannel> {
1818
private final IMessageHandler messageHandler;
19-
private SslBuilder sslBuilder;
19+
private SslHandlerProvider sslHandlerProvider;
2020
private final int maxContentLength;
2121
private final ThreadPoolExecutor executorGroup;
2222

@@ -30,8 +30,8 @@ public HttpInitializer(IMessageHandler messageHandler, ThreadPoolExecutor execut
3030
protected void initChannel(SocketChannel socketChannel) throws Exception {
3131
ChannelPipeline pipeline = socketChannel.pipeline();
3232

33-
if(sslBuilder != null) {
34-
SslHandler sslHandler = sslBuilder.build(socketChannel.alloc());
33+
if(sslHandlerProvider != null) {
34+
SslHandler sslHandler = sslHandlerProvider.getSslHandler(socketChannel.alloc());
3535
pipeline.addLast(sslHandler);
3636
}
3737
pipeline.addLast(new HttpServerCodec());
@@ -40,8 +40,8 @@ protected void initChannel(SocketChannel socketChannel) throws Exception {
4040
pipeline.addLast(new HttpServerHandler(messageHandler.copy(), executorGroup));
4141
}
4242

43-
public void enableSSL(SslBuilder builder) {
44-
sslBuilder = builder;
43+
public void enableSSL(SslHandlerProvider sslHandlerProvider) {
44+
this.sslHandlerProvider = sslHandlerProvider;
4545
}
4646
}
4747

src/main/java/org/logstash/plugins/inputs/http/NettyHttpServer.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@
66
import io.netty.channel.EventLoopGroup;
77
import io.netty.channel.nio.NioEventLoopGroup;
88
import io.netty.channel.socket.nio.NioServerSocketChannel;
9-
import io.netty.handler.codec.http.DefaultHttpHeaders;
10-
import io.netty.handler.codec.http.HttpHeaders;
119
import org.logstash.plugins.inputs.http.util.CustomRejectedExecutionHandler;
12-
import org.logstash.plugins.inputs.http.util.SslBuilder;
10+
import org.logstash.plugins.inputs.http.util.SslHandlerProvider;
1311

1412
import java.io.Closeable;
1513
import java.util.concurrent.ArrayBlockingQueue;
@@ -31,7 +29,7 @@ public class NettyHttpServer implements Runnable, Closeable {
3129
private final ThreadPoolExecutor executorGroup;
3230

3331
public NettyHttpServer(String host, int port, IMessageHandler messageHandler,
34-
SslBuilder sslBuilder, int threads,
32+
SslHandlerProvider sslHandlerProvider, int threads,
3533
int maxPendingRequests, int maxContentLength)
3634
{
3735
this.host = host;
@@ -44,8 +42,8 @@ public NettyHttpServer(String host, int port, IMessageHandler messageHandler,
4442

4543
final HttpInitializer httpInitializer = new HttpInitializer(messageHandler, executorGroup, maxContentLength);
4644

47-
if (sslBuilder != null) {
48-
httpInitializer.enableSSL(sslBuilder);
45+
if (sslHandlerProvider != null) {
46+
httpInitializer.enableSSL(sslHandlerProvider);
4947
}
5048

5149
serverBootstrap = new ServerBootstrap()
Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package org.logstash.plugins.inputs.http.util;
22

3-
import io.netty.buffer.ByteBufAllocator;
43
import io.netty.handler.ssl.SslContextBuilder;
54

6-
import io.netty.handler.ssl.SslHandler;
75

86
import javax.net.ssl.KeyManagerFactory;
9-
import javax.net.ssl.SSLEngine;
107
import javax.net.ssl.TrustManagerFactory;
118

129
import io.netty.handler.ssl.SslContext;
@@ -25,18 +22,13 @@ public class JksSslBuilder implements SslBuilder {
2522
private static final String ALGORITHM = "ssl.KeyManagerFactory.algorithm";
2623
private final String keyStorePath;
2724
private final char[] keyStorePassword;
28-
private SslClientVerifyMode verifyMode;
2925

3026
public JksSslBuilder(String keyStorePath, String keyStorePassword) {
3127
this.keyStorePath = keyStorePath;
3228
this.keyStorePassword = keyStorePassword.toCharArray();
3329
}
3430

35-
public void setVerifyMode(SslClientVerifyMode sslClientVerifyMode) {
36-
this.verifyMode = sslClientVerifyMode;
37-
}
38-
39-
public SslHandler build(ByteBufAllocator bufferAllocator) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
31+
public SslContext build() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
4032
String algorithm = Security.getProperty(ALGORITHM);
4133
if (algorithm == null) {
4234
algorithm = ALGORITHM_SUN_X509;
@@ -54,20 +46,7 @@ public SslHandler build(ByteBufAllocator bufferAllocator) throws IOException, Ke
5446

5547
SslContextBuilder builder = SslContextBuilder.forServer(kmf);
5648
builder.trustManager(tmf);
57-
SslContext context = builder.build();
58-
59-
SslHandler sslHandler = context.newHandler(bufferAllocator);
60-
61-
SSLEngine engine = sslHandler.engine();
62-
63-
if(verifyMode == SslClientVerifyMode.FORCE_PEER) {
64-
// Explicitly require a client certificate
65-
engine.setNeedClientAuth(true);
66-
} else if(verifyMode == SslClientVerifyMode.VERIFY_PEER) {
67-
// If the client supply a client certificate we will verify it.
68-
engine.setWantClientAuth(true);
69-
}
7049

71-
return sslHandler;
50+
return builder.build();
7251
}
7352
}
Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.logstash.plugins.inputs.http.util;
22

3-
import io.netty.buffer.ByteBufAllocator;
4-
import io.netty.handler.ssl.SslHandler;
3+
import io.netty.handler.ssl.SslContext;
54

65
import java.io.IOException;
76
import java.security.KeyManagementException;
@@ -12,10 +11,6 @@
1211

1312
public interface SslBuilder {
1413

15-
SslHandler build(ByteBufAllocator bufferAllocator) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException;
14+
SslContext build() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException;
1615

17-
enum SslClientVerifyMode {
18-
VERIFY_PEER,
19-
FORCE_PEER,
20-
}
2116
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package org.logstash.plugins.inputs.http.util;
2+
3+
import io.netty.buffer.ByteBufAllocator;
4+
import io.netty.handler.ssl.SslContext;
5+
import io.netty.handler.ssl.SslHandler;
6+
import org.apache.logging.log4j.LogManager;
7+
import org.apache.logging.log4j.Logger;
8+
9+
import javax.net.ssl.SSLEngine;
10+
import java.util.Arrays;
11+
12+
public class SslHandlerProvider {
13+
14+
private final static Logger logger = LogManager.getLogger(SslSimpleBuilder.class);
15+
private final SslContext sslContext;
16+
private SslClientVerifyMode verifyMode = SslClientVerifyMode.FORCE_PEER;
17+
private long handshakeTimeoutMilliseconds = 10000;
18+
19+
enum SslClientVerifyMode {
20+
VERIFY_PEER,
21+
FORCE_PEER,
22+
}
23+
24+
private String[] protocols = new String[] { "TLSv1.2" };
25+
26+
public SslHandlerProvider(SslContext sslContext) {
27+
this.sslContext = sslContext;
28+
}
29+
30+
public SslHandler getSslHandler(ByteBufAllocator bufferAllocator) {
31+
32+
SslHandler sslHandler = sslContext.newHandler(bufferAllocator);
33+
34+
SSLEngine engine = sslHandler.engine();
35+
engine.setEnabledProtocols(protocols);
36+
engine.setUseClientMode(false);
37+
38+
if (verifyMode == SslClientVerifyMode.FORCE_PEER) {
39+
// Explicitly require a client certificate
40+
engine.setNeedClientAuth(true);
41+
} else if (verifyMode == SslClientVerifyMode.VERIFY_PEER) {
42+
// If the client supply a client certificate we will verify it.
43+
engine.setWantClientAuth(true);
44+
}
45+
46+
sslHandler.setHandshakeTimeoutMillis(handshakeTimeoutMilliseconds);
47+
48+
return sslHandler;
49+
}
50+
51+
public void setVerifyMode(String verifyMode) {
52+
if (verifyMode.equals("FORCE_PEER")) {
53+
this.verifyMode = SslClientVerifyMode.FORCE_PEER;
54+
} else if (verifyMode.equals("PEER")) {
55+
this.verifyMode = SslClientVerifyMode.VERIFY_PEER;
56+
}
57+
}
58+
59+
public void setProtocols(String[] protocols) {
60+
if(logger.isDebugEnabled())
61+
logger.debug("TLS: " + Arrays.toString(protocols));
62+
63+
this.protocols = protocols;
64+
}
65+
66+
public void setHandshakeTimeoutMilliseconds(long handshakeTimeoutMilliseconds) {
67+
this.handshakeTimeoutMilliseconds = handshakeTimeoutMilliseconds;
68+
}
69+
}

0 commit comments

Comments
 (0)