Skip to content

Commit e3eb06c

Browse files
committed
Add Tee response wrappers for capturing response body
Introduces TeeHttpServletResponseWrapper and TeeServletOutputStream to capture HTTP response bodies while streaming to the client. Updates CachedBodyHttpServletResponse to extend the new wrapper and removes redundant body copying logic from HttpResponseEventPublisher.
1 parent dedf3d7 commit e3eb06c

File tree

4 files changed

+83
-5
lines changed

4 files changed

+83
-5
lines changed

spring-web-captor/src/main/java/com/davidrandoll/spring_web_captor/publisher/response/CachedBodyHttpServletResponse.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import static java.util.Objects.nonNull;
2020

2121
@Slf4j
22-
public class CachedBodyHttpServletResponse extends ContentCachingResponseWrapper {
22+
public class CachedBodyHttpServletResponse extends TeeHttpServletResponseWrapper {
2323
@Getter
2424
private final CachedBodyHttpServletRequest request;
2525

@@ -67,8 +67,7 @@ public void onStartAsync(AsyncEvent asyncEvent) {
6767
}
6868

6969
private void getBody(CompletableFuture<byte[]> future) throws IOException {
70-
future.complete(this.getContentAsByteArray());
71-
this.copyBodyToResponse(); // IMPORTANT: copy response back into original response
70+
future.complete(this.getCapturedResponseBody());
7271
}
7372

7473
public HttpStatus getResponseStatus() {

spring-web-captor/src/main/java/com/davidrandoll/spring_web_captor/publisher/response/HttpResponseEventPublisher.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull Ht
4949
responseWrapper.getResponseBody()
5050
.completeExceptionally(ex);
5151
throw ex;
52-
} finally {
53-
responseWrapper.copyBodyToResponse(); // IMPORTANT: copy response back into original response
5452
}
5553
}
5654
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.davidrandoll.spring_web_captor.publisher.response;
2+
3+
import jakarta.servlet.ServletOutputStream;
4+
import jakarta.servlet.http.HttpServletResponse;
5+
import jakarta.servlet.http.HttpServletResponseWrapper;
6+
7+
import java.io.ByteArrayOutputStream;
8+
import java.io.IOException;
9+
10+
public class TeeHttpServletResponseWrapper extends HttpServletResponseWrapper {
11+
private final ByteArrayOutputStream copy = new ByteArrayOutputStream();
12+
private ServletOutputStream teeStream;
13+
14+
public TeeHttpServletResponseWrapper(HttpServletResponse response) {
15+
super(response);
16+
}
17+
18+
@Override
19+
public ServletOutputStream getOutputStream() throws IOException {
20+
if (teeStream == null) {
21+
teeStream = new TeeServletOutputStream(super.getOutputStream(), copy);
22+
}
23+
return teeStream;
24+
}
25+
26+
public byte[] getCapturedResponseBody() {
27+
return copy.toByteArray();
28+
}
29+
}
30+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.davidrandoll.spring_web_captor.publisher.response;
2+
3+
import jakarta.servlet.ServletOutputStream;
4+
import jakarta.servlet.WriteListener;
5+
6+
import java.io.IOException;
7+
import java.io.OutputStream;
8+
9+
public class TeeServletOutputStream extends ServletOutputStream {
10+
private final ServletOutputStream original;
11+
private final OutputStream copy;
12+
13+
public TeeServletOutputStream(ServletOutputStream original, OutputStream copy) {
14+
this.original = original;
15+
this.copy = copy;
16+
}
17+
18+
@Override
19+
public void write(int b) throws IOException {
20+
original.write(b);
21+
copy.write(b);
22+
}
23+
24+
@Override
25+
public void write(byte[] b, int off, int len) throws IOException {
26+
original.write(b, off, len);
27+
copy.write(b, off, len);
28+
}
29+
30+
@Override
31+
public void flush() throws IOException {
32+
original.flush();
33+
copy.flush();
34+
}
35+
36+
@Override
37+
public void close() throws IOException {
38+
original.close();
39+
copy.close();
40+
}
41+
42+
@Override
43+
public boolean isReady() {
44+
return original.isReady();
45+
}
46+
47+
@Override
48+
public void setWriteListener(WriteListener writeListener) {
49+
original.setWriteListener(writeListener);
50+
}
51+
}

0 commit comments

Comments
 (0)