Skip to content

Commit c4efeb5

Browse files
fix(webflux): preserve multi-buffer response bodies (#412)
1 parent 2d23d65 commit c4efeb5

2 files changed

Lines changed: 54 additions & 4 deletions

File tree

‎spring-boot-starter/spring-boot-starter-webflux/src/main/java/com/getyourguide/openapi/validation/filter/decorator/BodyCachingServerHttpResponseDecorator.java‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import lombok.Setter;
99
import org.reactivestreams.Publisher;
1010
import org.springframework.core.io.buffer.DataBuffer;
11+
import org.springframework.core.io.buffer.DataBufferUtils;
1112
import org.springframework.http.server.reactive.ServerHttpResponse;
1213
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
1314
import org.springframework.lang.NonNull;
@@ -45,10 +46,15 @@ public Mono<Void> writeWith(@NonNull Publisher<? extends DataBuffer> body) {
4546
return super.writeWith(body);
4647
}
4748

48-
var buffer = Mono.from(body).doOnNext(dataBuffer -> {
49-
cachedBody = dataBuffer.toString(StandardCharsets.UTF_8);
50-
if (onBodyCachedListener != null) {
51-
onBodyCachedListener.run();
49+
var buffer = DataBufferUtils.join(body).doOnNext(dataBuffer -> {
50+
try {
51+
cachedBody = dataBuffer.toString(StandardCharsets.UTF_8);
52+
if (onBodyCachedListener != null) {
53+
onBodyCachedListener.run();
54+
}
55+
} catch (RuntimeException exception) {
56+
DataBufferUtils.release(dataBuffer);
57+
throw exception;
5258
}
5359
});
5460
return super.writeWith(buffer);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.getyourguide.openapi.validation.filter.decorator;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.mockito.ArgumentMatchers.any;
5+
import static org.mockito.ArgumentMatchers.eq;
6+
import static org.mockito.Mockito.mock;
7+
import static org.mockito.Mockito.when;
8+
9+
import com.getyourguide.openapi.validation.api.model.RequestMetaData;
10+
import com.getyourguide.openapi.validation.api.selector.TrafficSelector;
11+
import com.getyourguide.openapi.validation.factory.ReactiveMetaDataFactory;
12+
import java.nio.charset.StandardCharsets;
13+
import org.junit.jupiter.api.Test;
14+
import org.springframework.http.MediaType;
15+
import org.springframework.mock.http.server.reactive.MockServerHttpResponse;
16+
import reactor.core.publisher.Flux;
17+
18+
class BodyCachingServerHttpResponseDecoratorTest {
19+
20+
@Test
21+
public void whenResponseContainsMultipleBuffersThenAllBuffersAreWritten() {
22+
var delegate = new MockServerHttpResponse();
23+
delegate.getHeaders().setContentType(MediaType.APPLICATION_JSON);
24+
25+
var trafficSelector = mock(TrafficSelector.class);
26+
var requestMetaData = mock(RequestMetaData.class);
27+
when(trafficSelector.canResponseBeValidated(eq(requestMetaData), any())).thenReturn(true);
28+
29+
var decorator = new BodyCachingServerHttpResponseDecorator(
30+
delegate,
31+
trafficSelector,
32+
new ReactiveMetaDataFactory(),
33+
requestMetaData
34+
);
35+
36+
decorator.writeWith(Flux.just(
37+
delegate.bufferFactory().wrap("{\"name\":\"GetYour".getBytes(StandardCharsets.UTF_8)),
38+
delegate.bufferFactory().wrap("Guide\"}".getBytes(StandardCharsets.UTF_8))
39+
)).block();
40+
41+
assertEquals("{\"name\":\"GetYourGuide\"}", delegate.getBodyAsString().block());
42+
assertEquals("{\"name\":\"GetYourGuide\"}", decorator.getCachedBody());
43+
}
44+
}

0 commit comments

Comments
 (0)