Skip to content

Commit 0e5cf30

Browse files
committed
Add QUIC and HTTP/3 transport examples
Signed-off-by: jeroen.veltman <jeroen.veltman@nextend.nl>
1 parent b53e333 commit 0e5cf30

58 files changed

Lines changed: 4530 additions & 58 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

rsocket-examples/build.gradle

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,18 @@ plugins {
1818
id 'java'
1919
}
2020

21+
def netty_incubator_quic_version = "0.0.73.Final"
22+
def netty_incubator_http3_version = "0.0.30.Final"
23+
2124
dependencies {
2225
implementation project(':rsocket-core')
2326
implementation project(':rsocket-load-balancer')
27+
implementation project(':rsocket-transport-h3')
2428
implementation project(':rsocket-transport-local')
2529
implementation project(':rsocket-transport-netty')
30+
implementation project(':rsocket-transport-quic')
31+
implementation "io.netty.incubator:netty-incubator-codec-classes-quic:${netty_incubator_quic_version}"
32+
implementation "io.netty.incubator:netty-incubator-codec-http3:${netty_incubator_http3_version}"
2633

2734
implementation "io.micrometer:micrometer-core"
2835
implementation "io.micrometer:micrometer-tracing"
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Copyright 2015-2026 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.rsocket.examples.transport.h3;
18+
19+
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
20+
import io.netty.handler.ssl.util.SelfSignedCertificate;
21+
import io.netty.incubator.codec.http3.Http3;
22+
import io.netty.incubator.codec.quic.QuicSslContext;
23+
import io.netty.incubator.codec.quic.QuicSslContextBuilder;
24+
import io.rsocket.transport.netty.Http3TransportConfig;
25+
import io.rsocket.transport.netty.QuicTransportConfig;
26+
import io.rsocket.transport.netty.client.Http3ClientTransport;
27+
import io.rsocket.transport.netty.server.Http3ServerTransport;
28+
import java.net.InetSocketAddress;
29+
30+
public final class Http3TransportFactory {
31+
32+
private static final String DEFAULT_HOST = "localhost";
33+
private static final String DEFAULT_PATH = "/rsocket";
34+
private static final SelfSignedCertificate CERTIFICATE = createCertificate();
35+
private static final Http3TransportConfig SERVER_CONFIG = createServerConfig();
36+
private static final Http3TransportConfig CLIENT_CONFIG = createClientConfig();
37+
38+
static {
39+
Runtime.getRuntime().addShutdownHook(new Thread(CERTIFICATE::delete));
40+
}
41+
42+
private Http3TransportFactory() {}
43+
44+
public static Http3ClientTransport client(int port) {
45+
return client(DEFAULT_HOST, port);
46+
}
47+
48+
public static Http3ClientTransport client(String host, int port) {
49+
return Http3ClientTransport.create(host, port).config(CLIENT_CONFIG);
50+
}
51+
52+
public static Http3ClientTransport client(InetSocketAddress address) {
53+
return client(address.getHostString(), address.getPort());
54+
}
55+
56+
public static Http3ServerTransport server(int port) {
57+
return server(DEFAULT_HOST, port);
58+
}
59+
60+
public static Http3ServerTransport server(String host, int port) {
61+
return Http3ServerTransport.create(host, port).config(SERVER_CONFIG);
62+
}
63+
64+
private static SelfSignedCertificate createCertificate() {
65+
try {
66+
return new SelfSignedCertificate();
67+
} catch (Exception e) {
68+
throw new ExceptionInInitializerError(e);
69+
}
70+
}
71+
72+
private static Http3TransportConfig createServerConfig() {
73+
try {
74+
QuicSslContext serverSslContext =
75+
QuicSslContextBuilder.forServer(
76+
CERTIFICATE.privateKey(), null, CERTIFICATE.certificate())
77+
.applicationProtocols(Http3.supportedApplicationProtocols())
78+
.build();
79+
80+
return Http3TransportConfig.builder()
81+
.path(DEFAULT_PATH)
82+
.quicConfig(QuicTransportConfig.builder().sslContext(serverSslContext).build())
83+
.build();
84+
} catch (Exception e) {
85+
throw new ExceptionInInitializerError(e);
86+
}
87+
}
88+
89+
private static Http3TransportConfig createClientConfig() {
90+
try {
91+
QuicSslContext clientSslContext =
92+
QuicSslContextBuilder.forClient()
93+
.trustManager(InsecureTrustManagerFactory.INSTANCE)
94+
.applicationProtocols(Http3.supportedApplicationProtocols())
95+
.build();
96+
97+
return Http3TransportConfig.builder()
98+
.path(DEFAULT_PATH)
99+
.quicConfig(
100+
QuicTransportConfig.builder()
101+
.sslContext(clientSslContext)
102+
.validateCertificates(false)
103+
.build())
104+
.build();
105+
} catch (Exception e) {
106+
throw new ExceptionInInitializerError(e);
107+
}
108+
}
109+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2015-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.rsocket.examples.transport.h3.channel;
18+
19+
import io.rsocket.Payload;
20+
import io.rsocket.RSocket;
21+
import io.rsocket.SocketAcceptor;
22+
import io.rsocket.core.RSocketConnector;
23+
import io.rsocket.core.RSocketServer;
24+
import io.rsocket.examples.transport.h3.Http3TransportFactory;
25+
import io.rsocket.util.DefaultPayload;
26+
import java.time.Duration;
27+
import org.slf4j.Logger;
28+
import org.slf4j.LoggerFactory;
29+
import reactor.core.publisher.Flux;
30+
31+
public final class ChannelEchoClient {
32+
33+
private static final Logger logger = LoggerFactory.getLogger(ChannelEchoClient.class);
34+
35+
public static void main(String[] args) {
36+
37+
SocketAcceptor echoAcceptor =
38+
SocketAcceptor.forRequestChannel(
39+
payloads ->
40+
Flux.from(payloads)
41+
.map(Payload::getDataUtf8)
42+
.map(s -> "Echo: " + s)
43+
.map(DefaultPayload::create));
44+
45+
RSocketServer.create(echoAcceptor).bindNow(Http3TransportFactory.server("localhost", 7000));
46+
47+
RSocket socket =
48+
RSocketConnector.connectWith(Http3TransportFactory.client("localhost", 7000)).block();
49+
50+
socket
51+
.requestChannel(
52+
Flux.interval(Duration.ofMillis(1000)).map(i -> DefaultPayload.create("Hello")))
53+
.map(Payload::getDataUtf8)
54+
.doOnNext(logger::debug)
55+
.take(10)
56+
.doFinally(signalType -> socket.dispose())
57+
.then()
58+
.block();
59+
}
60+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package io.rsocket.examples.transport.h3.client;
2+
3+
import io.rsocket.Payload;
4+
import io.rsocket.RSocket;
5+
import io.rsocket.SocketAcceptor;
6+
import io.rsocket.core.RSocketClient;
7+
import io.rsocket.core.RSocketConnector;
8+
import io.rsocket.core.RSocketServer;
9+
import io.rsocket.examples.transport.h3.Http3TransportFactory;
10+
import io.rsocket.util.DefaultPayload;
11+
import java.time.Duration;
12+
import org.slf4j.Logger;
13+
import org.slf4j.LoggerFactory;
14+
import reactor.core.publisher.Mono;
15+
import reactor.util.retry.Retry;
16+
17+
public class RSocketClientExample {
18+
static final Logger logger = LoggerFactory.getLogger(RSocketClientExample.class);
19+
20+
public static void main(String[] args) {
21+
22+
RSocketServer.create(
23+
SocketAcceptor.forRequestResponse(
24+
p -> {
25+
String data = p.getDataUtf8();
26+
logger.info("Received request data {}", data);
27+
28+
Payload responsePayload = DefaultPayload.create("Echo: " + data);
29+
p.release();
30+
31+
return Mono.just(responsePayload);
32+
}))
33+
.bind(Http3TransportFactory.server("localhost", 7000))
34+
.delaySubscription(Duration.ofSeconds(5))
35+
.doOnNext(cc -> logger.info("Server started on the address : {}", cc.address()))
36+
.block();
37+
38+
Mono<RSocket> source =
39+
RSocketConnector.create()
40+
.reconnect(Retry.backoff(50, Duration.ofMillis(500)))
41+
.connect(Http3TransportFactory.client("localhost", 7000));
42+
43+
RSocketClient.from(source)
44+
.requestResponse(Mono.just(DefaultPayload.create("Test Request")))
45+
.doOnSubscribe(s -> logger.info("Executing Request"))
46+
.doOnNext(
47+
d -> {
48+
logger.info("Received response data {}", d.getDataUtf8());
49+
d.release();
50+
})
51+
.repeat(10)
52+
.blockLast();
53+
}
54+
}

0 commit comments

Comments
 (0)