Skip to content

Commit f4258aa

Browse files
committed
Merge branch '2.4.x'
Closes gh-24544
2 parents 58f0e25 + 7fd4c53 commit f4258aa

File tree

3 files changed

+161
-22
lines changed

3 files changed

+161
-22
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyReactiveWebServerFactory.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,17 @@ public NettyReactiveWebServerFactory(int port) {
6969
public WebServer getWebServer(HttpHandler httpHandler) {
7070
HttpServer httpServer = createHttpServer();
7171
ReactorHttpHandlerAdapter handlerAdapter = new ReactorHttpHandlerAdapter(httpHandler);
72-
NettyWebServer webServer = new NettyWebServer(httpServer, handlerAdapter, this.lifecycleTimeout, getShutdown());
72+
NettyWebServer webServer = createNettyWebServer(httpServer, handlerAdapter, this.lifecycleTimeout,
73+
getShutdown());
7374
webServer.setRouteProviders(this.routeProviders);
7475
return webServer;
7576
}
7677

78+
NettyWebServer createNettyWebServer(HttpServer httpServer, ReactorHttpHandlerAdapter handlerAdapter,
79+
Duration lifecycleTimeout, Shutdown shutdown) {
80+
return new NettyWebServer(httpServer, handlerAdapter, lifecycleTimeout, shutdown);
81+
}
82+
7783
/**
7884
* Returns a mutable collection of the {@link NettyServerCustomizer}s that will be
7985
* applied to the Netty server builder.

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyWebServer.java

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.List;
2222
import java.util.function.BiFunction;
2323
import java.util.function.Predicate;
24+
import java.util.function.Supplier;
2425

2526
import io.netty.channel.group.DefaultChannelGroup;
2627
import io.netty.channel.unix.Errors.NativeIoException;
@@ -106,11 +107,44 @@ public void start() throws WebServerException {
106107
});
107108
throw new WebServerException("Unable to start Netty", ex);
108109
}
109-
logger.info("Netty started on port(s): " + getPort());
110+
if (this.disposableServer != null) {
111+
logger.info("Netty started" + getStartedOnMessage(this.disposableServer));
112+
}
110113
startDaemonAwaitThread(this.disposableServer);
111114
}
112115
}
113116

117+
private String getStartedOnMessage(DisposableServer server) {
118+
StringBuilder message = new StringBuilder();
119+
tryAppend(message, "port %s", () -> server.port());
120+
tryAppend(message, "path %s", () -> server.path());
121+
return (message.length() > 0) ? " on " + message : "";
122+
}
123+
124+
private void tryAppend(StringBuilder message, String format, Supplier<Object> supplier) {
125+
try {
126+
Object value = supplier.get();
127+
message.append((message.length() != 0) ? " " : "");
128+
message.append(String.format(format, value));
129+
}
130+
catch (UnsupportedOperationException ex) {
131+
}
132+
}
133+
134+
DisposableServer startHttpServer() {
135+
HttpServer server = this.httpServer;
136+
if (this.routeProviders.isEmpty()) {
137+
server = server.handle(this.handler);
138+
}
139+
else {
140+
server = server.route(this::applyRouteProviders);
141+
}
142+
if (this.lifecycleTimeout != null) {
143+
return server.bindNow(this.lifecycleTimeout);
144+
}
145+
return server.bindNow();
146+
}
147+
114148
private boolean isPermissionDenied(Throwable bindExceptionCause) {
115149
try {
116150
if (bindExceptionCause instanceof NativeIoException) {
@@ -131,20 +165,6 @@ public void shutDownGracefully(GracefulShutdownCallback callback) {
131165
this.gracefulShutdown.shutDownGracefully(callback);
132166
}
133167

134-
private DisposableServer startHttpServer() {
135-
HttpServer server = this.httpServer;
136-
if (this.routeProviders.isEmpty()) {
137-
server = server.handle(this.handler);
138-
}
139-
else {
140-
server = server.route(this::applyRouteProviders);
141-
}
142-
if (this.lifecycleTimeout != null) {
143-
return server.bindNow(this.lifecycleTimeout);
144-
}
145-
return server.bindNow();
146-
}
147-
148168
private void applyRouteProviders(HttpServerRoutes routes) {
149169
for (NettyRouteProvider provider : this.routeProviders) {
150170
routes = provider.apply(routes);
@@ -190,7 +210,12 @@ public void stop() throws WebServerException {
190210
@Override
191211
public int getPort() {
192212
if (this.disposableServer != null) {
193-
return this.disposableServer.port();
213+
try {
214+
return this.disposableServer.port();
215+
}
216+
catch (UnsupportedOperationException ex) {
217+
return -1;
218+
}
194219
}
195220
return 0;
196221
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/NettyReactiveWebServerFactoryTests.java

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,19 @@
1717
package org.springframework.boot.web.embedded.netty;
1818

1919
import java.net.ConnectException;
20+
import java.net.SocketAddress;
2021
import java.time.Duration;
2122
import java.util.Arrays;
2223

24+
import io.netty.channel.Channel;
2325
import org.awaitility.Awaitility;
2426
import org.junit.jupiter.api.Test;
2527
import org.mockito.InOrder;
28+
import reactor.core.CoreSubscriber;
29+
import reactor.core.Disposable;
2630
import reactor.core.publisher.Mono;
31+
import reactor.netty.DisposableChannel;
32+
import reactor.netty.DisposableServer;
2733
import reactor.netty.http.server.HttpServer;
2834
import reactor.test.StepVerifier;
2935

@@ -34,6 +40,7 @@
3440
import org.springframework.boot.web.server.Ssl;
3541
import org.springframework.http.MediaType;
3642
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
43+
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
3744
import org.springframework.web.reactive.function.BodyInserters;
3845
import org.springframework.web.reactive.function.client.WebClient;
3946

@@ -52,11 +59,6 @@
5259
*/
5360
class NettyReactiveWebServerFactoryTests extends AbstractReactiveWebServerFactoryTests {
5461

55-
@Override
56-
protected NettyReactiveWebServerFactory getFactory() {
57-
return new NettyReactiveWebServerFactory(0);
58-
}
59-
6062
@Test
6163
void exceptionIsThrownWhenPortIsAlreadyInUse() {
6264
AbstractReactiveWebServerFactory factory = getFactory();
@@ -68,6 +70,14 @@ void exceptionIsThrownWhenPortIsAlreadyInUse() {
6870
.satisfies(this::portMatchesRequirement).withCauseInstanceOf(Throwable.class);
6971
}
7072

73+
@Test
74+
void getPortWhenDisposableServerPortOperationIsUnsupportedReturnsMinusOne() {
75+
NettyReactiveWebServerFactory factory = new NoPortNettyReactiveWebServerFactory(0);
76+
this.webServer = factory.getWebServer(new EchoHandler());
77+
this.webServer.start();
78+
assertThat(this.webServer.getPort()).isEqualTo(-1);
79+
}
80+
7181
private void portMatchesRequirement(PortInUseException exception) {
7282
assertThat(exception.getPort()).isEqualTo(this.webServer.getPort());
7383
}
@@ -143,4 +153,102 @@ protected Mono<String> testSslWithAlias(String alias) {
143153
.retrieve().bodyToMono(String.class);
144154
}
145155

156+
@Override
157+
protected NettyReactiveWebServerFactory getFactory() {
158+
return new NettyReactiveWebServerFactory(0);
159+
}
160+
161+
static class NoPortNettyReactiveWebServerFactory extends NettyReactiveWebServerFactory {
162+
163+
NoPortNettyReactiveWebServerFactory(int port) {
164+
super(port);
165+
}
166+
167+
@Override
168+
NettyWebServer createNettyWebServer(HttpServer httpServer, ReactorHttpHandlerAdapter handlerAdapter,
169+
Duration lifecycleTimeout, Shutdown shutdown) {
170+
return new NoPortNettyWebServer(httpServer, handlerAdapter, lifecycleTimeout, shutdown);
171+
}
172+
173+
}
174+
175+
static class NoPortNettyWebServer extends NettyWebServer {
176+
177+
NoPortNettyWebServer(HttpServer httpServer, ReactorHttpHandlerAdapter handlerAdapter, Duration lifecycleTimeout,
178+
Shutdown shutdown) {
179+
super(httpServer, handlerAdapter, lifecycleTimeout, shutdown);
180+
}
181+
182+
@Override
183+
DisposableServer startHttpServer() {
184+
return new NoPortDisposableServer(super.startHttpServer());
185+
}
186+
187+
}
188+
189+
static class NoPortDisposableServer implements DisposableServer {
190+
191+
private final DisposableServer delegate;
192+
193+
NoPortDisposableServer(DisposableServer delegate) {
194+
this.delegate = delegate;
195+
}
196+
197+
@Override
198+
public SocketAddress address() {
199+
return this.delegate.address();
200+
}
201+
202+
@Override
203+
public String host() {
204+
return this.delegate.host();
205+
}
206+
207+
@Override
208+
public String path() {
209+
return this.delegate.path();
210+
}
211+
212+
@Override
213+
public Channel channel() {
214+
return this.delegate.channel();
215+
}
216+
217+
@Override
218+
public void dispose() {
219+
this.delegate.dispose();
220+
}
221+
222+
@Override
223+
public void disposeNow() {
224+
this.delegate.disposeNow();
225+
}
226+
227+
@Override
228+
public void disposeNow(Duration timeout) {
229+
this.delegate.disposeNow(timeout);
230+
}
231+
232+
@Override
233+
public CoreSubscriber<Void> disposeSubscriber() {
234+
return this.delegate.disposeSubscriber();
235+
}
236+
237+
@Override
238+
public boolean isDisposed() {
239+
return this.delegate.isDisposed();
240+
}
241+
242+
@Override
243+
public Mono<Void> onDispose() {
244+
return this.delegate.onDispose();
245+
}
246+
247+
@Override
248+
public DisposableChannel onDispose(Disposable onDispose) {
249+
return this.delegate.onDispose(onDispose);
250+
}
251+
252+
}
253+
146254
}

0 commit comments

Comments
 (0)