Polish
This commit is contained in:
		
							parent
							
								
									2515134f8e
								
							
						
					
					
						commit
						262e5f783d
					
				| 
						 | 
				
			
			@ -300,10 +300,8 @@ class DefaultWebTestClient implements WebTestClient {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		public EntityExchangeResult<Void> consumeEmpty() {
 | 
			
		||||
			assertWithDiagnostics(() -> {
 | 
			
		||||
			DataBuffer buffer = this.response.body(toDataBuffers()).blockFirst(getTimeout());
 | 
			
		||||
				assertTrue("Expected empty body", buffer == null);
 | 
			
		||||
			});
 | 
			
		||||
			assertWithDiagnostics(() -> assertTrue("Expected empty body", buffer == null));
 | 
			
		||||
			return new EntityExchangeResult<>(this, null);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -344,10 +342,8 @@ class DefaultWebTestClient implements WebTestClient {
 | 
			
		|||
 | 
			
		||||
		@Override
 | 
			
		||||
		public ResponseSpec consumeWith(Consumer<ExchangeResult> consumer) {
 | 
			
		||||
			return this.result.assertWithDiagnosticsAndReturn(() -> {
 | 
			
		||||
				consumer.accept(this.result);
 | 
			
		||||
			this.result.assertWithDiagnostics(() -> consumer.accept(this.result));
 | 
			
		||||
			return this;
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
| 
						 | 
				
			
			@ -402,10 +398,9 @@ class DefaultWebTestClient implements WebTestClient {
 | 
			
		|||
 | 
			
		||||
		@Override
 | 
			
		||||
		public <T> EntityExchangeResult<T> isEqualTo(T expected) {
 | 
			
		||||
			return this.result.assertWithDiagnosticsAndReturn(() -> {
 | 
			
		||||
				assertEquals("Response body", expected, this.result.getResponseBody());
 | 
			
		||||
			Object actual = this.result.getResponseBody();
 | 
			
		||||
			this.result.assertWithDiagnostics(() -> assertEquals("Response body", expected, actual));
 | 
			
		||||
			return returnResult();
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@SuppressWarnings("unchecked")
 | 
			
		||||
| 
						 | 
				
			
			@ -427,10 +422,9 @@ class DefaultWebTestClient implements WebTestClient {
 | 
			
		|||
 | 
			
		||||
		@Override
 | 
			
		||||
		public <T> EntityExchangeResult<List<T>> isEqualTo(List<T> expected) {
 | 
			
		||||
			return this.result.assertWithDiagnosticsAndReturn(() -> {
 | 
			
		||||
				assertEquals("Response body", expected, this.result.getResponseBody());
 | 
			
		||||
			List<?> actual = this.result.getResponseBody();
 | 
			
		||||
			this.result.assertWithDiagnostics(() -> assertEquals("Response body", expected, actual));
 | 
			
		||||
			return returnResult();
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
| 
						 | 
				
			
			@ -440,21 +434,19 @@ class DefaultWebTestClient implements WebTestClient {
 | 
			
		|||
 | 
			
		||||
		@Override
 | 
			
		||||
		public ListBodySpec contains(Object... elements) {
 | 
			
		||||
			this.result.assertWithDiagnostics(() -> {
 | 
			
		||||
				List<Object> elementList = Arrays.asList(elements);
 | 
			
		||||
				String message = "Response body does not contain " + elementList;
 | 
			
		||||
				assertTrue(message, this.result.getResponseBody().containsAll(elementList));
 | 
			
		||||
			});
 | 
			
		||||
			List<?> expected = Arrays.asList(elements);
 | 
			
		||||
			List<?> actual = this.result.getResponseBody();
 | 
			
		||||
			String message = "Response body does not contain " + expected;
 | 
			
		||||
			this.result.assertWithDiagnostics(() -> assertTrue(message, actual.containsAll(expected)));
 | 
			
		||||
			return this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public ListBodySpec doesNotContain(Object... elements) {
 | 
			
		||||
			this.result.assertWithDiagnostics(() -> {
 | 
			
		||||
				List<Object> elementList = Arrays.asList(elements);
 | 
			
		||||
				String message = "Response body should have contained " + elementList;
 | 
			
		||||
				assertTrue(message, !this.result.getResponseBody().containsAll(Arrays.asList(elements)));
 | 
			
		||||
			});
 | 
			
		||||
			List<?> expected = Arrays.asList(elements);
 | 
			
		||||
			List<?> actual = this.result.getResponseBody();
 | 
			
		||||
			String message = "Response body should have contained " + expected;
 | 
			
		||||
			this.result.assertWithDiagnostics(() -> assertTrue(message, !actual.containsAll(expected)));
 | 
			
		||||
			return this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -507,43 +499,38 @@ class DefaultWebTestClient implements WebTestClient {
 | 
			
		|||
 | 
			
		||||
		@Override
 | 
			
		||||
		public <K, V> EntityExchangeResult<Map<K, V>> isEqualTo(Map<K, V> expected) {
 | 
			
		||||
			return this.result.assertWithDiagnosticsAndReturn(() -> {
 | 
			
		||||
				assertEquals("Response body map", expected, getBody());
 | 
			
		||||
			String message = "Response body map";
 | 
			
		||||
			this.result.assertWithDiagnostics(() -> assertEquals(message, expected, getBody()));
 | 
			
		||||
			return returnResult();
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public MapBodySpec hasSize(int size) {
 | 
			
		||||
			return this.result.assertWithDiagnosticsAndReturn(() -> {
 | 
			
		||||
				assertEquals("Response body map size", size, getBody().size());
 | 
			
		||||
			String message = "Response body map size";
 | 
			
		||||
			this.result.assertWithDiagnostics(() -> assertEquals(message, size, getBody().size()));
 | 
			
		||||
			return this;
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public MapBodySpec contains(Object key, Object value) {
 | 
			
		||||
			return this.result.assertWithDiagnosticsAndReturn(() -> {
 | 
			
		||||
				assertEquals("Response body map value for key " + key, value, getBody().get(key));
 | 
			
		||||
			String message = "Response body map value for key " + key;
 | 
			
		||||
			this.result.assertWithDiagnostics(() -> assertEquals(message, value, getBody().get(key)));
 | 
			
		||||
			return this;
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public MapBodySpec containsKeys(Object... keys) {
 | 
			
		||||
			return this.result.assertWithDiagnosticsAndReturn(() -> {
 | 
			
		||||
			List<?> missing = Arrays.stream(keys).filter(k -> !getBody().containsKey(k)).collect(toList());
 | 
			
		||||
				assertTrue("Response body map does not contain keys " + missing, missing.isEmpty());
 | 
			
		||||
			String message = "Response body map does not contain keys " + missing;
 | 
			
		||||
			this.result.assertWithDiagnostics(() -> assertTrue(message, missing.isEmpty()));
 | 
			
		||||
			return this;
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public MapBodySpec containsValues(Object... values) {
 | 
			
		||||
			this.result.assertWithDiagnostics(() -> {
 | 
			
		||||
			List<?> missing = Arrays.stream(values).filter(v -> !getBody().containsValue(v)).collect(toList());
 | 
			
		||||
				assertTrue("Response body map does not contain values " + missing, missing.isEmpty());
 | 
			
		||||
			});
 | 
			
		||||
			String message = "Response body map does not contain values " + missing;
 | 
			
		||||
			this.result.assertWithDiagnostics(() -> assertTrue(message, missing.isEmpty()));
 | 
			
		||||
			return this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,7 +20,6 @@ import java.nio.charset.Charset;
 | 
			
		|||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.function.Supplier;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
import reactor.core.publisher.MonoProcessor;
 | 
			
		||||
| 
						 | 
				
			
			@ -33,16 +32,17 @@ import org.springframework.http.ResponseCookie;
 | 
			
		|||
import org.springframework.util.MultiValueMap;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Simple container for request and response details from an exchange performed
 | 
			
		||||
 * Provides access to request and response details from an exchange performed
 | 
			
		||||
 * through the {@link WebTestClient}.
 | 
			
		||||
 *
 | 
			
		||||
 * <p>When an {@code ExchangeResult} is first created it has only the status and
 | 
			
		||||
 * headers of the response available. When the response body is extracted, the
 | 
			
		||||
 * {@code ExchangeResult} is re-created as either {@link EntityExchangeResult}
 | 
			
		||||
 * or {@link FluxExchangeResult} that further expose extracted entities.
 | 
			
		||||
 * <p>When an {@code ExchangeResult} is first created it has the status and the
 | 
			
		||||
 * headers of the response ready. Later when the response body is extracted,
 | 
			
		||||
 * the {@code ExchangeResult} is re-created as {@link EntityExchangeResult} or
 | 
			
		||||
 * {@link FluxExchangeResult} also exposing the extracted entities.
 | 
			
		||||
 *
 | 
			
		||||
 * <p>Raw request and response content may also be accessed once complete via
 | 
			
		||||
 * {@link #getRequestContent()} or {@link #getResponseContent()}.
 | 
			
		||||
 * <p>Serialized request and response content may also be accessed through the
 | 
			
		||||
 * methods {@link #getRequestContent()} and {@link #getResponseContent()} after
 | 
			
		||||
 * that content has been fully read or written.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Rossen Stoyanchev
 | 
			
		||||
 * @since 5.0
 | 
			
		||||
| 
						 | 
				
			
			@ -137,8 +137,9 @@ public class ExchangeResult {
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Execute the given Runnable in the context of "this" instance and decorate
 | 
			
		||||
	 * any {@link AssertionError}s raised with request and response details.
 | 
			
		||||
	 * Execute the given Runnable, catch any {@link AssertionError}, decorate
 | 
			
		||||
	 * with {@code AssertionError} containing diagnostic information about the
 | 
			
		||||
	 * request and response, and then re-throw.
 | 
			
		||||
	 */
 | 
			
		||||
	public void assertWithDiagnostics(Runnable assertion) {
 | 
			
		||||
		try {
 | 
			
		||||
| 
						 | 
				
			
			@ -149,32 +150,19 @@ public class ExchangeResult {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Variant of {@link #assertWithDiagnostics(Runnable)} that passes through
 | 
			
		||||
	 * a return value from the assertion code.
 | 
			
		||||
	 */
 | 
			
		||||
	public <T> T assertWithDiagnosticsAndReturn(Supplier<T> assertion) {
 | 
			
		||||
		try {
 | 
			
		||||
			return assertion.get();
 | 
			
		||||
		}
 | 
			
		||||
		catch (AssertionError ex) {
 | 
			
		||||
			throw new AssertionError("Assertion failed on the following exchange:" + this, ex);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public String toString() {
 | 
			
		||||
		return "\n" +
 | 
			
		||||
				"> " + getMethod() + " " + getUrl() + "\n" +
 | 
			
		||||
				"> " + formatHeaders("> ", getRequestHeaders()) + "\n" +
 | 
			
		||||
				"> " + formatHeaders(getRequestHeaders(), "\n> ") + "\n" +
 | 
			
		||||
				"\n" +
 | 
			
		||||
				formatContent(getRequestHeaders().getContentType(), getRequestContent()) + "\n" +
 | 
			
		||||
				formatBody(getRequestHeaders().getContentType(), getRequestContent()) + "\n" +
 | 
			
		||||
				"\n" +
 | 
			
		||||
				"< " + getStatus() + " " + getStatusReason() + "\n" +
 | 
			
		||||
				"< " + formatHeaders("< ", getResponseHeaders()) + "\n" +
 | 
			
		||||
				"< " + formatHeaders(getResponseHeaders(), "\n< ") + "\n" +
 | 
			
		||||
				"\n" +
 | 
			
		||||
				formatContent(getResponseHeaders().getContentType(), getResponseContent()) + "\n\n";
 | 
			
		||||
				formatBody(getResponseHeaders().getContentType(), getResponseContent()) + "\n\n";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private String getStatusReason() {
 | 
			
		||||
| 
						 | 
				
			
			@ -185,13 +173,13 @@ public class ExchangeResult {
 | 
			
		|||
		return reason;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private String formatHeaders(String linePrefix, HttpHeaders headers) {
 | 
			
		||||
	private String formatHeaders(HttpHeaders headers, String delimiter) {
 | 
			
		||||
		return headers.entrySet().stream()
 | 
			
		||||
				.map(entry -> entry.getKey() + ": " + entry.getValue())
 | 
			
		||||
				.collect(Collectors.joining("\n" + linePrefix));
 | 
			
		||||
				.collect(Collectors.joining(delimiter));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private String formatContent(MediaType contentType, MonoProcessor<byte[]> body) {
 | 
			
		||||
	private String formatBody(MediaType contentType, MonoProcessor<byte[]> body) {
 | 
			
		||||
		if (body.isSuccess()) {
 | 
			
		||||
			byte[] bytes = body.blockMillis(0);
 | 
			
		||||
			if (bytes.length == 0) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -60,13 +60,12 @@ public class HeaderAssertions {
 | 
			
		|||
	 * @param pattern String pattern to pass to {@link Pattern#compile(String)}
 | 
			
		||||
	 */
 | 
			
		||||
	public WebTestClient.ResponseSpec valueMatches(String name, String pattern) {
 | 
			
		||||
		return this.exchangeResult.assertWithDiagnosticsAndReturn(() -> {
 | 
			
		||||
		String value = getHeaders().getFirst(name);
 | 
			
		||||
		assertTrue(getMessage(name) + " not found", value != null);
 | 
			
		||||
		boolean match = Pattern.compile(pattern).matcher(value).matches();
 | 
			
		||||
			assertTrue(getMessage(name) + "=\'" + value + "\' does not match \'" + pattern + "\'", match);
 | 
			
		||||
		String message = getMessage(name) + "=\'" + value + "\' does not match \'" + pattern + "\'";
 | 
			
		||||
		this.exchangeResult.assertWithDiagnostics(() -> assertTrue(message, match));
 | 
			
		||||
		return this.responseSpec;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -123,10 +122,8 @@ public class HeaderAssertions {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	private WebTestClient.ResponseSpec assertHeader(String name, Object expected, Object actual) {
 | 
			
		||||
		return this.exchangeResult.assertWithDiagnosticsAndReturn(() -> {
 | 
			
		||||
			assertEquals(getMessage(name), expected, actual);
 | 
			
		||||
		this.exchangeResult.assertWithDiagnostics(() -> assertEquals(getMessage(name), expected, actual));
 | 
			
		||||
		return this.responseSpec;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,10 +51,9 @@ public class StatusAssertions {
 | 
			
		|||
	 * Assert the response status as an integer.
 | 
			
		||||
	 */
 | 
			
		||||
	public WebTestClient.ResponseSpec isEqualTo(int status) {
 | 
			
		||||
		return this.exchangeResult.assertWithDiagnosticsAndReturn(() -> {
 | 
			
		||||
			assertEquals("Status", status, this.exchangeResult.getStatus().value());
 | 
			
		||||
		int actual = this.exchangeResult.getStatus().value();
 | 
			
		||||
		this.exchangeResult.assertWithDiagnostics(() -> assertEquals("Status", status, actual));
 | 
			
		||||
		return this.responseSpec;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -146,11 +145,10 @@ public class StatusAssertions {
 | 
			
		|||
	 * Assert the response error message.
 | 
			
		||||
	 */
 | 
			
		||||
	public WebTestClient.ResponseSpec reasonEquals(String reason) {
 | 
			
		||||
		return this.exchangeResult.assertWithDiagnosticsAndReturn(() -> {
 | 
			
		||||
			HttpStatus status = this.exchangeResult.getStatus();
 | 
			
		||||
			assertEquals("Response status reason", reason, status.getReasonPhrase());
 | 
			
		||||
		String actual = this.exchangeResult.getStatus().getReasonPhrase();
 | 
			
		||||
		String message = "Response status reason";
 | 
			
		||||
		this.exchangeResult.assertWithDiagnostics(() -> assertEquals(message, reason, actual));
 | 
			
		||||
		return this.responseSpec;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -192,19 +190,16 @@ public class StatusAssertions {
 | 
			
		|||
	// Private methods
 | 
			
		||||
 | 
			
		||||
	private WebTestClient.ResponseSpec assertStatusAndReturn(HttpStatus expected) {
 | 
			
		||||
		return this.exchangeResult.assertWithDiagnosticsAndReturn(() -> {
 | 
			
		||||
			assertEquals("Status", expected, this.exchangeResult.getStatus());
 | 
			
		||||
		HttpStatus actual = this.exchangeResult.getStatus();
 | 
			
		||||
		this.exchangeResult.assertWithDiagnostics(() -> assertEquals("Status", expected, actual));
 | 
			
		||||
		return this.responseSpec;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private WebTestClient.ResponseSpec assertSeriesAndReturn(HttpStatus.Series expected) {
 | 
			
		||||
		return this.exchangeResult.assertWithDiagnosticsAndReturn(() -> {
 | 
			
		||||
		HttpStatus status = this.exchangeResult.getStatus();
 | 
			
		||||
		String message = "Range for response status value " + status;
 | 
			
		||||
			assertEquals(message, expected, status.series());
 | 
			
		||||
		this.exchangeResult.assertWithDiagnostics(() -> assertEquals(message, expected, status.series()));
 | 
			
		||||
		return this.responseSpec;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -512,9 +512,10 @@ public interface WebTestClient {
 | 
			
		|||
		ListBodySpec list(int elementCount);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Return request and response details from the exchange including the
 | 
			
		||||
		 * response body as a {@code Flux<T>} and available for example for use
 | 
			
		||||
		 * with a {@code StepVerifier} from Project Reactor.
 | 
			
		||||
		 * Return request and response details for the exchange incluidng the
 | 
			
		||||
		 * response body decoded as {@code Flux<T>} where {@code <T>} is the
 | 
			
		||||
		 * expected element type. The returned {@code Flux} may for example be
 | 
			
		||||
		 * verified with the Reactor {@code StepVerifier}.
 | 
			
		||||
		 */
 | 
			
		||||
		<T> FluxExchangeResult<T> returnResult();
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -530,7 +531,7 @@ public interface WebTestClient {
 | 
			
		|||
		<T> EntityExchangeResult<T> isEqualTo(T expected);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Return request and response details from the exchange including the
 | 
			
		||||
		 * Return request and response details for the exchange including the
 | 
			
		||||
		 * extracted response body.
 | 
			
		||||
		 */
 | 
			
		||||
		<T> EntityExchangeResult<T> returnResult();
 | 
			
		||||
| 
						 | 
				
			
			@ -565,7 +566,7 @@ public interface WebTestClient {
 | 
			
		|||
		ListBodySpec doesNotContain(Object... elements);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Return request and response details from the exchange including the
 | 
			
		||||
		 * Return request and response details for the exchange including the
 | 
			
		||||
		 * extracted response body.
 | 
			
		||||
		 */
 | 
			
		||||
		<T> EntityExchangeResult<List<T>> returnResult();
 | 
			
		||||
| 
						 | 
				
			
			@ -630,7 +631,7 @@ public interface WebTestClient {
 | 
			
		|||
		MapBodySpec containsValues(Object... values);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Return request and response details from the exchange including the
 | 
			
		||||
		 * Return request and response details for the exchange including the
 | 
			
		||||
		 * extracted response body.
 | 
			
		||||
		 */
 | 
			
		||||
		<K, V> EntityExchangeResult<Map<K, V>> returnResult();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,7 +27,8 @@ import org.springframework.http.client.reactive.ClientHttpRequest;
 | 
			
		|||
import org.springframework.http.client.reactive.ClientHttpRequestDecorator;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Client HTTP request decorator that saves the content written to the server.
 | 
			
		||||
 * Client HTTP request decorator that intercepts and saves content written to
 | 
			
		||||
 * the server.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Rossen Stoyanchev
 | 
			
		||||
 * @since 5.0
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +50,7 @@ class WiretapClientHttpRequest extends ClientHttpRequestDecorator {
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Return a "promise" for the request body content.
 | 
			
		||||
	 * Return a "promise" with the request body content written to the server.
 | 
			
		||||
	 */
 | 
			
		||||
	public MonoProcessor<byte[]> getBodyContent() {
 | 
			
		||||
		return this.body;
 | 
			
		||||
| 
						 | 
				
			
			@ -60,39 +61,39 @@ class WiretapClientHttpRequest extends ClientHttpRequestDecorator {
 | 
			
		|||
	public Mono<Void> writeWith(Publisher<? extends DataBuffer> publisher) {
 | 
			
		||||
		return super.writeWith(
 | 
			
		||||
				Flux.from(publisher)
 | 
			
		||||
						.doOnNext(this::handleBuffer)
 | 
			
		||||
						.doOnError(this::handleErrorSignal)
 | 
			
		||||
						.doOnCancel(this::handleCompleteSignal)
 | 
			
		||||
						.doOnComplete(this::handleCompleteSignal));
 | 
			
		||||
						.doOnNext(this::handleOnNext)
 | 
			
		||||
						.doOnError(this::handleError)
 | 
			
		||||
						.doOnCancel(this::handleOnComplete)
 | 
			
		||||
						.doOnComplete(this::handleOnComplete));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> publisher) {
 | 
			
		||||
		return super.writeAndFlushWith(
 | 
			
		||||
				Flux.from(publisher)
 | 
			
		||||
						.map(p -> Flux.from(p).doOnNext(this::handleBuffer).doOnError(this::handleErrorSignal))
 | 
			
		||||
						.doOnError(this::handleErrorSignal)
 | 
			
		||||
						.doOnCancel(this::handleCompleteSignal)
 | 
			
		||||
						.doOnComplete(this::handleCompleteSignal));
 | 
			
		||||
						.map(p -> Flux.from(p).doOnNext(this::handleOnNext).doOnError(this::handleError))
 | 
			
		||||
						.doOnError(this::handleError)
 | 
			
		||||
						.doOnCancel(this::handleOnComplete)
 | 
			
		||||
						.doOnComplete(this::handleOnComplete));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Mono<Void> setComplete() {
 | 
			
		||||
		handleCompleteSignal();
 | 
			
		||||
		handleOnComplete();
 | 
			
		||||
		return super.setComplete();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void handleBuffer(DataBuffer buffer) {
 | 
			
		||||
	private void handleOnNext(DataBuffer buffer) {
 | 
			
		||||
		this.buffer.write(buffer);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void handleErrorSignal(Throwable ex) {
 | 
			
		||||
	private void handleError(Throwable ex) {
 | 
			
		||||
		if (!this.body.isTerminated()) {
 | 
			
		||||
			this.body.onError(ex);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void handleCompleteSignal() {
 | 
			
		||||
	private void handleOnComplete() {
 | 
			
		||||
		if (!this.body.isTerminated()) {
 | 
			
		||||
			byte[] bytes = new byte[this.buffer.readableByteCount()];
 | 
			
		||||
			this.buffer.read(bytes);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,8 @@ import org.springframework.http.client.reactive.ClientHttpResponse;
 | 
			
		|||
import org.springframework.http.client.reactive.ClientHttpResponseDecorator;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Client HTTP response decorator that saves the content read from the server.
 | 
			
		||||
 * Client HTTP response decorator that interceptrs and saves the content read
 | 
			
		||||
 * from the server.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Rossen Stoyanchev
 | 
			
		||||
 * @since 5.0
 | 
			
		||||
| 
						 | 
				
			
			@ -47,7 +48,7 @@ class WiretapClientHttpResponse extends ClientHttpResponseDecorator {
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Return a "promise" for the response body content.
 | 
			
		||||
	 * Return a "promise" with the response body content read from the server.
 | 
			
		||||
	 */
 | 
			
		||||
	public MonoProcessor<byte[]> getBodyContent() {
 | 
			
		||||
		return this.body;
 | 
			
		||||
| 
						 | 
				
			
			@ -58,11 +59,11 @@ class WiretapClientHttpResponse extends ClientHttpResponseDecorator {
 | 
			
		|||
		return super.getBody()
 | 
			
		||||
				.doOnNext(buffer::write)
 | 
			
		||||
				.doOnError(body::onError)
 | 
			
		||||
				.doOnCancel(this::handleCompleteSignal)
 | 
			
		||||
				.doOnComplete(this::handleCompleteSignal);
 | 
			
		||||
				.doOnCancel(this::handleOnComplete)
 | 
			
		||||
				.doOnComplete(this::handleOnComplete);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void handleCompleteSignal() {
 | 
			
		||||
	private void handleOnComplete() {
 | 
			
		||||
		if (!this.body.isTerminated()) {
 | 
			
		||||
			byte[] bytes = new byte[this.buffer.readableByteCount()];
 | 
			
		||||
			this.buffer.read(bytes);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,12 +30,13 @@ import org.springframework.http.client.reactive.ClientHttpResponse;
 | 
			
		|||
import org.springframework.util.Assert;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Decorate any other {@link ClientHttpConnector} with the purpose of
 | 
			
		||||
 * intercepting, capturing, and exposing actual request and response content
 | 
			
		||||
 * Decorate another {@link ClientHttpConnector} with the purpose of
 | 
			
		||||
 * intercepting, capturing, and exposing actual request and response data
 | 
			
		||||
 * transmitted to and received from the server.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Rossen Stoyanchev
 | 
			
		||||
 * @since 5.0
 | 
			
		||||
 *
 | 
			
		||||
 * @see HttpHandlerConnector
 | 
			
		||||
 */
 | 
			
		||||
class WiretapConnector implements ClientHttpConnector {
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +46,7 @@ class WiretapConnector implements ClientHttpConnector {
 | 
			
		|||
 | 
			
		||||
	private final ClientHttpConnector delegate;
 | 
			
		||||
 | 
			
		||||
	private final Map<String, ExchangeResult> capturedExchanges = new ConcurrentHashMap<>();
 | 
			
		||||
	private final Map<String, ExchangeResult> exchanges = new ConcurrentHashMap<>();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	public WiretapConnector(ClientHttpConnector delegate) {
 | 
			
		||||
| 
						 | 
				
			
			@ -66,21 +67,21 @@ class WiretapConnector implements ClientHttpConnector {
 | 
			
		|||
					return requestCallback.apply(wrapped);
 | 
			
		||||
				})
 | 
			
		||||
				.map(response ->  {
 | 
			
		||||
					WiretapClientHttpRequest request = requestRef.get();
 | 
			
		||||
					String requestId = request.getHeaders().getFirst(REQUEST_ID_HEADER_NAME);
 | 
			
		||||
					WiretapClientHttpRequest wrappedRequest = requestRef.get();
 | 
			
		||||
					String requestId = wrappedRequest.getHeaders().getFirst(REQUEST_ID_HEADER_NAME);
 | 
			
		||||
					Assert.notNull(requestId, "No request-id header");
 | 
			
		||||
					WiretapClientHttpResponse wrapped = new WiretapClientHttpResponse(response);
 | 
			
		||||
					ExchangeResult result = new ExchangeResult(request, wrapped);
 | 
			
		||||
					this.capturedExchanges.put(requestId, result);
 | 
			
		||||
					return wrapped;
 | 
			
		||||
					WiretapClientHttpResponse wrappedResponse = new WiretapClientHttpResponse(response);
 | 
			
		||||
					ExchangeResult result = new ExchangeResult(wrappedRequest, wrappedResponse);
 | 
			
		||||
					this.exchanges.put(requestId, result);
 | 
			
		||||
					return wrappedResponse;
 | 
			
		||||
				});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Retrieve the request with the given "request-id" header.
 | 
			
		||||
	 * Retrieve the {@code ExchangeResult} for the given "request-id" header value.
 | 
			
		||||
	 */
 | 
			
		||||
	public ExchangeResult claimRequest(String requestId) {
 | 
			
		||||
		ExchangeResult result = this.capturedExchanges.get(requestId);
 | 
			
		||||
		ExchangeResult result = this.exchanges.get(requestId);
 | 
			
		||||
		Assert.notNull(result, "No match for request with id [" + requestId + "]");
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue