From f840141652a4ecf6455ebd3805b6262ddd06ebf8 Mon Sep 17 00:00:00 2001 From: Leo Li <269739606@qq.com> Date: Wed, 26 Jan 2022 10:56:50 +0800 Subject: [PATCH 1/2] Allow custom RSocket WebsocketServerSpecs to be defined See gh-29567 --- .../rsocket/RSocketProperties.java | 68 +++++++++++++++++++ .../RSocketServerAutoConfiguration.java | 2 +- .../RSocketWebSocketNettyRouteProvider.java | 14 +++- 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketProperties.java index f1921f23885..c43895c47ab 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketProperties.java @@ -73,6 +73,8 @@ public class RSocketProperties { @NestedConfigurationProperty private Ssl ssl; + private Spec spec = new Spec(); + public Integer getPort() { return this.port; } @@ -121,6 +123,72 @@ public class RSocketProperties { this.ssl = ssl; } + public Spec getSpec() { + return this.spec; + } + + public void setSpec(Spec spec) { + this.spec = spec; + } + + public static class Spec { + + /** + * Sub-protocol to use in websocket handshake signature. + */ + private String protocols; + + /** + * Specifies a custom maximum allowable frame payload length. 65536 by + * default. + */ + private int maxFramePayloadLength = 65536; + + /** + * Flag whether to proxy websocket ping frames or respond to them. + */ + private boolean handlePing; + + /** + * Flag whether the websocket compression extension is enabled if the client + * request presents websocket extensions headers. + */ + private boolean compress; + + public String getProtocols() { + return this.protocols; + } + + public void setProtocols(String protocols) { + this.protocols = protocols; + } + + public int getMaxFramePayloadLength() { + return this.maxFramePayloadLength; + } + + public void setMaxFramePayloadLength(int maxFramePayloadLength) { + this.maxFramePayloadLength = maxFramePayloadLength; + } + + public boolean isHandlePing() { + return this.handlePing; + } + + public void setHandlePing(boolean handlePing) { + this.handlePing = handlePing; + } + + public boolean isCompress() { + return this.compress; + } + + public void setCompress(boolean compress) { + this.compress = compress; + } + + } + } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketServerAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketServerAutoConfiguration.java index 96bbbc454ba..6c5394d5904 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketServerAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketServerAutoConfiguration.java @@ -73,7 +73,7 @@ public class RSocketServerAutoConfiguration { RSocketWebSocketNettyRouteProvider rSocketWebsocketRouteProvider(RSocketProperties properties, RSocketMessageHandler messageHandler, ObjectProvider customizers) { return new RSocketWebSocketNettyRouteProvider(properties.getServer().getMappingPath(), - messageHandler.responder(), customizers.orderedStream()); + properties.getServer().getSpec(), messageHandler.responder(), customizers.orderedStream()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketWebSocketNettyRouteProvider.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketWebSocketNettyRouteProvider.java index 5cb8d7f374f..9d9ddc91dd5 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketWebSocketNettyRouteProvider.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketWebSocketNettyRouteProvider.java @@ -24,7 +24,9 @@ import io.rsocket.core.RSocketServer; import io.rsocket.transport.ServerTransport; import io.rsocket.transport.netty.server.WebsocketRouteTransport; import reactor.netty.http.server.HttpServerRoutes; +import reactor.netty.http.server.WebsocketServerSpec; +import org.springframework.boot.autoconfigure.rsocket.RSocketProperties.Server.Spec; import org.springframework.boot.rsocket.server.RSocketServerCustomizer; import org.springframework.boot.web.embedded.netty.NettyRouteProvider; @@ -32,6 +34,7 @@ import org.springframework.boot.web.embedded.netty.NettyRouteProvider; * {@link NettyRouteProvider} that configures an RSocket Websocket endpoint. * * @author Brian Clozel + * @author Leo Li */ class RSocketWebSocketNettyRouteProvider implements NettyRouteProvider { @@ -41,11 +44,14 @@ class RSocketWebSocketNettyRouteProvider implements NettyRouteProvider { private final List customizers; - RSocketWebSocketNettyRouteProvider(String mappingPath, SocketAcceptor socketAcceptor, + private final Spec spec; + + RSocketWebSocketNettyRouteProvider(String mappingPath, Spec spec, SocketAcceptor socketAcceptor, Stream customizers) { this.mappingPath = mappingPath; this.socketAcceptor = socketAcceptor; this.customizers = customizers.toList(); + this.spec = spec; } @Override @@ -53,7 +59,11 @@ class RSocketWebSocketNettyRouteProvider implements NettyRouteProvider { RSocketServer server = RSocketServer.create(this.socketAcceptor); this.customizers.forEach((customizer) -> customizer.customize(server)); ServerTransport.ConnectionAcceptor connectionAcceptor = server.asConnectionAcceptor(); - return httpServerRoutes.ws(this.mappingPath, WebsocketRouteTransport.newHandler(connectionAcceptor)); + WebsocketServerSpec.Builder build = (this.spec.getProtocols() == null) ? WebsocketServerSpec.builder() + : WebsocketServerSpec.builder().protocols(this.spec.getProtocols()); + return httpServerRoutes.ws(this.mappingPath, WebsocketRouteTransport.newHandler(connectionAcceptor), + build.maxFramePayloadLength(this.spec.getMaxFramePayloadLength()).handlePing(this.spec.isHandlePing()) + .compress(this.spec.isCompress()).build()); } } From b0438b0f034b1f501a26eba17dea8176e6fb6548 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 26 Jul 2023 13:42:17 +0200 Subject: [PATCH 2/2] Polish "Allow custom RSocket WebsocketServerSpecs to be defined" See gh-29567 --- .../rsocket/RSocketProperties.java | 22 ++++------ .../RSocketServerAutoConfiguration.java | 18 +++++++- .../RSocketWebSocketNettyRouteProvider.java | 24 ++++++----- .../rsocket/RSocketPropertiesTests.java | 43 +++++++++++++++++++ 4 files changed, 82 insertions(+), 25 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/rsocket/RSocketPropertiesTests.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketProperties.java index c43895c47ab..dd16caad719 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -73,7 +73,7 @@ public class RSocketProperties { @NestedConfigurationProperty private Ssl ssl; - private Spec spec = new Spec(); + private final Spec spec = new Spec(); public Integer getPort() { return this.port; @@ -127,10 +127,6 @@ public class RSocketProperties { return this.spec; } - public void setSpec(Spec spec) { - this.spec = spec; - } - public static class Spec { /** @@ -139,19 +135,17 @@ public class RSocketProperties { private String protocols; /** - * Specifies a custom maximum allowable frame payload length. 65536 by - * default. + * Maximum allowable frame payload length. */ - private int maxFramePayloadLength = 65536; + private DataSize maxFramePayloadLength = DataSize.ofBytes(65536); /** - * Flag whether to proxy websocket ping frames or respond to them. + * Whether to proxy websocket ping frames or respond to them. */ private boolean handlePing; /** - * Flag whether the websocket compression extension is enabled if the client - * request presents websocket extensions headers. + * Whether the websocket compression extension is enabled. */ private boolean compress; @@ -163,11 +157,11 @@ public class RSocketProperties { this.protocols = protocols; } - public int getMaxFramePayloadLength() { + public DataSize getMaxFramePayloadLength() { return this.maxFramePayloadLength; } - public void setMaxFramePayloadLength(int maxFramePayloadLength) { + public void setMaxFramePayloadLength(DataSize maxFramePayloadLength) { this.maxFramePayloadLength = maxFramePayloadLength; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketServerAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketServerAutoConfiguration.java index 6c5394d5904..e329ef099e3 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketServerAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketServerAutoConfiguration.java @@ -16,10 +16,13 @@ package org.springframework.boot.autoconfigure.rsocket; +import java.util.function.Consumer; + import io.rsocket.core.RSocketServer; import io.rsocket.frame.decoder.PayloadDecoder; import io.rsocket.transport.netty.server.TcpServerTransport; import reactor.netty.http.server.HttpServer; +import reactor.netty.http.server.WebsocketServerSpec.Builder; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -31,6 +34,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.reactor.netty.ReactorNettyConfigurations; +import org.springframework.boot.autoconfigure.rsocket.RSocketProperties.Server.Spec; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.boot.rsocket.context.RSocketServerBootstrap; @@ -46,6 +50,7 @@ import org.springframework.core.io.buffer.NettyDataBufferFactory; import org.springframework.http.client.reactive.ReactorResourceFactory; import org.springframework.messaging.rsocket.RSocketStrategies; import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler; +import org.springframework.util.unit.DataSize; /** * {@link EnableAutoConfiguration Auto-configuration} for RSocket servers. In the case of @@ -73,7 +78,18 @@ public class RSocketServerAutoConfiguration { RSocketWebSocketNettyRouteProvider rSocketWebsocketRouteProvider(RSocketProperties properties, RSocketMessageHandler messageHandler, ObjectProvider customizers) { return new RSocketWebSocketNettyRouteProvider(properties.getServer().getMappingPath(), - properties.getServer().getSpec(), messageHandler.responder(), customizers.orderedStream()); + messageHandler.responder(), customizeWebsocketServerSpec(properties.getServer().getSpec()), + customizers.orderedStream()); + } + + private Consumer customizeWebsocketServerSpec(Spec spec) { + return (builder) -> { + PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); + map.from(spec.getProtocols()).to(builder::protocols); + map.from(spec.getMaxFramePayloadLength()).asInt(DataSize::toBytes).to(builder::maxFramePayloadLength); + map.from(spec.isHandlePing()).to(builder::handlePing); + map.from(spec.isCompress()).to(builder::compress); + }; } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketWebSocketNettyRouteProvider.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketWebSocketNettyRouteProvider.java index 9d9ddc91dd5..f70f9d41d54 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketWebSocketNettyRouteProvider.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketWebSocketNettyRouteProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.boot.autoconfigure.rsocket; import java.util.List; +import java.util.function.Consumer; import java.util.stream.Stream; import io.rsocket.SocketAcceptor; @@ -25,8 +26,8 @@ import io.rsocket.transport.ServerTransport; import io.rsocket.transport.netty.server.WebsocketRouteTransport; import reactor.netty.http.server.HttpServerRoutes; import reactor.netty.http.server.WebsocketServerSpec; +import reactor.netty.http.server.WebsocketServerSpec.Builder; -import org.springframework.boot.autoconfigure.rsocket.RSocketProperties.Server.Spec; import org.springframework.boot.rsocket.server.RSocketServerCustomizer; import org.springframework.boot.web.embedded.netty.NettyRouteProvider; @@ -44,14 +45,14 @@ class RSocketWebSocketNettyRouteProvider implements NettyRouteProvider { private final List customizers; - private final Spec spec; + private final Consumer serverSpecCustomizer; - RSocketWebSocketNettyRouteProvider(String mappingPath, Spec spec, SocketAcceptor socketAcceptor, - Stream customizers) { + RSocketWebSocketNettyRouteProvider(String mappingPath, SocketAcceptor socketAcceptor, + Consumer serverSpecCustomizer, Stream customizers) { this.mappingPath = mappingPath; this.socketAcceptor = socketAcceptor; + this.serverSpecCustomizer = serverSpecCustomizer; this.customizers = customizers.toList(); - this.spec = spec; } @Override @@ -59,11 +60,14 @@ class RSocketWebSocketNettyRouteProvider implements NettyRouteProvider { RSocketServer server = RSocketServer.create(this.socketAcceptor); this.customizers.forEach((customizer) -> customizer.customize(server)); ServerTransport.ConnectionAcceptor connectionAcceptor = server.asConnectionAcceptor(); - WebsocketServerSpec.Builder build = (this.spec.getProtocols() == null) ? WebsocketServerSpec.builder() - : WebsocketServerSpec.builder().protocols(this.spec.getProtocols()); return httpServerRoutes.ws(this.mappingPath, WebsocketRouteTransport.newHandler(connectionAcceptor), - build.maxFramePayloadLength(this.spec.getMaxFramePayloadLength()).handlePing(this.spec.isHandlePing()) - .compress(this.spec.isCompress()).build()); + createWebsocketServerSpec()); + } + + private WebsocketServerSpec createWebsocketServerSpec() { + WebsocketServerSpec.Builder builder = WebsocketServerSpec.builder(); + this.serverSpecCustomizer.accept(builder); + return builder.build(); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/rsocket/RSocketPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/rsocket/RSocketPropertiesTests.java new file mode 100644 index 00000000000..eefd1b7212d --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/rsocket/RSocketPropertiesTests.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.rsocket; + +import org.junit.jupiter.api.Test; +import reactor.netty.http.server.WebsocketServerSpec; + +import org.springframework.boot.autoconfigure.rsocket.RSocketProperties.Server.Spec; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link RSocketProperties}. + * + * @author Stephane Nicoll + */ +class RSocketPropertiesTests { + + @Test + void defaultServerSpecValuesAreConsistent() { + WebsocketServerSpec spec = WebsocketServerSpec.builder().build(); + Spec properties = new RSocketProperties().getServer().getSpec(); + assertThat(properties.getProtocols()).isEqualTo(spec.protocols()); + assertThat(properties.getMaxFramePayloadLength().toBytes()).isEqualTo(spec.maxFramePayloadLength()); + assertThat(properties.isHandlePing()).isEqualTo(spec.handlePing()); + assertThat(properties.isCompress()).isEqualTo(spec.compress()); + } + +}