Files
FetchAPI/src/main/java/com/minicircuits/BufferedResponseWrapper.java
T
2026-10-12 12:58:02 -04:00

145 lines
4.4 KiB
Java

package com.minicircuits;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
public class BufferedResponseWrapper extends HttpServletResponseWrapper {
private final Path tempFile;
private final FileOutputStream fileOutputStream;
private final ServletOutputStream servletOutputStream;
private PrintWriter writer;
public BufferedResponseWrapper(HttpServletResponse response) throws IOException {
super(response);
this.tempFile = Files.createTempFile("fetchapi_", ".tmp");
this.fileOutputStream = new FileOutputStream(tempFile.toFile());
this.servletOutputStream = new ServletOutputStream() {
@Override
public void write(int b) throws IOException {
fileOutputStream.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
fileOutputStream.write(b, off, len);
}
@Override
public void flush() throws IOException {
fileOutputStream.flush();
}
@Override
public void close() throws IOException {
fileOutputStream.close();
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener writeListener) {
// Not used for blocking I/O
}
};
}
@Override
public ServletOutputStream getOutputStream() {
return servletOutputStream;
}
@Override
public PrintWriter getWriter() throws IOException {
if (writer == null) {
String charset = getCharacterEncoding();
writer = new PrintWriter(new OutputStreamWriter(fileOutputStream,
charset != null ? charset : "UTF-8"));
}
return writer;
}
@Override
public void flushBuffer() {
// Don't flush to the real response — we're buffering
}
@Override
public void reset() {
// Don't reset the real response during buffering
}
@Override
public void resetBuffer() {
// Don't reset during buffering
}
/**
* Flush any remaining data, then stream the temp file to the real response.
* Sets Content-Length. Deletes the temp file when done.
*/
public void writeToResponse() throws IOException {
// Flush and close writer/stream
if (writer != null) {
writer.close();
} else {
fileOutputStream.close();
}
// Set content length from temp file size
long fileSize = Files.size(tempFile);
if (fileSize <= Integer.MAX_VALUE) {
getResponse().setContentLength((int) fileSize);
} else {
((HttpServletResponse) getResponse()).setHeader("Content-Length", Long.toString(fileSize));
}
// Stream temp file to real response
HttpServletResponse realResponse = (HttpServletResponse) getResponse();
try (InputStream in = new BufferedInputStream(new FileInputStream(tempFile.toFile()), 32768);
OutputStream out = new BufferedOutputStream(realResponse.getOutputStream(), 32768)) {
byte[] buffer = new byte[32768];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
out.flush();
} finally {
// Always delete the temp file
Files.deleteIfExists(tempFile);
}
}
/**
* Clean up temp file if writeToResponse was never called (e.g., on error).
*/
public void cleanup() {
try {
if (fileOutputStream != null) {
fileOutputStream.close();
}
} catch (IOException ignored) {
}
try {
Files.deleteIfExists(tempFile);
} catch (IOException ignored) {
}
}
}