Skip to content

Commit

Permalink
Merge branch 'release/1.3.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
mostroverkhov committed Jul 25, 2024
2 parents f726d7a + 9054288 commit 16b53e8
Show file tree
Hide file tree
Showing 55 changed files with 2,910 additions and 948 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
58 changes: 49 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,30 @@
[![Build](https://github.com/jauntsdn/netty-websocket-http2/actions/workflows/ci-build.yml/badge.svg)](https://github.com/jauntsdn/netty-websocket-http2/actions/workflows/ci-build.yml)
# netty-websocket-http2

Netty based implementation of [rfc8441](https://tools.ietf.org/html/rfc8441) - bootstrapping websockets with http/2
Netty based implementation of [rfc8441](https://tools.ietf.org/html/rfc8441) - bootstrapping websockets with http/2, and multiprotocol support (websocket-over-http1, websocket-over-http2).

Library addresses 2 use cases: for application servers and clients,
It is transparent use of existing http1 websocket handlers on top of http2 streams; for gateways/proxies,
It is websockets-over-http2 support with no http1 dependencies and minimal overhead.
### use cases

* Websocket channel API

for application servers and clients, It provides transparent use of existing http1 websocket handlers on top of http2 streams. Compatible with
callbacks codec (described below).

* Websocket handshake only API

for gateways/proxies, It provides websockets-over-http2 support with no http1 dependencies and minimal overhead.

* Websocket multiprotocol

for application servers, It provides transparent use of existing http1 websocket handlers to process both http1 and http2 websockets.
Compatible with callbacks codec (described below).

[https://jauntsdn.com/post/netty-websocket-http2/](https://jauntsdn.com/post/netty-websocket-http2/)

### much faster http1 codec
Integration with [jauntsdn/netty-websocket-http1](https://github.com/jauntsdn/netty-websocket-http2/tree/develop/netty-websocket-http2-callbacks-codec) codec for websocket-http1
Integration with [jauntsdn/netty-websocket-http1](https://github.com/jauntsdn/netty-websocket-http2/tree/develop/netty-websocket-http2-callbacks-codec) codec (callbacks codec) for websocket-http1
frames processing [improves](https://github.com/jauntsdn/netty-websocket-http2/tree/develop/netty-websocket-http2-perftest/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/perftest/callbackscodec)
throughput 1.4x - 1.7x for small messages.
throughput 1.4x - 1.7x for small messages compared to one provided by netty (default codec).

### websocket channel API
Intended for application servers and clients.
Expand All @@ -25,6 +37,7 @@ EchoWebSocketHandler http1WebSocketHandler = new EchoWebSocketHandler();
Http2WebSocketServerHandler http2webSocketHandler =
Http2WebSocketServerBuilder.create()
.codec(Http1WebSocketCodec.DEFAULT)
.acceptor(
(ctx, path, subprotocols, request, response) -> {
switch (path) {
Expand Down Expand Up @@ -70,6 +83,7 @@ EchoWebSocketHandler http1WebSocketHandler = new EchoWebSocketHandler();
Http2WebSocketClientHandler http2WebSocketClientHandler =
Http2WebSocketClientBuilder.create()
.codec(Http1WebSocketCodec.DEFAULT)
.handshakeTimeoutMillis(15_000)
.build();
Expand All @@ -87,7 +101,7 @@ EchoWebSocketHandler http1WebSocketHandler = new EchoWebSocketHandler();
Http2WebSocketClientHandshaker handShaker = Http2WebSocketClientHandshaker.create(channel);
Http2Headers headers =
new DefaultHttp2Headers().set("user-agent", "jauntsdn-websocket-http2-client/1.2.5");
new DefaultHttp2Headers().set("user-agent", "jauntsdn-websocket-http2-client/1.2.6");
ChannelFuture handshakeFuture =
/*http1 websocket handler*/
handShaker.handshake("/echo", headers, new EchoWebSocketHandler());
Expand Down Expand Up @@ -127,6 +141,26 @@ Runnable demo is available in `netty-websocket-http2-example` module -
[handshakeserver](https://github.com/jauntsdn/netty-websocket-http2/blob/develop/netty-websocket-http2-example/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/example/handshakeserver/Main.java),
[channelclient](https://github.com/jauntsdn/netty-websocket-http2/blob/develop/netty-websocket-http2-example/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/example/channelclient/Main.java).

### websocket multiprotocol
Provides transparent use of existing http1 websocket handlers to process both http1 and http2 websockets.

* Server
```groovy
MultiProtocolWebSocketServerHandler multiprotocolHandler =
MultiprotocolWebSocketServerBuilder.create()
.path("/echo")
.subprotocols("echo.jauntsdn.com")
.defaultCodec()
.handler(new DefaultEchoWebSocketHandler())
.build();
ch.pipeline().addLast(sslHandler, multiprotocolHandler);
```

Runnable demo is available in `netty-websocket-http2-example` module -
[multiprotocol.server.defaultcodec](https://github.com/jauntsdn/netty-websocket-http2/blob/develop/netty-websocket-http2-example/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/example/multiprotocol/server/defaultcodec/Main.java),
[multiprotocol.server.callbackscodec](https://github.com/jauntsdn/netty-websocket-http2/blob/develop/netty-websocket-http2-example/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/example/multiprotocol/server/callbackscodec/Main.java),
[multiprotocol.client.defaultcodec](https://github.com/jauntsdn/netty-websocket-http2/blob/develop/netty-websocket-http2-example/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/example/multiprotocol/client/Main.java),

### configuration
Initial settings of server http2 codecs (`Http2ConnectionHandler` or `Http2FrameCodec`) should contain [SETTINGS_ENABLE_CONNECT_PROTOCOL=1](https://tools.ietf.org/html/rfc8441#section-9.1)
parameter to advertise websocket-over-http2 support.
Expand Down Expand Up @@ -181,6 +215,12 @@ Events are fired on parent channel, also on websocket channel if one gets create
* `Http2WebSocketHandshakeErrorEvent(webSocketId, path, subprotocols, timestampNanos, responseHeaders, error)`
* `Http2WebSocketHandshakeSuccessEvent(webSocketId, path, subprotocols, timestampNanos, responseHeaders)`

These events are accompanied by transport agnostic variants

* `WebSocketHandshakeStartEvent(websocketId, path, subprotocols, timestampNanos, requestHeaders)`
* `WebSocketHandshakeErrorEvent(webSocketId, path, subprotocols, timestampNanos, responseHeaders, error)`
* `WebSocketHandshakeSuccessEvent(webSocketId, path, subprotocols, timestampNanos, responseHeaders)`

#### close events

Outbound `Http2WebSocketLocalCloseEvent` on websocket channel pipeline closes
Expand Down Expand Up @@ -265,7 +305,7 @@ the results are as follows (measured over time spans of 5 seconds):

* `channelserver, channelclient` packages for websocket subchannel API demos.
* `handshakeserver, channelclient` packages for handshake only API demo.
* `multiprotocolserver, multiprotocolclient` packages for demo of server handling htt1/http2 websockets on the same port.
* `multiprotocol` packages for demo of server handling htt1/http2 websockets on the same port.
* `lwsclient` package for client demo that runs against [https://libwebsockets.org/testserver/](https://libwebsockets.org/testserver/) which hosts websocket-over-http2
server implemented with [libwebsockets](https://github.com/warmcat/libwebsockets) - popular C-based networking library.

Expand All @@ -286,7 +326,7 @@ repositories {
}
dependencies {
implementation 'com.jauntsdn.netty:netty-websocket-http2:1.2.5'
implementation 'com.jauntsdn.netty:netty-websocket-http2:1.2.6'
}
```

Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ subprojects {

repositories {
mavenCentral()
mavenLocal()
}

plugins.withType(JavaPlugin) {
Expand Down
6 changes: 3 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
group=com.jauntsdn.netty
version=1.2.6
version=1.3.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
jauntNettyWebsocketHttp1=1.1.4
nettyVersion=4.1.112.Final
jauntNettyWebsocketHttp1=1.2.0
nettyTcnativeVersion=2.0.65.Final
hdrHistogramVersion=2.1.12
slf4jVersion=1.7.36
Expand Down
3 changes: 3 additions & 0 deletions multiprotocol_callbacks_server.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

./gradlew netty-websocket-http2-example:runMultiProtocolCallbacksServer
3 changes: 3 additions & 0 deletions multiprotocol_default_server.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

./gradlew netty-websocket-http2-example:runMultiProtocolDefaultServer
3 changes: 0 additions & 3 deletions multiprotocol_server.sh

This file was deleted.

20 changes: 10 additions & 10 deletions netty-websocket-http2-callbacks-codec/gradle.lockfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ 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
com.jauntsdn.netty:netty-websocket-http1:1.1.4=compileClasspath
io.netty:netty-buffer:4.1.109.Final=compileClasspath
io.netty:netty-codec-http2:4.1.109.Final=compileClasspath
io.netty:netty-codec-http:4.1.109.Final=compileClasspath
io.netty:netty-codec:4.1.109.Final=compileClasspath
io.netty:netty-common:4.1.109.Final=compileClasspath
io.netty:netty-handler:4.1.109.Final=compileClasspath
io.netty:netty-resolver:4.1.109.Final=compileClasspath
io.netty:netty-transport-native-unix-common:4.1.109.Final=compileClasspath
io.netty:netty-transport:4.1.109.Final=compileClasspath
com.jauntsdn.netty:netty-websocket-http1:1.2.0=compileClasspath
io.netty:netty-buffer:4.1.112.Final=compileClasspath
io.netty:netty-codec-http2:4.1.112.Final=compileClasspath
io.netty:netty-codec-http:4.1.112.Final=compileClasspath
io.netty:netty-codec:4.1.112.Final=compileClasspath
io.netty:netty-common:4.1.112.Final=compileClasspath
io.netty:netty-handler:4.1.112.Final=compileClasspath
io.netty:netty-resolver:4.1.112.Final=compileClasspath
io.netty:netty-transport-native-unix-common:4.1.112.Final=compileClasspath
io.netty:netty-transport:4.1.112.Final=compileClasspath
org.codehaus.mojo:animal-sniffer-annotations:1.14=googleJavaFormat1.6
empty=annotationProcessor
13 changes: 10 additions & 3 deletions netty-websocket-http2-example/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ description = "Netty based implementation of rfc8441 - bootstrapping websockets

dependencies {
implementation project(":netty-websocket-http2")
implementation project(":netty-websocket-multiprotocol")
implementation project(":netty-websocket-http2-callbacks-codec")
implementation "org.slf4j:slf4j-api"
runtimeOnly "io.netty:netty-tcnative-boringssl-static::${osdetector.classifier}"
runtimeOnly "ch.qos.logback:logback-classic"
Expand All @@ -37,9 +39,14 @@ task runChannelServer(type: JavaExec) {
mainClass = "com.jauntsdn.netty.handler.codec.http2.websocketx.example.channelserver.Main"
}

task runMultiProtocolServer(type: JavaExec) {
task runMultiProtocolDefaultServer(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
mainClass = "com.jauntsdn.netty.handler.codec.http2.websocketx.example.multiprotocolserver.Main"
mainClass = "com.jauntsdn.netty.handler.codec.http2.websocketx.example.multiprotocol.server.defaultcodec.Main"
}

task runMultiProtocolCallbacksServer(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
mainClass = "com.jauntsdn.netty.handler.codec.http2.websocketx.example.multiprotocol.server.callbackscodec.Main"
}

task runChannelClient(type: JavaExec) {
Expand All @@ -54,6 +61,6 @@ task runLwsClient(type: JavaExec) {

task runMultiProtocolClient(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
mainClass = "com.jauntsdn.netty.handler.codec.http2.websocketx.example.multiprotocolclient.Main"
mainClass = "com.jauntsdn.netty.handler.codec.http2.websocketx.example.multiprotocol.client.Main"
}

Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@
package com.jauntsdn.netty.handler.codec.http2.websocketx.example;

import io.netty.handler.codec.http2.Http2SecurityUtil;
import io.netty.handler.ssl.*;
import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import java.io.InputStream;
import java.security.KeyStore;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,35 @@

package com.jauntsdn.netty.handler.codec.http2.websocketx.example.channelclient;

import static com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketEvent.*;

import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketClientBuilder;
import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketClientHandler;
import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketClientHandshaker;
import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketEvent.Http2WebSocketHandshakeErrorEvent;
import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketEvent.Http2WebSocketHandshakeStartEvent;
import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketEvent.Http2WebSocketHandshakeSuccessEvent;
import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketEvent.Http2WebSocketLifecycleEvent;
import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketEvent.Http2WebSocketLocalCloseEvent;
import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketEvent.Http2WebSocketStreamWeightUpdateEvent;
import com.jauntsdn.netty.handler.codec.http2.websocketx.example.Security;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketDecoderConfig;
import io.netty.handler.codec.http2.*;
import io.netty.handler.ssl.*;
import io.netty.handler.codec.http2.DefaultHttp2Headers;
import io.netty.handler.codec.http2.Http2FrameCodec;
import io.netty.handler.codec.http2.Http2FrameCodecBuilder;
import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import java.io.IOException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,45 @@

package com.jauntsdn.netty.handler.codec.http2.websocketx.example.channelserver;

import static com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketEvent.*;
import static io.netty.channel.ChannelHandler.*;

import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketAcceptor;
import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketEvent.Http2WebSocketHandshakeErrorEvent;
import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketEvent.Http2WebSocketHandshakeStartEvent;
import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketEvent.Http2WebSocketHandshakeSuccessEvent;
import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketEvent.Http2WebSocketLifecycleEvent;
import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketEvent.Http2WebSocketLocalCloseEvent;
import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketServerBuilder;
import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketServerHandler;
import com.jauntsdn.netty.handler.codec.http2.websocketx.example.Security;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketHandshakeException;
import io.netty.handler.codec.http2.*;
import io.netty.handler.codec.http2.DefaultHttp2DataFrame;
import io.netty.handler.codec.http2.DefaultHttp2GoAwayFrame;
import io.netty.handler.codec.http2.DefaultHttp2Headers;
import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame;
import io.netty.handler.codec.http2.Http2ChannelDuplexHandler;
import io.netty.handler.codec.http2.Http2DataFrame;
import io.netty.handler.codec.http2.Http2Error;
import io.netty.handler.codec.http2.Http2Frame;
import io.netty.handler.codec.http2.Http2FrameCodec;
import io.netty.handler.codec.http2.Http2FrameCodecBuilder;
import io.netty.handler.codec.http2.Http2FrameStream;
import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.codec.http2.Http2HeadersFrame;
import io.netty.handler.codec.http2.ReadOnlyHttp2Headers;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.AsciiString;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,20 @@
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http2.*;
import io.netty.handler.codec.http2.DefaultHttp2GoAwayFrame;
import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame;
import io.netty.handler.codec.http2.DefaultHttp2WindowUpdateFrame;
import io.netty.handler.codec.http2.Http2ChannelDuplexHandler;
import io.netty.handler.codec.http2.Http2DataFrame;
import io.netty.handler.codec.http2.Http2Error;
import io.netty.handler.codec.http2.Http2Frame;
import io.netty.handler.codec.http2.Http2FrameCodec;
import io.netty.handler.codec.http2.Http2FrameCodecBuilder;
import io.netty.handler.codec.http2.Http2FrameStream;
import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.codec.http2.Http2HeadersFrame;
import io.netty.handler.codec.http2.Http2ResetFrame;
import io.netty.handler.codec.http2.ReadOnlyHttp2Headers;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.AsciiString;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@
import com.jauntsdn.netty.handler.codec.http2.websocketx.Http2WebSocketClientHandshaker;
import com.jauntsdn.netty.handler.codec.http2.websocketx.example.Security;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
Expand Down
Loading

0 comments on commit 16b53e8

Please sign in to comment.