Skip to content

Commit

Permalink
Merge branch 'release/1.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
mostroverkhov committed May 30, 2023
2 parents ad8c84e + 9680c1e commit 3400442
Show file tree
Hide file tree
Showing 11 changed files with 286 additions and 39 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ repositories {
}
dependencies {
implementation "com.jauntsdn.netty:netty-websocket-http1:0.9.2"
implementation "com.jauntsdn.netty:netty-websocket-http1:1.0.0"
}
```

Expand Down
8 changes: 4 additions & 4 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
group=com.jauntsdn.netty
version=1.0.0
version=1.1.0

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

nettyVersion=4.1.90.Final
nettyTcnativeVersion=2.0.59.Final
nettyVersion=4.1.93.Final
nettyTcnativeVersion=2.0.61.Final
hdrHistogramVersion=2.1.12
slf4jVersion=1.7.36
logbackVersion=1.2.11
jsr305Version=3.0.2

junitVersion=5.9.2
junitVersion=5.9.3
assertjVersion=3.24.2

org.gradle.parallel=true
Expand Down
8 changes: 4 additions & 4 deletions netty-websocket-http1-perftest/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,23 @@ dependencies {

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

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

task serverScripts(type: CreateStartScripts) {
mainClass = "com.jauntsdn.netty.handler.codec.http.websocketx.perftest.server.Main"
mainClass = "com.jauntsdn.netty.handler.codec.http.websocketx.perftest.encoder.server.Main"
applicationName = "${project.name}-server"
classpath = startScripts.classpath
outputDir = startScripts.outputDir
}

task clientScripts(type: CreateStartScripts) {
mainClass = "com.jauntsdn.netty.handler.codec.http.websocketx.perftest.client.Main"
mainClass = "com.jauntsdn.netty.handler.codec.http.websocketx.perftest.encoder.client.Main"
applicationName = "${project.name}-client"
classpath = startScripts.classpath
outputDir = startScripts.outputDir
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package com.jauntsdn.netty.handler.codec.http.websocketx.perftest.client;
package com.jauntsdn.netty.handler.codec.http.websocketx.perftest.encoder.client;

import com.jauntsdn.netty.handler.codec.http.websocketx.WebSocketCallbacksHandler;
import com.jauntsdn.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package com.jauntsdn.netty.handler.codec.http.websocketx.perftest.server;
package com.jauntsdn.netty.handler.codec.http.websocketx.perftest.encoder.server;

import com.jauntsdn.netty.handler.codec.http.websocketx.WebSocketCallbacksHandler;
import com.jauntsdn.netty.handler.codec.http.websocketx.WebSocketFrameFactory;
Expand Down
32 changes: 16 additions & 16 deletions netty-websocket-http1-test/gradle.lockfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,26 @@ 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.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-codec-http:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-codec:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-common:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-handler:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-resolver:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport-classes-epoll:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport-classes-kqueue:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport-native-unix-common:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-buffer:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-codec-http:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-codec:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-common:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-handler:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-resolver:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport-classes-epoll:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport-classes-kqueue:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport-native-unix-common:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.12.21=testCompileClasspath,testRuntimeClasspath
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
org.assertj:assertj-core:3.24.2=testCompileClasspath,testRuntimeClasspath
org.codehaus.mojo:animal-sniffer-annotations:1.14=googleJavaFormat1.6
org.junit.jupiter:junit-jupiter-api:5.9.2=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:5.9.2=testRuntimeClasspath
org.junit.jupiter:junit-jupiter-params:5.9.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:1.9.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:1.9.2=testRuntimeClasspath
org.junit:junit-bom:5.9.2=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:5.9.3=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:5.9.3=testRuntimeClasspath
org.junit.jupiter:junit-jupiter-params:5.9.3=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:1.9.3=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:1.9.3=testRuntimeClasspath
org.junit:junit-bom:5.9.3=testCompileClasspath,testRuntimeClasspath
org.opentest4j:opentest4j:1.2.0=testCompileClasspath,testRuntimeClasspath
org.slf4j:slf4j-api:1.7.36=compileClasspath,testCompileClasspath,testRuntimeClasspath
empty=annotationProcessor,testAnnotationProcessor
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,25 @@ void binaryFramesEncoder(boolean mask) throws Exception {
client.close();
}

@Timeout(300)
@ValueSource(booleans = {true, false})
@ParameterizedTest
void binaryFramesBulkEncoder(boolean mask) throws Exception {
int maxFrameSize = 1000;
Channel s = server = nettyServer(new BinaryFramesTestServerHandler(), mask, false);
BinaryFramesEncoderClientBulkHandler clientHandler =
new BinaryFramesEncoderClientBulkHandler(maxFrameSize);
Channel client =
webSocketCallbacksClient(s.localAddress(), mask, true, maxFrameSize, clientHandler);

WebSocketFrameFactory.BulkEncoder encoder = clientHandler.onHandshakeCompleted().join();
Assertions.assertThat(encoder).isNotNull();

CompletableFuture<Void> onComplete = clientHandler.startFramesExchange();
onComplete.join();
client.close();
}

@Timeout(300)
@MethodSource("maskingArgs")
@ParameterizedTest
Expand Down Expand Up @@ -444,6 +463,162 @@ protected void initChannel(SocketChannel ch) {
}
}

static class BinaryFramesEncoderClientBulkHandler
implements WebSocketCallbacksHandler, WebSocketFrameListener {
private final CompletableFuture<WebSocketFrameFactory.BulkEncoder> onHandshakeComplete =
new CompletableFuture<>();
private final CompletableFuture<Void> onFrameExchangeComplete = new CompletableFuture<>();
private WebSocketFrameFactory.BulkEncoder binaryFrameEncoder;
private final int framesCount;
private int receivedFrames;
private int sentFrames;
private ByteBuf outBuffer;
private volatile ChannelHandlerContext ctx;

BinaryFramesEncoderClientBulkHandler(int maxFrameSize) {
this.framesCount = maxFrameSize;
}

@Override
public WebSocketFrameListener exchange(
ChannelHandlerContext ctx, WebSocketFrameFactory webSocketFrameFactory) {
this.binaryFrameEncoder = webSocketFrameFactory.bulkEncoder();
return this;
}

@Override
public void onChannelRead(
ChannelHandlerContext ctx, boolean finalFragment, int rsv, int opcode, ByteBuf payload) {
if (!finalFragment) {
onFrameExchangeComplete.completeExceptionally(
new AssertionError("received non-final frame: " + finalFragment));
payload.release();
return;
}
if (rsv != 0) {
onFrameExchangeComplete.completeExceptionally(
new AssertionError("received frame with non-zero rsv: " + rsv));
payload.release();
return;
}
if (opcode != WebSocketProtocol.OPCODE_BINARY) {
onFrameExchangeComplete.completeExceptionally(
new AssertionError("received non-binary frame: " + Long.toHexString(opcode)));
payload.release();
return;
}

int readableBytes = payload.readableBytes();

int expectedSize = receivedFrames;
if (expectedSize != readableBytes) {
onFrameExchangeComplete.completeExceptionally(
new AssertionError(
"received frame of unexpected size: "
+ expectedSize
+ ", actual: "
+ readableBytes));
payload.release();
return;
}

for (int i = 0; i < readableBytes; i++) {
byte b = payload.readByte();
if (b != (byte) 0xFE) {
onFrameExchangeComplete.completeExceptionally(
new AssertionError("received frame with unexpected content: " + Long.toHexString(b)));
payload.release();
return;
}
}
payload.release();
if (++receivedFrames == framesCount) {
onFrameExchangeComplete.complete(null);
}
}

@Override
public void onChannelWritabilityChanged(ChannelHandlerContext ctx) {
boolean writable = ctx.channel().isWritable();
if (sentFrames > 0 && writable) {
int toSend = framesCount - sentFrames;
if (toSend > 0) {
sendFrames(ctx, toSend);
}
}
}

@Override
public void onOpen(ChannelHandlerContext ctx) {
this.ctx = ctx;
int bufferSize = 4 * framesCount;
this.outBuffer = ctx.alloc().buffer(bufferSize, bufferSize);
onHandshakeComplete.complete(binaryFrameEncoder);
}

@Override
public void onClose(ChannelHandlerContext ctx) {
ByteBuf out = outBuffer;
if (out != null) {
outBuffer = null;
out.release();
}
if (!onFrameExchangeComplete.isDone()) {
onFrameExchangeComplete.completeExceptionally(new ClosedChannelException());
}
}

@Override
public void onExceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
if (!onFrameExchangeComplete.isDone()) {
onFrameExchangeComplete.completeExceptionally(cause);
}
}

CompletableFuture<WebSocketFrameFactory.BulkEncoder> onHandshakeCompleted() {
return onHandshakeComplete;
}

CompletableFuture<Void> startFramesExchange() {
ChannelHandlerContext c = ctx;
c.executor().execute(() -> sendFrames(c, framesCount - sentFrames));
return onFrameExchangeComplete;
}

private void sendFrames(ChannelHandlerContext c, int toSend) {
WebSocketFrameFactory.BulkEncoder frameEncoder = binaryFrameEncoder;
for (int frameIdx = 0; frameIdx < toSend; frameIdx++) {
if (!c.channel().isOpen()) {
return;
}
int payloadSize = sentFrames;
int frameSize = frameEncoder.sizeofBinaryFrame(payloadSize);
ByteBuf out = outBuffer;
if (frameSize > out.capacity() - out.writerIndex()) {
int readableBytes = out.readableBytes();
int bufferSize = 4 * framesCount;
outBuffer = c.alloc().buffer(bufferSize, bufferSize);
if (c.channel().bytesBeforeUnwritable() < readableBytes) {
c.writeAndFlush(out, c.voidPromise());
if (!c.channel().isWritable()) {
return;
}
} else {
c.write(out, c.voidPromise());
}
out = outBuffer;
}
int mask = frameEncoder.encodeBinaryFramePrefix(out, payloadSize);
for (int payloadIdx = 0; payloadIdx < payloadSize; payloadIdx++) {
out.writeByte(0xFE);
}
frameEncoder.maskBinaryFrame(out, mask, payloadSize);
sentFrames++;
}
c.flush();
}
}

static class BinaryFramesEncoderClientHandler
implements WebSocketCallbacksHandler, WebSocketFrameListener {
private final CompletableFuture<WebSocketFrameFactory.Encoder> onHandshakeComplete =
Expand Down
16 changes: 8 additions & 8 deletions netty-websocket-http1/gradle.lockfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ 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.90.Final=compileClasspath
io.netty:netty-codec-http:4.1.90.Final=compileClasspath
io.netty:netty-codec:4.1.90.Final=compileClasspath
io.netty:netty-common:4.1.90.Final=compileClasspath
io.netty:netty-handler:4.1.90.Final=compileClasspath
io.netty:netty-resolver:4.1.90.Final=compileClasspath
io.netty:netty-transport-native-unix-common:4.1.90.Final=compileClasspath
io.netty:netty-transport:4.1.90.Final=compileClasspath
io.netty:netty-buffer:4.1.93.Final=compileClasspath
io.netty:netty-codec-http:4.1.93.Final=compileClasspath
io.netty:netty-codec:4.1.93.Final=compileClasspath
io.netty:netty-common:4.1.93.Final=compileClasspath
io.netty:netty-handler:4.1.93.Final=compileClasspath
io.netty:netty-resolver:4.1.93.Final=compileClasspath
io.netty:netty-transport-native-unix-common:4.1.93.Final=compileClasspath
io.netty:netty-transport:4.1.93.Final=compileClasspath
org.codehaus.mojo:animal-sniffer-annotations:1.14=googleJavaFormat1.6
empty=annotationProcessor
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ public WebSocketFrameFactory frameFactory(ChannelHandlerContext ctx) {
return FrameFactory.INSTANCE;
}

static class FrameFactory implements WebSocketFrameFactory, WebSocketFrameFactory.Encoder {
static class FrameFactory
implements WebSocketFrameFactory,
WebSocketFrameFactory.Encoder,
WebSocketFrameFactory.BulkEncoder {
static final int PREFIX_SIZE_SMALL = 6;
static final int BINARY_FRAME_SMALL =
OPCODE_BINARY << 8 | /*FIN*/ (byte) 1 << 15 | /*MASK*/ (byte) 1 << 7;
Expand Down Expand Up @@ -145,6 +148,11 @@ public Encoder encoder() {
return this;
}

@Override
public BulkEncoder bulkEncoder() {
return this;
}

@Override
public ByteBuf encodeBinaryFrame(ByteBuf binaryFrame) {
int frameSize = binaryFrame.readableBytes();
Expand All @@ -168,6 +176,30 @@ public ByteBuf encodeBinaryFrame(ByteBuf binaryFrame) {
throw new IllegalArgumentException(payloadSizeLimit(payloadSize, 65_535));
}

@Override
public int encodeBinaryFramePrefix(ByteBuf byteBuf, int payloadSize) {
if (payloadSize <= 125) {
byteBuf.writeShort(BINARY_FRAME_SMALL | payloadSize);
int mask = mask();
byteBuf.writeInt(mask);
return mask;
}

if (payloadSize <= 65_535) {
int mask = mask();
byteBuf.writeLong(((BINARY_FRAME_MEDIUM | (long) payloadSize) << 32) | mask);
return mask;
}
throw new IllegalArgumentException(payloadSizeLimit(payloadSize, 65_535));
}

@Override
public ByteBuf maskBinaryFrame(ByteBuf byteBuf, int mask, int payloadSize) {
int end = byteBuf.writerIndex();
int start = end - payloadSize;
return mask(mask, byteBuf, start, end);
}

@Override
public int sizeofBinaryFrame(int payloadSize) {
if (payloadSize <= 125) {
Expand Down
Loading

0 comments on commit 3400442

Please sign in to comment.