Skip to content

Commit

Permalink
Merge branch 'release/1.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
mostroverkhov committed Jul 25, 2024
2 parents 134fb82 + 5c1f545 commit 783077f
Show file tree
Hide file tree
Showing 25 changed files with 2,130 additions and 225 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:

strategy:
matrix:
os: [ ubuntu-20.04, macos-11, windows-2019 ]
os: [ ubuntu-20.04, macos-12, windows-2019 ]
jdk: [ 8, 11, 17, 21 ]
fail-fast: false

Expand Down
34 changes: 21 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,33 @@

Alternative Netty implementation of [RFC6455](https://tools.ietf.org/html/rfc6455) - the WebSocket protocol.

Its advantage is significant per-core throughput improvement (1.8 - 2x) for small frames in comparison to netty's out-of-the-box
websocket codecs, and minimal heap allocations on frame path. Library is compatible with
Its advantages are significant per-core throughput improvement (1.8 - 2x) for small frames compared to netty's out-of-the-box
websocket codecs, minimal heap allocations on frame path, and compatibility with
[netty-websocket-http2](https://github.com/jauntsdn/netty-websocket-http2).

### use case & scope

* Intended for efficiently encoded, dense binary data: no extensions (compression) support / outbound text frames / inbound
utf8 validation.
* Intended for dense binary data & small text messages: no extensions (compression) support.

* No per-frame heap allocations in websocket frameFactory / decoder.

* Library assumes small frames - many have payload <= 125 bytes, most are < 1500, maximum supported is 65k (65535 bytes).

* Just codec - fragments, pings, close frames are decoded & validated only. It is responsibility of user code
* Just codec - fragments, pings, close frames are decoded & protocol validated only. It is responsibility of user code
to handle frames according to protocol (reassemble frame fragments, perform graceful close,
respond to pings).

* Dedicated decoder for case of exchanging tiny messages over TLS connection:
only non-masked frames with <= 125 bytes of payload for minimal per-webSocket state (memory) overhead.

* No per-frame heap allocations in websocket frameFactory / decoder.
respond to pings) and do utf8 validation of inbound text frames ([utility](https://github.com/jauntsdn/netty-websocket-http1/blob/fb7bbb12d4fc0e62a72845dee89fe8f1d86f9a0a/netty-websocket-http1/src/main/java/com/jauntsdn/netty/handler/codec/http/websocketx/WebSocketFrameListener.java#L81) is provided).

* Single-threaded (transport IO event-loop) callbacks / frame factory API -
in practice user code has its own message types to carry data, external means (e.g. mpsc / spsc queues) may be used to
in practice user code has its own message types to carry data, and external means (e.g. mpsc / spsc queues) may be used to
properly publish messages on eventloop thread.

* On encoder side 3 use cases are supported: frame factory [[1]](https://github.com/jauntsdn/netty-websocket-http1/blob/fb7bbb12d4fc0e62a72845dee89fe8f1d86f9a0a/netty-websocket-http1-test/src/test/java/com/jauntsdn/netty/handler/codec/http/websocketx/WebSocketCodecTest.java#L1475) (create bytebuffer and encode frame prefix),
frame encoder [[2]](https://github.com/jauntsdn/netty-websocket-http1/blob/fb7bbb12d4fc0e62a72845dee89fe8f1d86f9a0a/netty-websocket-http1-test/src/test/java/com/jauntsdn/netty/handler/codec/http/websocketx/WebSocketCodecTest.java#L1019) (encode frame prefix into provided bytebuffer),
frame bulk-encoder [[3]](https://github.com/jauntsdn/netty-websocket-http1/blob/fb7bbb12d4fc0e62a72845dee89fe8f1d86f9a0a/netty-websocket-http1-test/src/test/java/com/jauntsdn/netty/handler/codec/http/websocketx/WebSocketCodecTest.java#L707) (much more performant - encode multiple frames into provided bytebuffer).

* Dedicated decoder for case of exchanging tiny messages over TLS connection:
only non-masked frames with <= 125 bytes of payload for minimal per-webSocket state (memory) overhead.

### performance

Per-core throughput [this codec perf-test](https://github.com/jauntsdn/netty-websocket-http1/tree/develop/netty-websocket-http1-perftest/src/main/java/com/jauntsdn/netty/handler/codec/http/websocketx/perftest),
Expand Down Expand Up @@ -77,6 +80,11 @@ to create outbound frames. It is library user responsibility to mask outbound fr
public interface WebSocketFrameFactory {

ByteBuf createBinaryFrame(ByteBufAllocator allocator, int binaryDataSize);

// ByteBuf createTextFrame(ByteBufAllocator allocator, int binaryDataSize);

// ByteBuf create*Fragment*(ByteBufAllocator allocator, int textDataSize);

// create*Frame are omitted for control frames, created in similar fashion

ByteBuf mask(ByteBuf frame);
Expand Down Expand Up @@ -159,7 +167,7 @@ repositories {
}
dependencies {
implementation "com.jauntsdn.netty:netty-websocket-http1:1.1.3"
implementation "com.jauntsdn.netty:netty-websocket-http1:1.1.4"
}
```

Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
group=com.jauntsdn.netty
version=1.1.4
version=1.2.0

googleJavaFormatPluginVersion=0.9
dependencyManagementPluginVersion=1.1.0
gitPluginVersion=0.13.0
osDetectorPluginVersion=1.7.3
versionsPluginVersion=0.45.0

nettyVersion=4.1.109.Final
nettyVersion=4.1.112.Final
nettyTcnativeVersion=2.0.65.Final
hdrHistogramVersion=2.1.12
slf4jVersion=1.7.36
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public static void main(String[] args) throws Exception {
boolean isNativeTransport =
Boolean.parseBoolean(System.getProperty("NATIVE_TRANSPORT", "true"));
boolean isEncrypted = Boolean.parseBoolean(System.getProperty("ENCRYPT", "true"));
String keyStoreFile = System.getProperty("KEYSTORE", "localhost.p12");
String keyStorePassword = System.getProperty("KEYSTORE_PASS", "localhost");

boolean isOpensslAvailable = OpenSsl.isAvailable();
boolean isEpollAvailable = Transport.isEpollAvailable();
Expand All @@ -67,7 +69,8 @@ public static void main(String[] args) throws Exception {

Transport transport = Transport.get(isNativeTransport);
logger.info("\n==> io transport: {}", transport.type());
SslContext sslContext = isEncrypted ? Security.serverSslContext() : null;
SslContext sslContext =
isEncrypted ? Security.serverSslContext(keyStoreFile, keyStorePassword) : null;

ServerBootstrap bootstrap = new ServerBootstrap();
Channel server =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public static void main(String[] args) throws Exception {
boolean isNativeTransport =
Boolean.parseBoolean(System.getProperty("NATIVE_TRANSPORT", "true"));
boolean isEncrypted = Boolean.parseBoolean(System.getProperty("ENCRYPT", "true"));
String keyStoreFile = System.getProperty("KEYSTORE", "localhost.p12");
String keyStorePassword = System.getProperty("KEYSTORE_PASS", "localhost");

boolean isOpensslAvailable = OpenSsl.isAvailable();
boolean isEpollAvailable = Transport.isEpollAvailable();
Expand All @@ -67,7 +69,8 @@ public static void main(String[] args) throws Exception {

Transport transport = Transport.get(isNativeTransport);
logger.info("\n==> io transport: {}", transport.type());
SslContext sslContext = isEncrypted ? Security.serverSslContext() : null;
SslContext sslContext =
isEncrypted ? Security.serverSslContext(keyStoreFile, keyStorePassword) : null;

ServerBootstrap bootstrap = new ServerBootstrap();
Channel server =
Expand Down
Binary file not shown.
10 changes: 0 additions & 10 deletions netty-websocket-http1-soaktest/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,6 @@ dependencies {
runtimeOnly "ch.qos.logback:logback-classic"
}

task runServer(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
mainClass = "com.jauntsdn.netty.handler.codec.http.websocketx.soaktest.server.Main"
}

task runClient(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
mainClass = "com.jauntsdn.netty.handler.codec.http.websocketx.soaktest.client.Main"
}

task serverScripts(type: CreateStartScripts) {
mainClass = "com.jauntsdn.netty.handler.codec.http.websocketx.soaktest.server.Main"
applicationName = "${project.name}-server"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ public static void main(String[] args) throws Exception {
int frameSizeLimit = Integer.parseInt(System.getProperty("SIZE", "65535"));
boolean expectMasked = Boolean.parseBoolean(System.getProperty("MASKED", "false"));
boolean maskMismatch = !Boolean.parseBoolean(System.getProperty("STRICT", "false"));
String keyStoreFile = System.getProperty("KEYSTORE", "localhost.p12");
String keyStorePassword = System.getProperty("KEYSTORE_PASS", "localhost");

boolean isOpensslAvailable = OpenSsl.isAvailable();
boolean isEpollAvailable = Transport.isEpollAvailable();
Expand All @@ -67,7 +69,7 @@ public static void main(String[] args) throws Exception {

Transport transport = Transport.get(/*native IO*/ true);
logger.info("\n==> io transport: {}", transport.type());
SslContext sslContext = Security.serverSslContext();
SslContext sslContext = Security.serverSslContext(keyStoreFile, keyStorePassword);

ServerBootstrap bootstrap = new ServerBootstrap();
Channel server =
Expand Down
Binary file not shown.
20 changes: 10 additions & 10 deletions netty-websocket-http1-test/gradle.lockfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ com.google.errorprone:javac-shaded:9+181-r4173-1=googleJavaFormat1.6
com.google.googlejavaformat:google-java-format:1.6=googleJavaFormat1.6
com.google.guava:guava:22.0=googleJavaFormat1.6
com.google.j2objc:j2objc-annotations:1.1=googleJavaFormat1.6
io.netty:netty-buffer:4.1.109.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-codec-http:4.1.109.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-codec:4.1.109.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-common:4.1.109.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-handler:4.1.109.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-resolver:4.1.109.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport-classes-epoll:4.1.109.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport-classes-kqueue:4.1.109.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport-native-unix-common:4.1.109.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport:4.1.109.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-buffer:4.1.112.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-codec-http:4.1.112.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-codec:4.1.112.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-common:4.1.112.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-handler:4.1.112.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-resolver:4.1.112.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport-classes-epoll:4.1.112.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport-classes-kqueue:4.1.112.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport-native-unix-common:4.1.112.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport:4.1.112.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.14.11=testCompileClasspath,testRuntimeClasspath
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
org.assertj:assertj-core:3.25.3=testCompileClasspath,testRuntimeClasspath
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,28 @@
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import java.security.SecureRandom;
import java.io.InputStream;
import java.security.KeyStore;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class Security {
private static final Logger logger = LoggerFactory.getLogger(Security.class);

public static SslContext serverSslContext() throws Exception {
SecureRandom random = new SecureRandom();
SelfSignedCertificate ssc = new SelfSignedCertificate("com.jauntsdn", random, 1024);
public static SslContext serverSslContext(String keystoreFile, String keystorePassword)
throws Exception {
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
KeyStore keyStore = KeyStore.getInstance("PKCS12");
InputStream keystoreStream = Security.class.getClassLoader().getResourceAsStream(keystoreFile);
char[] keystorePasswordArray = keystorePassword.toCharArray();
keyStore.load(keystoreStream, keystorePasswordArray);
keyManagerFactory.init(keyStore, keystorePasswordArray);

return SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
return SslContextBuilder.forServer(keyManagerFactory)
.protocols("TLSv1.3")
.sslProvider(sslProvider())
.ciphers(supportedCypherSuites(), SupportedCipherSuiteFilter.INSTANCE)
Expand Down
Loading

0 comments on commit 783077f

Please sign in to comment.