Skip to content

Commit 3400442

Browse files
committed
Merge branch 'release/1.1.0'
2 parents ad8c84e + 9680c1e commit 3400442

File tree

11 files changed

+286
-39
lines changed

11 files changed

+286
-39
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ repositories {
173173
}
174174
175175
dependencies {
176-
implementation "com.jauntsdn.netty:netty-websocket-http1:0.9.2"
176+
implementation "com.jauntsdn.netty:netty-websocket-http1:1.0.0"
177177
}
178178
```
179179

gradle.properties

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
group=com.jauntsdn.netty
2-
version=1.0.0
2+
version=1.1.0
33

44
googleJavaFormatPluginVersion=0.9
55
dependencyManagementPluginVersion=1.1.0
66
gitPluginVersion=0.13.0
77
osDetectorPluginVersion=1.7.3
88
versionsPluginVersion=0.45.0
99

10-
nettyVersion=4.1.90.Final
11-
nettyTcnativeVersion=2.0.59.Final
10+
nettyVersion=4.1.93.Final
11+
nettyTcnativeVersion=2.0.61.Final
1212
hdrHistogramVersion=2.1.12
1313
slf4jVersion=1.7.36
1414
logbackVersion=1.2.11
1515
jsr305Version=3.0.2
1616

17-
junitVersion=5.9.2
17+
junitVersion=5.9.3
1818
assertjVersion=3.24.2
1919

2020
org.gradle.parallel=true

netty-websocket-http1-perftest/build.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,23 @@ dependencies {
3636

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

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

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

5454
task clientScripts(type: CreateStartScripts) {
55-
mainClass = "com.jauntsdn.netty.handler.codec.http.websocketx.perftest.client.Main"
55+
mainClass = "com.jauntsdn.netty.handler.codec.http.websocketx.perftest.encoder.client.Main"
5656
applicationName = "${project.name}-client"
5757
classpath = startScripts.classpath
5858
outputDir = startScripts.outputDir
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package com.jauntsdn.netty.handler.codec.http.websocketx.perftest.client;
17+
package com.jauntsdn.netty.handler.codec.http.websocketx.perftest.encoder.client;
1818

1919
import com.jauntsdn.netty.handler.codec.http.websocketx.WebSocketCallbacksHandler;
2020
import com.jauntsdn.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package com.jauntsdn.netty.handler.codec.http.websocketx.perftest.server;
17+
package com.jauntsdn.netty.handler.codec.http.websocketx.perftest.encoder.server;
1818

1919
import com.jauntsdn.netty.handler.codec.http.websocketx.WebSocketCallbacksHandler;
2020
import com.jauntsdn.netty.handler.codec.http.websocketx.WebSocketFrameFactory;

netty-websocket-http1-test/gradle.lockfile

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,26 @@ com.google.errorprone:javac-shaded:9+181-r4173-1=googleJavaFormat1.6
99
com.google.googlejavaformat:google-java-format:1.6=googleJavaFormat1.6
1010
com.google.guava:guava:22.0=googleJavaFormat1.6
1111
com.google.j2objc:j2objc-annotations:1.1=googleJavaFormat1.6
12-
io.netty:netty-buffer:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
13-
io.netty:netty-codec-http:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
14-
io.netty:netty-codec:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
15-
io.netty:netty-common:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
16-
io.netty:netty-handler:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
17-
io.netty:netty-resolver:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
18-
io.netty:netty-transport-classes-epoll:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
19-
io.netty:netty-transport-classes-kqueue:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
20-
io.netty:netty-transport-native-unix-common:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
21-
io.netty:netty-transport:4.1.90.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
12+
io.netty:netty-buffer:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
13+
io.netty:netty-codec-http:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
14+
io.netty:netty-codec:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
15+
io.netty:netty-common:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
16+
io.netty:netty-handler:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
17+
io.netty:netty-resolver:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
18+
io.netty:netty-transport-classes-epoll:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
19+
io.netty:netty-transport-classes-kqueue:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
20+
io.netty:netty-transport-native-unix-common:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
21+
io.netty:netty-transport:4.1.93.Final=compileClasspath,testCompileClasspath,testRuntimeClasspath
2222
net.bytebuddy:byte-buddy:1.12.21=testCompileClasspath,testRuntimeClasspath
2323
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
2424
org.assertj:assertj-core:3.24.2=testCompileClasspath,testRuntimeClasspath
2525
org.codehaus.mojo:animal-sniffer-annotations:1.14=googleJavaFormat1.6
26-
org.junit.jupiter:junit-jupiter-api:5.9.2=testCompileClasspath,testRuntimeClasspath
27-
org.junit.jupiter:junit-jupiter-engine:5.9.2=testRuntimeClasspath
28-
org.junit.jupiter:junit-jupiter-params:5.9.2=testCompileClasspath,testRuntimeClasspath
29-
org.junit.platform:junit-platform-commons:1.9.2=testCompileClasspath,testRuntimeClasspath
30-
org.junit.platform:junit-platform-engine:1.9.2=testRuntimeClasspath
31-
org.junit:junit-bom:5.9.2=testCompileClasspath,testRuntimeClasspath
26+
org.junit.jupiter:junit-jupiter-api:5.9.3=testCompileClasspath,testRuntimeClasspath
27+
org.junit.jupiter:junit-jupiter-engine:5.9.3=testRuntimeClasspath
28+
org.junit.jupiter:junit-jupiter-params:5.9.3=testCompileClasspath,testRuntimeClasspath
29+
org.junit.platform:junit-platform-commons:1.9.3=testCompileClasspath,testRuntimeClasspath
30+
org.junit.platform:junit-platform-engine:1.9.3=testRuntimeClasspath
31+
org.junit:junit-bom:5.9.3=testCompileClasspath,testRuntimeClasspath
3232
org.opentest4j:opentest4j:1.2.0=testCompileClasspath,testRuntimeClasspath
3333
org.slf4j:slf4j-api:1.7.36=compileClasspath,testCompileClasspath,testRuntimeClasspath
3434
empty=annotationProcessor,testAnnotationProcessor

netty-websocket-http1-test/src/test/java/com/jauntsdn/netty/handler/codec/http/websocketx/WebSocketCodecTest.java

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,25 @@ void binaryFramesEncoder(boolean mask) throws Exception {
9898
client.close();
9999
}
100100

101+
@Timeout(300)
102+
@ValueSource(booleans = {true, false})
103+
@ParameterizedTest
104+
void binaryFramesBulkEncoder(boolean mask) throws Exception {
105+
int maxFrameSize = 1000;
106+
Channel s = server = nettyServer(new BinaryFramesTestServerHandler(), mask, false);
107+
BinaryFramesEncoderClientBulkHandler clientHandler =
108+
new BinaryFramesEncoderClientBulkHandler(maxFrameSize);
109+
Channel client =
110+
webSocketCallbacksClient(s.localAddress(), mask, true, maxFrameSize, clientHandler);
111+
112+
WebSocketFrameFactory.BulkEncoder encoder = clientHandler.onHandshakeCompleted().join();
113+
Assertions.assertThat(encoder).isNotNull();
114+
115+
CompletableFuture<Void> onComplete = clientHandler.startFramesExchange();
116+
onComplete.join();
117+
client.close();
118+
}
119+
101120
@Timeout(300)
102121
@MethodSource("maskingArgs")
103122
@ParameterizedTest
@@ -444,6 +463,162 @@ protected void initChannel(SocketChannel ch) {
444463
}
445464
}
446465

466+
static class BinaryFramesEncoderClientBulkHandler
467+
implements WebSocketCallbacksHandler, WebSocketFrameListener {
468+
private final CompletableFuture<WebSocketFrameFactory.BulkEncoder> onHandshakeComplete =
469+
new CompletableFuture<>();
470+
private final CompletableFuture<Void> onFrameExchangeComplete = new CompletableFuture<>();
471+
private WebSocketFrameFactory.BulkEncoder binaryFrameEncoder;
472+
private final int framesCount;
473+
private int receivedFrames;
474+
private int sentFrames;
475+
private ByteBuf outBuffer;
476+
private volatile ChannelHandlerContext ctx;
477+
478+
BinaryFramesEncoderClientBulkHandler(int maxFrameSize) {
479+
this.framesCount = maxFrameSize;
480+
}
481+
482+
@Override
483+
public WebSocketFrameListener exchange(
484+
ChannelHandlerContext ctx, WebSocketFrameFactory webSocketFrameFactory) {
485+
this.binaryFrameEncoder = webSocketFrameFactory.bulkEncoder();
486+
return this;
487+
}
488+
489+
@Override
490+
public void onChannelRead(
491+
ChannelHandlerContext ctx, boolean finalFragment, int rsv, int opcode, ByteBuf payload) {
492+
if (!finalFragment) {
493+
onFrameExchangeComplete.completeExceptionally(
494+
new AssertionError("received non-final frame: " + finalFragment));
495+
payload.release();
496+
return;
497+
}
498+
if (rsv != 0) {
499+
onFrameExchangeComplete.completeExceptionally(
500+
new AssertionError("received frame with non-zero rsv: " + rsv));
501+
payload.release();
502+
return;
503+
}
504+
if (opcode != WebSocketProtocol.OPCODE_BINARY) {
505+
onFrameExchangeComplete.completeExceptionally(
506+
new AssertionError("received non-binary frame: " + Long.toHexString(opcode)));
507+
payload.release();
508+
return;
509+
}
510+
511+
int readableBytes = payload.readableBytes();
512+
513+
int expectedSize = receivedFrames;
514+
if (expectedSize != readableBytes) {
515+
onFrameExchangeComplete.completeExceptionally(
516+
new AssertionError(
517+
"received frame of unexpected size: "
518+
+ expectedSize
519+
+ ", actual: "
520+
+ readableBytes));
521+
payload.release();
522+
return;
523+
}
524+
525+
for (int i = 0; i < readableBytes; i++) {
526+
byte b = payload.readByte();
527+
if (b != (byte) 0xFE) {
528+
onFrameExchangeComplete.completeExceptionally(
529+
new AssertionError("received frame with unexpected content: " + Long.toHexString(b)));
530+
payload.release();
531+
return;
532+
}
533+
}
534+
payload.release();
535+
if (++receivedFrames == framesCount) {
536+
onFrameExchangeComplete.complete(null);
537+
}
538+
}
539+
540+
@Override
541+
public void onChannelWritabilityChanged(ChannelHandlerContext ctx) {
542+
boolean writable = ctx.channel().isWritable();
543+
if (sentFrames > 0 && writable) {
544+
int toSend = framesCount - sentFrames;
545+
if (toSend > 0) {
546+
sendFrames(ctx, toSend);
547+
}
548+
}
549+
}
550+
551+
@Override
552+
public void onOpen(ChannelHandlerContext ctx) {
553+
this.ctx = ctx;
554+
int bufferSize = 4 * framesCount;
555+
this.outBuffer = ctx.alloc().buffer(bufferSize, bufferSize);
556+
onHandshakeComplete.complete(binaryFrameEncoder);
557+
}
558+
559+
@Override
560+
public void onClose(ChannelHandlerContext ctx) {
561+
ByteBuf out = outBuffer;
562+
if (out != null) {
563+
outBuffer = null;
564+
out.release();
565+
}
566+
if (!onFrameExchangeComplete.isDone()) {
567+
onFrameExchangeComplete.completeExceptionally(new ClosedChannelException());
568+
}
569+
}
570+
571+
@Override
572+
public void onExceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
573+
if (!onFrameExchangeComplete.isDone()) {
574+
onFrameExchangeComplete.completeExceptionally(cause);
575+
}
576+
}
577+
578+
CompletableFuture<WebSocketFrameFactory.BulkEncoder> onHandshakeCompleted() {
579+
return onHandshakeComplete;
580+
}
581+
582+
CompletableFuture<Void> startFramesExchange() {
583+
ChannelHandlerContext c = ctx;
584+
c.executor().execute(() -> sendFrames(c, framesCount - sentFrames));
585+
return onFrameExchangeComplete;
586+
}
587+
588+
private void sendFrames(ChannelHandlerContext c, int toSend) {
589+
WebSocketFrameFactory.BulkEncoder frameEncoder = binaryFrameEncoder;
590+
for (int frameIdx = 0; frameIdx < toSend; frameIdx++) {
591+
if (!c.channel().isOpen()) {
592+
return;
593+
}
594+
int payloadSize = sentFrames;
595+
int frameSize = frameEncoder.sizeofBinaryFrame(payloadSize);
596+
ByteBuf out = outBuffer;
597+
if (frameSize > out.capacity() - out.writerIndex()) {
598+
int readableBytes = out.readableBytes();
599+
int bufferSize = 4 * framesCount;
600+
outBuffer = c.alloc().buffer(bufferSize, bufferSize);
601+
if (c.channel().bytesBeforeUnwritable() < readableBytes) {
602+
c.writeAndFlush(out, c.voidPromise());
603+
if (!c.channel().isWritable()) {
604+
return;
605+
}
606+
} else {
607+
c.write(out, c.voidPromise());
608+
}
609+
out = outBuffer;
610+
}
611+
int mask = frameEncoder.encodeBinaryFramePrefix(out, payloadSize);
612+
for (int payloadIdx = 0; payloadIdx < payloadSize; payloadIdx++) {
613+
out.writeByte(0xFE);
614+
}
615+
frameEncoder.maskBinaryFrame(out, mask, payloadSize);
616+
sentFrames++;
617+
}
618+
c.flush();
619+
}
620+
}
621+
447622
static class BinaryFramesEncoderClientHandler
448623
implements WebSocketCallbacksHandler, WebSocketFrameListener {
449624
private final CompletableFuture<WebSocketFrameFactory.Encoder> onHandshakeComplete =

netty-websocket-http1/gradle.lockfile

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ com.google.errorprone:javac-shaded:9+181-r4173-1=googleJavaFormat1.6
77
com.google.googlejavaformat:google-java-format:1.6=googleJavaFormat1.6
88
com.google.guava:guava:22.0=googleJavaFormat1.6
99
com.google.j2objc:j2objc-annotations:1.1=googleJavaFormat1.6
10-
io.netty:netty-buffer:4.1.90.Final=compileClasspath
11-
io.netty:netty-codec-http:4.1.90.Final=compileClasspath
12-
io.netty:netty-codec:4.1.90.Final=compileClasspath
13-
io.netty:netty-common:4.1.90.Final=compileClasspath
14-
io.netty:netty-handler:4.1.90.Final=compileClasspath
15-
io.netty:netty-resolver:4.1.90.Final=compileClasspath
16-
io.netty:netty-transport-native-unix-common:4.1.90.Final=compileClasspath
17-
io.netty:netty-transport:4.1.90.Final=compileClasspath
10+
io.netty:netty-buffer:4.1.93.Final=compileClasspath
11+
io.netty:netty-codec-http:4.1.93.Final=compileClasspath
12+
io.netty:netty-codec:4.1.93.Final=compileClasspath
13+
io.netty:netty-common:4.1.93.Final=compileClasspath
14+
io.netty:netty-handler:4.1.93.Final=compileClasspath
15+
io.netty:netty-resolver:4.1.93.Final=compileClasspath
16+
io.netty:netty-transport-native-unix-common:4.1.93.Final=compileClasspath
17+
io.netty:netty-transport:4.1.93.Final=compileClasspath
1818
org.codehaus.mojo:animal-sniffer-annotations:1.14=googleJavaFormat1.6
1919
empty=annotationProcessor

netty-websocket-http1/src/main/java/com/jauntsdn/netty/handler/codec/http/websocketx/MaskingWebSocketEncoder.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ public WebSocketFrameFactory frameFactory(ChannelHandlerContext ctx) {
4848
return FrameFactory.INSTANCE;
4949
}
5050

51-
static class FrameFactory implements WebSocketFrameFactory, WebSocketFrameFactory.Encoder {
51+
static class FrameFactory
52+
implements WebSocketFrameFactory,
53+
WebSocketFrameFactory.Encoder,
54+
WebSocketFrameFactory.BulkEncoder {
5255
static final int PREFIX_SIZE_SMALL = 6;
5356
static final int BINARY_FRAME_SMALL =
5457
OPCODE_BINARY << 8 | /*FIN*/ (byte) 1 << 15 | /*MASK*/ (byte) 1 << 7;
@@ -145,6 +148,11 @@ public Encoder encoder() {
145148
return this;
146149
}
147150

151+
@Override
152+
public BulkEncoder bulkEncoder() {
153+
return this;
154+
}
155+
148156
@Override
149157
public ByteBuf encodeBinaryFrame(ByteBuf binaryFrame) {
150158
int frameSize = binaryFrame.readableBytes();
@@ -168,6 +176,30 @@ public ByteBuf encodeBinaryFrame(ByteBuf binaryFrame) {
168176
throw new IllegalArgumentException(payloadSizeLimit(payloadSize, 65_535));
169177
}
170178

179+
@Override
180+
public int encodeBinaryFramePrefix(ByteBuf byteBuf, int payloadSize) {
181+
if (payloadSize <= 125) {
182+
byteBuf.writeShort(BINARY_FRAME_SMALL | payloadSize);
183+
int mask = mask();
184+
byteBuf.writeInt(mask);
185+
return mask;
186+
}
187+
188+
if (payloadSize <= 65_535) {
189+
int mask = mask();
190+
byteBuf.writeLong(((BINARY_FRAME_MEDIUM | (long) payloadSize) << 32) | mask);
191+
return mask;
192+
}
193+
throw new IllegalArgumentException(payloadSizeLimit(payloadSize, 65_535));
194+
}
195+
196+
@Override
197+
public ByteBuf maskBinaryFrame(ByteBuf byteBuf, int mask, int payloadSize) {
198+
int end = byteBuf.writerIndex();
199+
int start = end - payloadSize;
200+
return mask(mask, byteBuf, start, end);
201+
}
202+
171203
@Override
172204
public int sizeofBinaryFrame(int payloadSize) {
173205
if (payloadSize <= 125) {

0 commit comments

Comments
 (0)