Remove ServerWebExchange dependency in ServerRequestObservationContext

Avoiding cycle between http.server and web.server packages.

See gh-30013
This commit is contained in:
Juergen Hoeller 2023-06-14 21:57:27 +02:00
parent 220995b830
commit f00a8cb3a3
7 changed files with 40 additions and 22 deletions

View File

@ -25,11 +25,12 @@ import io.micrometer.observation.transport.RequestReplyReceiverContext;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.web.server.ServerWebExchange;
/** /**
* Context that holds information for metadata collection regarding * Context that holds information for metadata collection regarding
* {@link ServerHttpObservationDocumentation#HTTP_REACTIVE_SERVER_REQUESTS reactive HTTP requests} observations. * {@link ServerHttpObservationDocumentation#HTTP_REACTIVE_SERVER_REQUESTS reactive HTTP requests}
* observations.
*
* <p>This context also extends {@link RequestReplyReceiverContext} for propagating * <p>This context also extends {@link RequestReplyReceiverContext} for propagating
* tracing information during HTTP request processing. * tracing information during HTTP request processing.
* *
@ -39,10 +40,11 @@ import org.springframework.web.server.ServerWebExchange;
public class ServerRequestObservationContext extends RequestReplyReceiverContext<ServerHttpRequest, ServerHttpResponse> { public class ServerRequestObservationContext extends RequestReplyReceiverContext<ServerHttpRequest, ServerHttpResponse> {
/** /**
* Name of the request attribute holding the {@link ServerRequestObservationContext context} for the current observation. * Name of the request attribute holding the {@link ServerRequestObservationContext context}
* for the current observation.
* @since 6.1 * @since 6.1
*/ */
public static final String CURRENT_OBSERVATION_CONTEXT_ATTRIBUTE = ServerRequestObservationContext.class.getName() + ".context"; public static final String CURRENT_OBSERVATION_CONTEXT_ATTRIBUTE = ServerRequestObservationContext.class.getName();
private final Map<String, Object> attributes; private final Map<String, Object> attributes;
@ -53,7 +55,15 @@ public class ServerRequestObservationContext extends RequestReplyReceiverContext
private boolean connectionAborted; private boolean connectionAborted;
public ServerRequestObservationContext(ServerHttpRequest request, ServerHttpResponse response, Map<String, Object> attributes) { /**
* Create a new {@code ServerRequestObservationContext} instance.
* @param request the current request
* @param response the current response
* @param attributes the current attributes
*/
public ServerRequestObservationContext(
ServerHttpRequest request, ServerHttpResponse response, Map<String, Object> attributes) {
super((req, key) -> req.getHeaders().getFirst(key)); super((req, key) -> req.getHeaders().getFirst(key));
setCarrier(request); setCarrier(request);
setResponse(response); setResponse(response);
@ -89,8 +99,8 @@ public class ServerRequestObservationContext extends RequestReplyReceiverContext
} }
/** /**
* Whether the current connection was aborted by the client, resulting * Whether the current connection was aborted by the client, resulting in a
* in a {@link reactor.core.publisher.SignalType#CANCEL cancel signal} on the reactive chain, * {@link reactor.core.publisher.SignalType#CANCEL cancel signal} on the reactive chain,
* or an {@code AbortedException} when reading the request. * or an {@code AbortedException} when reading the request.
* @return if the connection has been aborted * @return if the connection has been aborted
*/ */
@ -99,8 +109,8 @@ public class ServerRequestObservationContext extends RequestReplyReceiverContext
} }
/** /**
* Set whether the current connection was aborted by the client, resulting * Set whether the current connection was aborted by the client, resulting in a
* in a {@link reactor.core.publisher.SignalType#CANCEL cancel signal} on the reactive chain, * {@link reactor.core.publisher.SignalType#CANCEL cancel signal} on the reactive chain,
* or an {@code AbortedException} when reading the request. * or an {@code AbortedException} when reading the request.
* @param connectionAborted if the connection has been aborted * @param connectionAborted if the connection has been aborted
*/ */
@ -110,13 +120,15 @@ public class ServerRequestObservationContext extends RequestReplyReceiverContext
/** /**
* Get the current {@link ServerRequestObservationContext observation context} from the given exchange, if available. * Get the current {@link ServerRequestObservationContext observation context}
* @param exchange the current exchange * from the given attributes, if available.
* @param attributes the current exchange attributes
* @return the current observation context * @return the current observation context
* @since 6.1 * @since 6.1
*/ */
public static Optional<ServerRequestObservationContext> findCurrent(ServerWebExchange exchange) { public static Optional<ServerRequestObservationContext> findCurrent(Map<String, Object> attributes) {
return Optional.ofNullable(exchange.getAttribute(CURRENT_OBSERVATION_CONTEXT_ATTRIBUTE)); return Optional.ofNullable(
(ServerRequestObservationContext) attributes.get(CURRENT_OBSERVATION_CONTEXT_ATTRIBUTE));
} }
} }

