diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/cloud-foundry.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/cloud-foundry.adoc index 0e90877e583..97e81386fd6 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/cloud-foundry.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/cloud-foundry.adoc @@ -48,3 +48,7 @@ The configuration differs, depending on the web server in use. For Tomcat, you can add the following configuration: include::code:MyCloudFoundryConfiguration[] + +If you're using a Webflux based application, you can use the following configuration: + +include::code:MyReactiveCloudFoundryConfiguration[] diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/actuator/cloudfoundry/customcontextpath/MyReactiveCloudFoundryConfiguration.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/actuator/cloudfoundry/customcontextpath/MyReactiveCloudFoundryConfiguration.java new file mode 100644 index 00000000000..c4712866c75 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/actuator/cloudfoundry/customcontextpath/MyReactiveCloudFoundryConfiguration.java @@ -0,0 +1,69 @@ +/* + * 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.docs.actuator.cloudfoundry.customcontextpath; + +import java.util.Map; + +import reactor.core.publisher.Mono; + +import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.server.reactive.ContextPathCompositeHandler; +import org.springframework.http.server.reactive.HttpHandler; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.adapter.WebHttpHandlerBuilder; + +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(WebFluxProperties.class) +public class MyReactiveCloudFoundryConfiguration { + + @Bean + public HttpHandler httpHandler(ApplicationContext applicationContext, WebFluxProperties properties) { + HttpHandler httpHandler = WebHttpHandlerBuilder.applicationContext(applicationContext).build(); + return new CloudFoundryHttpHandler(properties.getBasePath(), httpHandler); + } + + private static final class CloudFoundryHttpHandler implements HttpHandler { + + private final HttpHandler delegate; + + private final ContextPathCompositeHandler contextPathDelegate; + + private CloudFoundryHttpHandler(String basePath, HttpHandler delegate) { + this.delegate = delegate; + this.contextPathDelegate = new ContextPathCompositeHandler(Map.of(basePath, delegate)); + } + + @Override + public Mono handle(ServerHttpRequest request, ServerHttpResponse response) { + // Remove underlying context path first (e.g. Servlet container) + String path = request.getPath().pathWithinApplication().value(); + if (path.startsWith("/cloudfoundryapplication")) { + return this.delegate.handle(request, response); + } + else { + return this.contextPathDelegate.handle(request, response); + } + } + + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/actuator/cloudfoundry/customcontextpath/MyReactiveCloudFoundryConfiguration.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/actuator/cloudfoundry/customcontextpath/MyReactiveCloudFoundryConfiguration.kt new file mode 100644 index 00000000000..71e748ef4a7 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/actuator/cloudfoundry/customcontextpath/MyReactiveCloudFoundryConfiguration.kt @@ -0,0 +1,53 @@ +/* + * 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.docs.actuator.cloudfoundry.customcontextpath + +import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.context.ApplicationContext +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.http.server.reactive.ContextPathCompositeHandler +import org.springframework.http.server.reactive.HttpHandler +import org.springframework.http.server.reactive.ServerHttpRequest +import org.springframework.http.server.reactive.ServerHttpResponse +import org.springframework.web.server.adapter.WebHttpHandlerBuilder +import reactor.core.publisher.Mono + +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(WebFluxProperties::class) +class MyReactiveCloudFoundryConfiguration { + + @Bean + fun httpHandler(applicationContext: ApplicationContext, properties: WebFluxProperties): HttpHandler { + val httpHandler = WebHttpHandlerBuilder.applicationContext(applicationContext).build() + return CloudFoundryHttpHandler(properties.basePath, httpHandler) + } + + private class CloudFoundryHttpHandler(basePath: String, private val delegate: HttpHandler) : HttpHandler { + private val contextPathDelegate = ContextPathCompositeHandler(mapOf(basePath to delegate)) + + override fun handle(request: ServerHttpRequest, response: ServerHttpResponse): Mono { + // Remove underlying context path first (e.g. Servlet container) + val path = request.path.pathWithinApplication().value() + return if (path.startsWith("/cloudfoundryapplication")) { + delegate.handle(request, response) + } else { + contextPathDelegate.handle(request, response) + } + } + } +}