From e6d206b45a460c2ac2e1ccde8098815163cb6124 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 15 Mar 2019 16:12:58 -0400 Subject: [PATCH] Extra information in WebFlux stacktraces Use the checkpoint operator at various places in WebFlux to insert information that Reactor then uses to enrich exceptions, via suppressed exceptions, when error signals flow through the operator. Closes gh-22105 --- .../web/method/HandlerMethod.java | 18 ++++++++++- .../server/handler/DefaultWebFilterChain.java | 19 +++++++----- .../handler/ExceptionHandlingWebHandler.java | 30 +++++++++++++++++-- .../web/reactive/DispatcherHandler.java | 16 +++++----- .../client/DefaultClientResponse.java | 24 +++++++++++++-- .../client/DefaultClientResponseBuilder.java | 4 +-- .../function/client/DefaultWebClient.java | 28 +++++++++++++---- .../function/client/ExchangeFunctions.java | 5 ++-- .../client/WebClientResponseException.java | 9 ++++-- .../adapter/JettyWebSocketHandlerAdapter.java | 6 ++-- .../StandardWebSocketHandlerAdapter.java | 6 ++-- .../client/ReactorNettyWebSocketClient.java | 2 +- .../client/UndertowWebSocketClient.java | 6 ++-- .../ReactorNettyRequestUpgradeStrategy.java | 6 ++-- .../UndertowRequestUpgradeStrategy.java | 8 +++-- .../client/DefaultClientResponseTests.java | 2 +- 16 files changed, 144 insertions(+), 45 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java b/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java index d1613211b0..a9b990300e 100644 --- a/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java +++ b/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java @@ -21,6 +21,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.StringJoiner; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -89,6 +90,8 @@ public class HandlerMethod { @Nullable private volatile List interfaceParameterAnnotations; + private final String description; + /** * Create an instance from a bean instance and a method. @@ -103,6 +106,7 @@ public class HandlerMethod { this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); this.parameters = initMethodParameters(); evaluateResponseStatus(); + this.description = initDescription(this.beanType, this.method); } /** @@ -119,6 +123,7 @@ public class HandlerMethod { this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(this.method); this.parameters = initMethodParameters(); evaluateResponseStatus(); + this.description = initDescription(this.beanType, this.method); } /** @@ -141,6 +146,7 @@ public class HandlerMethod { this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); this.parameters = initMethodParameters(); evaluateResponseStatus(); + this.description = initDescription(this.beanType, this.method); } /** @@ -156,6 +162,7 @@ public class HandlerMethod { this.parameters = handlerMethod.parameters; this.responseStatus = handlerMethod.responseStatus; this.responseStatusReason = handlerMethod.responseStatusReason; + this.description = handlerMethod.description; this.resolvedFromHandlerMethod = handlerMethod.resolvedFromHandlerMethod; } @@ -174,6 +181,7 @@ public class HandlerMethod { this.responseStatus = handlerMethod.responseStatus; this.responseStatusReason = handlerMethod.responseStatusReason; this.resolvedFromHandlerMethod = handlerMethod; + this.description = handlerMethod.description; } private MethodParameter[] initMethodParameters() { @@ -198,6 +206,14 @@ public class HandlerMethod { } } + private static String initDescription(Class beanType, Method method) { + StringJoiner joiner = new StringJoiner(", ", "(", ")"); + for (Class paramType : method.getParameterTypes()) { + joiner.add(paramType.getSimpleName()); + } + return beanType.getName() + "#" + method.getName() + joiner.toString(); + } + /** * Return the bean for this handler method. @@ -389,7 +405,7 @@ public class HandlerMethod { @Override public String toString() { - return this.method.toGenericString(); + return this.description; } diff --git a/spring-web/src/main/java/org/springframework/web/server/handler/DefaultWebFilterChain.java b/spring-web/src/main/java/org/springframework/web/server/handler/DefaultWebFilterChain.java index 06729c3a58..5435b4b0f3 100644 --- a/spring-web/src/main/java/org/springframework/web/server/handler/DefaultWebFilterChain.java +++ b/spring-web/src/main/java/org/springframework/web/server/handler/DefaultWebFilterChain.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -53,7 +53,7 @@ public class DefaultWebFilterChain implements WebFilterChain { private final WebFilter currentFilter; @Nullable - private final DefaultWebFilterChain next; + private final DefaultWebFilterChain chain; /** @@ -68,7 +68,7 @@ public class DefaultWebFilterChain implements WebFilterChain { this.handler = handler; DefaultWebFilterChain chain = initChain(filters, handler); this.currentFilter = chain.currentFilter; - this.next = chain.next; + this.chain = chain.chain; } private static DefaultWebFilterChain initChain(List filters, WebHandler handler) { @@ -84,12 +84,12 @@ public class DefaultWebFilterChain implements WebFilterChain { * Private constructor to represent one link in the chain. */ private DefaultWebFilterChain(List allFilters, WebHandler handler, - @Nullable WebFilter currentFilter, @Nullable DefaultWebFilterChain next) { + @Nullable WebFilter currentFilter, @Nullable DefaultWebFilterChain chain) { this.allFilters = allFilters; this.currentFilter = currentFilter; this.handler = handler; - this.next = next; + this.chain = chain; } /** @@ -117,9 +117,14 @@ public class DefaultWebFilterChain implements WebFilterChain { @Override public Mono filter(ServerWebExchange exchange) { return Mono.defer(() -> - this.currentFilter != null && this.next != null ? - this.currentFilter.filter(exchange, this.next) : + this.currentFilter != null && this.chain != null ? + invokeFilter(this.currentFilter, this.chain, exchange) : this.handler.handle(exchange)); } + private Mono invokeFilter(WebFilter current, DefaultWebFilterChain chain, ServerWebExchange exchange) { + return current.filter(exchange, chain) + .checkpoint(current.getClass().getName() + " [DefaultWebFilterChain]"); + } + } diff --git a/spring-web/src/main/java/org/springframework/web/server/handler/ExceptionHandlingWebHandler.java b/spring-web/src/main/java/org/springframework/web/server/handler/ExceptionHandlingWebHandler.java index 12bd48f25e..e89a1e4993 100644 --- a/spring-web/src/main/java/org/springframework/web/server/handler/ExceptionHandlingWebHandler.java +++ b/spring-web/src/main/java/org/springframework/web/server/handler/ExceptionHandlingWebHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -22,6 +22,9 @@ import java.util.List; import reactor.core.publisher.Mono; +import org.springframework.http.HttpMethod; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebExceptionHandler; import org.springframework.web.server.WebHandler; @@ -41,7 +44,10 @@ public class ExceptionHandlingWebHandler extends WebHandlerDecorator { public ExceptionHandlingWebHandler(WebHandler delegate, List handlers) { super(delegate); - this.exceptionHandlers = Collections.unmodifiableList(new ArrayList<>(handlers)); + List handlersToUse = new ArrayList<>(); + handlersToUse.add(new CheckpointInsertingHandler()); + handlersToUse.addAll(handlers); + this.exceptionHandlers = Collections.unmodifiableList(handlersToUse); } @@ -71,4 +77,24 @@ public class ExceptionHandlingWebHandler extends WebHandlerDecorator { return completion; } + + /** + * WebExceptionHandler to insert a checkpoint with current URL information. + * Must be the first in order to ensure we catch the error signal before + * the exception is handled and e.g. turned into an error response. + * @since 5.2 + */ + private static class CheckpointInsertingHandler implements WebExceptionHandler { + + @Override + public Mono handle(ServerWebExchange exchange, Throwable ex) { + ServerHttpRequest request = exchange.getRequest(); + String rawQuery = request.getURI().getRawQuery(); + String query = StringUtils.hasText(rawQuery) ? "?" + rawQuery : ""; + HttpMethod httpMethod = request.getMethod(); + String description = "HTTP " + httpMethod + " \"" + request.getPath() + query + "\""; + return Mono.error(ex).checkpoint(description + " [ExceptionHandlingWebHandler]").cast(Void.class); + } + } + } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/DispatcherHandler.java b/spring-webflux/src/main/java/org/springframework/web/reactive/DispatcherHandler.java index b047a6f96b..4c3f68b955 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/DispatcherHandler.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/DispatcherHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -67,11 +67,6 @@ import org.springframework.web.server.adapter.WebHttpHandlerBuilder; */ public class DispatcherHandler implements WebHandler, ApplicationContextAware { - @SuppressWarnings("ThrowableInstanceNeverThrown") - private static final Exception HANDLER_NOT_FOUND_EXCEPTION = - new ResponseStatusException(HttpStatus.NOT_FOUND, "No matching handler"); - - @Nullable private List handlerMappings; @@ -172,8 +167,13 @@ public class DispatcherHandler implements WebHandler, ApplicationContextAware { private Mono handleResult(ServerWebExchange exchange, HandlerResult result) { return getResultHandler(result).handleResult(exchange, result) - .onErrorResume(ex -> result.applyExceptionHandler(ex).flatMap(exceptionResult -> - getResultHandler(exceptionResult).handleResult(exchange, exceptionResult))); + .checkpoint("Handler " + result.getHandler() + " [DispatcherHandler]") + .onErrorResume(ex -> + result.applyExceptionHandler(ex).flatMap(exResult -> { + String text = "Exception handler " + exResult.getHandler() + + ", error=\"" + ex.getMessage() + "\" [DispatcherHandler]"; + return getResultHandler(exResult).handleResult(exchange, exResult).checkpoint(text); + })); } private HandlerResultHandler getResultHandler(HandlerResult handlerResult) { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java index 51e91f7dcb..8d80b40e35 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -56,12 +56,17 @@ class DefaultClientResponse implements ClientResponse { private final String logPrefix; + private final String requestDescription; + + + public DefaultClientResponse(ClientHttpResponse response, ExchangeStrategies strategies, + String logPrefix, String requestDescription) { - public DefaultClientResponse(ClientHttpResponse response, ExchangeStrategies strategies, String logPrefix) { this.response = response; this.strategies = strategies; this.headers = new DefaultHeaders(); this.logPrefix = logPrefix; + this.requestDescription = requestDescription; } @@ -90,22 +95,35 @@ class DefaultClientResponse implements ClientResponse { return this.response.getCookies(); } + @SuppressWarnings("unchecked") @Override public T body(BodyExtractor extractor) { - return extractor.extract(this.response, new BodyExtractor.Context() { + T result = extractor.extract(this.response, new BodyExtractor.Context() { @Override public List> messageReaders() { return strategies.messageReaders(); } + @Override public Optional serverResponse() { return Optional.empty(); } + @Override public Map hints() { return Hints.from(Hints.LOG_PREFIX_HINT, logPrefix); } }); + String description = "Body from " + this.requestDescription + " [DefaultClientResponse]"; + if (result instanceof Mono) { + return (T) ((Mono) result).checkpoint(description); + } + else if (result instanceof Flux) { + return (T) ((Flux) result).checkpoint(description); + } + else { + return result; + } } @Override diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponseBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponseBuilder.java index 0e7ead9a7e..72429d4c84 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponseBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponseBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -136,7 +136,7 @@ final class DefaultClientResponseBuilder implements ClientResponse.Builder { // When building ClientResponse manually, the ClientRequest.logPrefix() has to be passed, // e.g. via ClientResponse.Builder, but this (builder) is not used currently. - return new DefaultClientResponse(httpResponse, this.strategies, ""); + return new DefaultClientResponse(httpResponse, this.strategies, "", ""); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java index 778ecf2766..bd2ddce43c 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java @@ -316,8 +316,9 @@ class DefaultWebClient implements WebClient { ClientRequest request = (this.inserter != null ? initRequestBuilder().body(this.inserter).build() : initRequestBuilder().build()); - return Mono.defer(() -> exchangeFunction.exchange(request)) - .switchIfEmpty(NO_HTTP_CLIENT_RESPONSE_ERROR); + return Mono.defer(() -> exchangeFunction.exchange(request) + .checkpoint("Request to " + this.httpMethod.name() + " " + this.uri + " [DefaultWebClient]") + .switchIfEmpty(NO_HTTP_CLIENT_RESPONSE_ERROR)); } private ClientRequest.Builder initRequestBuilder() { @@ -445,8 +446,8 @@ class DefaultWebClient implements WebClient { @Override public Flux bodyToFlux(ParameterizedTypeReference elementType) { - return this.responseMono.flatMapMany(response -> handleBody(response, - response.bodyToFlux(elementType), mono -> mono.flatMapMany(Flux::error))); + return this.responseMono.flatMapMany(response -> + handleBody(response, response.bodyToFlux(elementType), mono -> mono.flatMapMany(Flux::error))); } private > T handleBody(ClientResponse response, @@ -459,7 +460,8 @@ class DefaultWebClient implements WebClient { Mono exMono = handler.apply(response, request); exMono = exMono.flatMap(ex -> drainBody(response, ex)); exMono = exMono.onErrorResume(ex -> drainBody(response, ex)); - return errorFunction.apply(exMono); + T result = errorFunction.apply(exMono); + return insertCheckpoint(result, response.statusCode(), request); } } return bodyPublisher; @@ -477,6 +479,22 @@ class DefaultWebClient implements WebClient { .onErrorResume(ex2 -> Mono.empty()).thenReturn(ex); } + @SuppressWarnings("unchecked") + private > T insertCheckpoint(T result, HttpStatus status, HttpRequest request) { + String httpMethod = request.getMethodValue(); + URI uri = request.getURI(); + String description = status + " from " + httpMethod + " " + uri + " [DefaultWebClient]"; + if (result instanceof Mono) { + return (T) ((Mono) result).checkpoint(description); + } + else if (result instanceof Flux) { + return (T) ((Flux) result).checkpoint(description); + } + else { + return result; + } + } + private static Mono createResponseException( ClientResponse response, HttpRequest request) { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunctions.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunctions.java index a141869e6f..eeb0b816f8 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunctions.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunctions.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -105,7 +105,8 @@ public abstract class ExchangeFunctions { .doOnCancel(() -> logger.debug(logPrefix + "Cancel signal (to close connection)")) .map(httpResponse -> { logResponse(httpResponse, logPrefix); - return new DefaultClientResponse(httpResponse, this.strategies, logPrefix); + return new DefaultClientResponse( + httpResponse, this.strategies, logPrefix, httpMethod.name() + " " + url); }); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientResponseException.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientResponseException.java index c32dc819d5..67353f94bc 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientResponseException.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientResponseException.java @@ -63,11 +63,16 @@ public class WebClientResponseException extends WebClientException { * Constructor with response data only, and a default message. * @since 5.1.4 */ - public WebClientResponseException(int statusCode, String statusText, + public WebClientResponseException(int status, String reasonPhrase, @Nullable HttpHeaders headers, @Nullable byte[] body, @Nullable Charset charset, @Nullable HttpRequest request) { - this(statusCode + " " + statusText, statusCode, statusText, headers, body, charset, request); + this(initMessage(status, reasonPhrase, request), status, reasonPhrase, headers, body, charset, request); + } + + private static String initMessage(int status, String reasonPhrase, @Nullable HttpRequest request) { + return status + " " + reasonPhrase + + (request != null ? " from " + request.getMethodValue() + " " + request.getURI() : ""); } /** diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/adapter/JettyWebSocketHandlerAdapter.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/adapter/JettyWebSocketHandlerAdapter.java index 9317dc8cf6..3778a655c1 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/adapter/JettyWebSocketHandlerAdapter.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/adapter/JettyWebSocketHandlerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -74,7 +74,9 @@ public class JettyWebSocketHandlerAdapter { @OnWebSocketConnect public void onWebSocketConnect(Session session) { this.delegateSession = this.sessionFactory.apply(session); - this.delegateHandler.handle(this.delegateSession).subscribe(this.delegateSession); + this.delegateHandler.handle(this.delegateSession) + .checkpoint(session.getUpgradeRequest().getRequestURI() + " [JettyWebSocketHandlerAdapter]") + .subscribe(this.delegateSession); } @OnWebSocketMessage diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/adapter/StandardWebSocketHandlerAdapter.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/adapter/StandardWebSocketHandlerAdapter.java index e0defe4571..c0f1b540fa 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/adapter/StandardWebSocketHandlerAdapter.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/adapter/StandardWebSocketHandlerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -80,7 +80,9 @@ public class StandardWebSocketHandlerAdapter extends Endpoint { this.delegateSession.handleMessage(webSocketMessage.getType(), webSocketMessage); }); - this.delegateHandler.handle(this.delegateSession).subscribe(this.delegateSession); + this.delegateHandler.handle(this.delegateSession) + .checkpoint(session.getRequestURI() + " [StandardWebSocketHandlerAdapter]") + .subscribe(this.delegateSession); } private WebSocketMessage toMessage(T message) { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/client/ReactorNettyWebSocketClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/client/ReactorNettyWebSocketClient.java index 9e4aacf4f6..429c27ec07 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/client/ReactorNettyWebSocketClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/client/ReactorNettyWebSocketClient.java @@ -117,7 +117,7 @@ public class ReactorNettyWebSocketClient implements WebSocketClient { if (logger.isDebugEnabled()) { logger.debug("Started session '" + session.getId() + "' for " + url); } - return handler.handle(session); + return handler.handle(session).checkpoint(url + " [ReactorNettyWebSocketClient]"); }) .doOnRequest(n -> { if (logger.isDebugEnabled()) { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/client/UndertowWebSocketClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/client/UndertowWebSocketClient.java index d7601a9e2b..dccd7abc2b 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/client/UndertowWebSocketClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/client/UndertowWebSocketClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -204,7 +204,9 @@ public class UndertowWebSocketClient implements WebSocketClient { channel.getReceiveSetter().set(adapter); channel.resumeReceives(); - handler.handle(session).subscribe(session); + handler.handle(session) + .checkpoint(url + " [UndertowWebSocketClient]") + .subscribe(session); } private HandshakeInfo createHandshakeInfo(URI url, DefaultNegotiation negotiation) { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/ReactorNettyRequestUpgradeStrategy.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/ReactorNettyRequestUpgradeStrategy.java index 809f874391..9445f0b1b3 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/ReactorNettyRequestUpgradeStrategy.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/ReactorNettyRequestUpgradeStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -16,6 +16,7 @@ package org.springframework.web.reactive.socket.server.upgrade; +import java.net.URI; import java.util.function.Supplier; import reactor.core.publisher.Mono; @@ -81,7 +82,8 @@ public class ReactorNettyRequestUpgradeStrategy implements RequestUpgradeStrateg ReactorNettyWebSocketSession session = new ReactorNettyWebSocketSession( in, out, handshakeInfo, bufferFactory, this.maxFramePayloadLength); - return handler.handle(session); + URI uri = exchange.getRequest().getURI(); + return handler.handle(session).checkpoint(uri + " [ReactorNettyRequestUpgradeStrategy]"); }); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/UndertowRequestUpgradeStrategy.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/UndertowRequestUpgradeStrategy.java index 1928089e33..47079b9d92 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/UndertowRequestUpgradeStrategy.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/UndertowRequestUpgradeStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -93,14 +93,16 @@ public class UndertowRequestUpgradeStrategy implements RequestUpgradeStrategy { } @Override - public void onConnect(WebSocketHttpExchange httpExchange, WebSocketChannel channel) { + public void onConnect(WebSocketHttpExchange exchange, WebSocketChannel channel) { UndertowWebSocketSession session = createSession(channel); UndertowWebSocketHandlerAdapter adapter = new UndertowWebSocketHandlerAdapter(session); channel.getReceiveSetter().set(adapter); channel.resumeReceives(); - this.handler.handle(session).subscribe(session); + this.handler.handle(session) + .checkpoint(exchange.getRequestURI() + " [UndertowRequestUpgradeStrategy]") + .subscribe(session); } private UndertowWebSocketSession createSession(WebSocketChannel channel) { diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultClientResponseTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultClientResponseTests.java index f4fb4e421d..d420969a69 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultClientResponseTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultClientResponseTests.java @@ -70,7 +70,7 @@ public class DefaultClientResponseTests { public void createMocks() { mockResponse = mock(ClientHttpResponse.class); mockExchangeStrategies = mock(ExchangeStrategies.class); - defaultClientResponse = new DefaultClientResponse(mockResponse, mockExchangeStrategies, ""); + defaultClientResponse = new DefaultClientResponse(mockResponse, mockExchangeStrategies, "", ""); }