View File

@ -296,9 +296,10 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
exchange.getLogPrefix() + formatRequest(exchange.getRequest()) + exchange.getLogPrefix() + formatRequest(exchange.getRequest()) +
(traceOn ? ", headers=" + formatHeaders(exchange.getRequest().getHeaders()) : "")); (traceOn ? ", headers=" + formatHeaders(exchange.getRequest().getHeaders()) : ""));
ServerRequestObservationContext observationContext = new ServerRequestObservationContext(exchange.getRequest(), ServerRequestObservationContext observationContext = new ServerRequestObservationContext(
exchange.getResponse(), exchange.getAttributes()); exchange.getRequest(), exchange.getResponse(), exchange.getAttributes());
exchange.getAttributes().put(ServerRequestObservationContext.CURRENT_OBSERVATION_CONTEXT_ATTRIBUTE, observationContext); exchange.getAttributes().put(
ServerRequestObservationContext.CURRENT_OBSERVATION_CONTEXT_ATTRIBUTE, observationContext);
return getDelegate().handle(exchange) return getDelegate().handle(exchange)
.transformDeferred(call -> transform(exchange, observationContext, call)) .transformDeferred(call -> transform(exchange, observationContext, call))

View File

@ -51,6 +51,7 @@ class HttpWebHandlerAdapterObservabilityTests {
private final MockServerHttpResponse response = new MockServerHttpResponse(); private final MockServerHttpResponse response = new MockServerHttpResponse();
@Test @Test
void handlerShouldSetObservationContextOnExchange() { void handlerShouldSetObservationContextOnExchange() {
HttpStatusSuccessStubWebHandler targetHandler = new HttpStatusSuccessStubWebHandler(HttpStatus.OK); HttpStatusSuccessStubWebHandler targetHandler = new HttpStatusSuccessStubWebHandler(HttpStatus.OK);
@ -97,6 +98,7 @@ class HttpWebHandlerAdapterObservabilityTests {
.hasObservationWithNameEqualTo("http.server.requests").that(); .hasObservationWithNameEqualTo("http.server.requests").that();
} }
private static class HttpStatusSuccessStubWebHandler implements WebHandler { private static class HttpStatusSuccessStubWebHandler implements WebHandler {
private final HttpStatus responseStatus; private final HttpStatus responseStatus;
@ -109,12 +111,13 @@ class HttpWebHandlerAdapterObservabilityTests {
@Override @Override
public Mono<Void> handle(ServerWebExchange exchange) { public Mono<Void> handle(ServerWebExchange exchange) {
this.observationContext = ServerRequestObservationContext.findCurrent(exchange); this.observationContext = ServerRequestObservationContext.findCurrent(exchange.getAttributes());
exchange.getResponse().setStatusCode(this.responseStatus); exchange.getResponse().setStatusCode(this.responseStatus);
return Mono.empty(); return Mono.empty();
} }
} }
private static class ReactorContextWebHandler implements WebHandler { private static class ReactorContextWebHandler implements WebHandler {
ContextView contextView; ContextView contextView;
@ -129,6 +132,7 @@ class HttpWebHandlerAdapterObservabilityTests {
} }
} }
private static class ThrowingExceptionWebHandler implements WebHandler { private static class ThrowingExceptionWebHandler implements WebHandler {
private final Throwable exception; private final Throwable exception;
@ -141,11 +145,12 @@ class HttpWebHandlerAdapterObservabilityTests {
@Override @Override
public Mono<Void> handle(ServerWebExchange exchange) { public Mono<Void> handle(ServerWebExchange exchange) {
this.observationContext = ServerRequestObservationContext.findCurrent(exchange); this.observationContext = ServerRequestObservationContext.findCurrent(exchange.getAttributes());
return Mono.error(this.exception); return Mono.error(this.exception);
} }
} }
private static class BadRequestExceptionHandler implements WebExceptionHandler { private static class BadRequestExceptionHandler implements WebExceptionHandler {
@Override @Override

View File

@ -174,7 +174,7 @@ public class RouterFunctionMapping extends AbstractHandlerMapping implements Ini
org.springframework.web.filter.reactive.ServerHttpObservationFilter org.springframework.web.filter.reactive.ServerHttpObservationFilter
.findObservationContext(serverRequest.exchange()) .findObservationContext(serverRequest.exchange())
.ifPresent(context -> context.setPathPattern(matchingPattern.toString())); .ifPresent(context -> context.setPathPattern(matchingPattern.toString()));
ServerRequestObservationContext.findCurrent(serverRequest.exchange()) ServerRequestObservationContext.findCurrent(serverRequest.exchange().getAttributes())
.ifPresent(context -> context.setPathPattern(matchingPattern.toString())); .ifPresent(context -> context.setPathPattern(matchingPattern.toString()));
} }
Map<String, String> uriVariables = Map<String, String> uriVariables =

View File

@ -170,7 +170,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
org.springframework.web.filter.reactive.ServerHttpObservationFilter org.springframework.web.filter.reactive.ServerHttpObservationFilter
.findObservationContext(exchange) .findObservationContext(exchange)
.ifPresent(context -> context.setPathPattern(pattern.toString())); .ifPresent(context -> context.setPathPattern(pattern.toString()));
ServerRequestObservationContext.findCurrent(exchange) ServerRequestObservationContext.findCurrent(exchange.getAttributes())
.ifPresent(context -> context.setPathPattern(pattern.toString())); .ifPresent(context -> context.setPathPattern(pattern.toString()));
exchange.getAttributes().put(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping); exchange.getAttributes().put(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, matchInfo.getUriVariables()); exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, matchInfo.getUriVariables());

View File

@ -145,7 +145,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
org.springframework.web.filter.reactive.ServerHttpObservationFilter org.springframework.web.filter.reactive.ServerHttpObservationFilter
.findObservationContext(exchange) .findObservationContext(exchange)
.ifPresent(context -> context.setPathPattern(bestPattern.toString())); .ifPresent(context -> context.setPathPattern(bestPattern.toString()));
ServerRequestObservationContext.findCurrent(exchange) ServerRequestObservationContext.findCurrent(exchange.getAttributes())
.ifPresent(context -> context.setPathPattern(bestPattern.toString())); .ifPresent(context -> context.setPathPattern(bestPattern.toString()));
exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriVariables); exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriVariables);
exchange.getAttributes().put(MATRIX_VARIABLES_ATTRIBUTE, matrixVariables); exchange.getAttributes().put(MATRIX_VARIABLES_ATTRIBUTE, matrixVariables);

View File

@ -137,7 +137,7 @@ class RouterFunctionMappingTests {
assertThat(matchingPattern.getPatternString()).isEqualTo("/match"); assertThat(matchingPattern.getPatternString()).isEqualTo("/match");
assertThat(org.springframework.web.filter.reactive.ServerHttpObservationFilter.findObservationContext(exchange)) assertThat(org.springframework.web.filter.reactive.ServerHttpObservationFilter.findObservationContext(exchange))
.hasValueSatisfying(context -> assertThat(context.getPathPattern()).isEqualTo(matchingPattern.getPatternString())); .hasValueSatisfying(context -> assertThat(context.getPathPattern()).isEqualTo(matchingPattern.getPatternString()));
assertThat(ServerRequestObservationContext.findCurrent(exchange)) assertThat(ServerRequestObservationContext.findCurrent(exchange.getAttributes()))
.hasValueSatisfying(context -> assertThat(context.getPathPattern()).isEqualTo(matchingPattern.getPatternString())); .hasValueSatisfying(context -> assertThat(context.getPathPattern()).isEqualTo(matchingPattern.getPatternString()));
ServerRequest serverRequest = exchange.getAttribute(RouterFunctions.REQUEST_ATTRIBUTE); ServerRequest serverRequest = exchange.getAttribute(RouterFunctions.REQUEST_ATTRIBUTE);